linux异步IO编程实例分析


linux异步IO编程实例分析

文章插图
异步阻塞IO模型的流程图
在Direct IO模式下,异步是非常有必要的(因为绕过了pagecache,直接和磁盘交互) 。linux Native AIO正是基于这种场景设计的,具体的介绍见:KernelAsynchronousI/O (AIO)SupportforLinux 。下面我们就来分析一下AIO编程的相关知识 。
阻塞模式下的IO过程如下:
int fd = open(const char *pathname, int flags, mode_t mode);ssize_t pread(int fd, void *buf, size_t count, off_t offset);ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);int close(int fd);因为整个过程会等待read/write的返回,所以不需要任何额外的数据结构 。但异步IO的思想是:应用程序不能阻塞在昂贵的系统调用上让CPU睡大觉,而是将IO操作抽象成一个个的任务单元提交给内核,内核完成IO任务后将结果放在应用程序可以取到的地方 。这样在底层做I/O的这段时间内,CPU可以去干其他的计算任务 。但异步的IO任务批量的提交和完成,必须有自身可描述的结构,最重要的两个就是iocb和io_event 。
libaio中的structsstruct iocb {        void     *data;  /* Return in the io completion event */        unsigned key;   /*r 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;};struct io_iocb_common {        void            *buf;        unsigned long   nbytes;        long long       offset;        unsigned        flags;        unsigned        resfd;};iocb是提交IO任务时用到的,可以完整地描述一个IO请求:
data是留给用来自定义的指针:可以设置为IO完成后的callback函数;
aio_lio_opcode表示操作的类型:IO_CMD_PWRITE | IO_CMD_PREAD;
aio_fildes是要操作的文件:fd;
io_iocb_common中的buf, nbytes, offset分别记录的IO请求的mem buffer,大小和偏移 。
struct io_event {        void *data;        struct iocb *obj;        unsigned long res;        unsigned long res2;};io_event是用来描述返回结果的:
obj就是之前提交IO任务时的iocb;
res和res2来表示IO任务完成的状态 。
libaio提供的API和完成IO的过程libaio提供的API有:io_setup, io_submit, io_getevents, io_destroy 。
1. 建立IO任务
int io_setup (int maxevents, io_context_t *ctxp);io_context_t对应内核中一个结构,为异步IO请求提供上下文环境 。注意在setup前必须将io_context_t初始化为0 。
当然,这里也需要open需要操作的文件,注意设置O_DIRECT标志 。
2.提交IO任务
long io_submit (aio_context_t ctx_id, long nr, struct iocb **iocbpp);提交任务之前必须先填充iocb结构体,libaio提供的包装函数说明了需要完成的工作:
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;}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;}


推荐阅读