首先推荐这篇文章 http://hi.baidu.com/_kouu/blog/item/e225f67b337841f42f73b341.html
准备分三部分来讲,内核的aio,glibc也就是用户空间的aio,内核aio如何通知用户空间
内核的aio是通过用户态的libaio库开放出去的,libaio其实啥也没做,只是封装了一些系统调用,去调用aio.h/aio.c 中的函数。所有异步IO的请求,在libaio里是一个 struct iocb 结构,对应于内核的 struct kiocb 结构。
aio_run_iocb 是内核 aio 的第一个核心函数,首先调用下 iocb 对应的 retry 函数,如果返回了 EIOCBQUEUED,说明这个 iocb 已经进入了 workqueue,如果返回 EIOCBRETRY,此时这个 iocb 必然后一个 kick 标记,这时会把这个 iocb 加入到 内核的 workqueue 机制中延迟处理。这个 aio_wq 是初始化好的 workqueue,一般而言,对特定的workqueue,每个cpu会对应一个,所以cpu核数的多少对异步IO的性能还是有影响的。
如果iocb被queue了,会启动一个timer,时间到了之后调用 delayed_work_timer_fn,总之 iocb 最终是通过工作队列的方式被执行的。
aio_rw_vect_retry 就是前面提到过的 retry 函数,其最终还是去执行 pread/pwrite 之类的调用,该函数会去调用一个 rw_op, 这个函数指针对应的是 file->f_op->aio_read / aio_write, 中间有个循环体会一直重试,只要 iocb->ki_left 还有货没出完,最后一个条件是说,如果操作是 pwritev 同时这个inode 既不是 fifo 也不是 socket , 所以大部分情况下,会需要多次 kick_iocb,而且每次 retry 都返回 EIOCBQUEUED 或者 EIOCBRETRY
aio.c 最后都是定义系统调用的声明:
io_setup:调用 ioctx_alloc 创建一个 struct kioct
io_destroy: 调用 io_destroy() 同时取消所有未完成的 iocb 请求
io_submit:读取用户空间的 nr 个 iocbpp 指针指向的 struct iocb 结构,对每个iocb 调用 io_submit_one。
io_cancel:调用iocb注册的 cancel 函数,并把结果通过 copy_to_user 返回。