libaio api
AIO system call entry points are located in fs/aio.c
; file in the kernel’s
source code. Types and constants exported to the user space reside in
/usr/include/linux/aio_abi.h
header file.
There are only 5 AIO system calls:
int io_setup(unsigned nr_events, aio_context_t *ctxp);
int io_destroy(aio_context_t ctx);
int io_submit(aio_context_t ctx, long nr, struct iocb *cbp[]);
int io_cancel(aio_context_t ctx, struct iocb *, struct io_event *result);
int io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events, struct timespec *timeout);
The Linux AIO model is used as follows:
- Open an I/O context to submit and reap I/O requests from.
- Create one or more request objects and set them up to represent the desired operation
- Submit these requests to the I/O context, which will send them down to the device driver to process on the device
- Reap completions from the I/O context in the form of event completion objects,
- Return to step 2 as needed.
I/O Context
The first step to use libaio is create and init io_context
by using io_setup
io_context_t ctx;
memset(&ctx, 0, sizeof(ctx));
if(io_setup(10, &ctx)!=0){//init
printf("io_setup errorn");
return -1;
}
The first augments of io_setup
is the maximum length of the I/O query.
Create I/O request
The I/O request is represented by iocb
structure.
struct iocb {
PADDEDptr(void *data, __pad1); /* Return in the io completion event */
PADDED(unsigned key, __pad2); /* For use in identifying io requests */
short aio_lio_opcode;
short aio_reqprio;
int aio_fildes;
union {
struct io_iocb_common c;
struct io_iocb_vector v;
struct io_iocb_poll poll;
struct io_iocb_sockaddr saddr;
} u;
};
It is defined in libaio.h. You can manually set up the variables. However, it is recommended to use the helper functions:
static inline void io_set_callback(struct iocb *iocb, io_callback_t cb)
{
iocb->data = (void *)cb;
}
static inline void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset)
{
memset(iocb, 0, sizeof(*iocb));
iocb->aio_fildes = fd;
iocb->aio_lio_opcode = IO_CMD_PREAD;
iocb->aio_reqprio = 0;
iocb->u.c.buf = buf;
iocb->u.c.nbytes = count;
iocb->u.c.offset = offset;
}
static inline void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset)
{
memset(iocb, 0, sizeof(*iocb));
iocb->aio_fildes = fd;
iocb->aio_lio_opcode = IO_CMD_PWRITE;
iocb->aio_reqprio = 0;
iocb->u.c.buf = buf;
iocb->u.c.nbytes = count;
iocb->u.c.offset = offset;
}
Submit the request
The submittion is easy:
if(io_submit(ctx, 1, &iocb_request)!=1){
io_destroy(ctx);
printf("io_submit errorn");
return -1;
}
You can also submit an array of iocb
s:
io_submit(ctx, sizeof(iocb_array)/sizeof(struct iocb), iocb_array);
Wait the io events with timeout
struct io_event e;
while(1){
timeout.tv_sec=0;
timeout.tv_nsec=500000000;//0.5s
if(io_getevents(ctx, 0, 1, &e, &timeout)==1){
break;
}
printf("haven't donen");
sleep(1);
}
The io_event
is a structure, you can get the submitted iocb
from it:
struct io_event {
PADDEDptr(void *data, __pad1);
PADDEDptr(struct iocb *obj, __pad2);
PADDEDul(res, __pad3);
PADDEDul(res2, __pad4);
};
Release I/O Context
If you do not use the I/O context any more, remember to destroy it:
io_destroy(ctx);