在实际的文件系统等高并发度的场景中,用得更多的是异步IO模式还是基于libaio。下面简要介绍基于libaio的实现。

 

1 安装依赖头文件和库

 

由于libaio不是centos原生的支持,需要手动安装开发库:

 

[root@localhost test]# yum install libaio.x86_64 libaio-devel.x86_64

 

2 理解主要的数据结构和API

 

主要的内容在:/usr/include/libaio.h



wKiom1nO8-uguLUDAAHjNd3_Ydk992.png

显然从上面的iocb数据结构能看出:它支持单个的io操作(io_iocb_common),也支持成批的IO处理(io_iocb_vector),还支持基于polling 模式。

 

为了便于实际调用,避免对内部数据结构初始化的麻烦,它还封装好了多个常用的inline函数,这些inline函数基本上可以理解成宏:

 

static inline int io_fsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd)

{

    io_prep_fsync(iocb, fd);

    io_set_callback(iocb, cb);

    return io_submit(ctx, 1, &iocb);

}

 

static inline void io_prep_fdsync(struct iocb *iocb, int fd)

{

    memset(iocb, 0, sizeof(*iocb));

    iocb->aio_fildes = fd;

    iocb->aio_lio_opcode = IO_CMD_FDSYNC;

    iocb->aio_reqprio = 0;

}

 

static inline int io_fdsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd)

{

    io_prep_fdsync(iocb, fd);

    io_set_callback(iocb, cb);

    return io_submit(ctx, 1, &iocb);

}

 

不同于上一篇博客中提到的那种异步IO通过信号机制实现异步,libaio主要用event 来实现异步:一个IO初始化的时候对应一个io event, 每完成一个IO就产生一个event, 因此IO 请求提交之后,随时检查返回的io event的数量,就知道哪些IO已经完成。

 

static inline void io_set_eventfd(struct iocb *iocb, int eventfd)

{

    iocb->u.c.flags |= (1 << 0) /* IOCB_FLAG_RESFD */;

    iocb->u.c.resfd = eventfd;

}

 

3. 注意事项


3.1  aio context 数据结构在调用之前必须清0;

 DESCRIPTION

       The  io_setup()  system  call  creates an asynchronous I/O context suitable for concurrently processing nr_events operations.  The ctx_idp argument must not

       point to an AIO context that already exists, and must be initialized to 0 prior to the call.  On successful creation of the AIO context, *ctx_idp is  filled

       in with the resulting handle.

 

 

3.2  aio context system call : io_submit/io_setup 系统调用等不能直接使用,需要调用wrapper().

 

 Note that the libaio wrapper function uses a different type (io_context_t *) for the ctx_idp argument.  Note also that the libaio wrapper  does  not  follow

       the  usual C library conventions for indicating errors: on error it returns a negated error number (the negative of one of the values listed in ERRORS).  If

       the system call is invoked via syscall(2), then the return value follows the usual conventions for indicating an error: -1, with errno set to  a  (positive)

       value that indicates the error.

 

3.3 注意数据结构的区别

aio_context_t 只在libaio.h 中有定义;

而io_context_t 只是在linux/aio_abi.h 和 aio.h中有定义。

 

4. 如何实现事件传递和异步

 

实际文件系统中常见的用法:

memset(&myctx, 0, sizeof(myctx)) 初始化有ioctx;

io_queue_init(AIO_MAXIO, &myctx); 第一个参数指定了IO数目吧

 

io_prep_pread()/ io_prep_pwrite()

io_set_callback(io, callbak); //  设置IO完成之后的回调函数

 

io_submit()提交一组IO 请求;

io_getevents()得到已经执行完的IO数目及其状态。

 

io_run_queue()

io_wait_queue()

 

5. 编译

不能再像自带的aio库一样依赖librt了,否则编译过程当会出现类似下面的错误:

[root@localhost test]# gcc -o libaio_test libaio_test.c  -lrt

/tmp/ccXHzl0H.o: In function `main':

libaio_test.c:(.text+0x62): undefined reference to `io_setup'

libaio_test.c:(.text+0x8f): undefined reference to `io_prep_pwrite'

libaio_test.c:(.text+0xae): undefined reference to `io_fsync'

collect2: error: ld returned 1 exit status

 

而应该用libaio:

gcc -o libaio_test libaio_test.c  -laio (库在/lib64下面:./lib64/libaio.so.1) 连接到linux的libaio.so库。

 

 

6. 参考链接

http://blog.csdn.net/ixidof/article/details/51211508

http://blog.csdn.net/zhaofuguang/article/details/12853881

http://blog.csdn.net/zhaofuguang/article/details/12853883