背景
很多人都使用libaio接口,但知道为啥提交的时候出现EAGAIN错误,出现这种错误该怎么办?有什么办法避免吗?
要回答上面问题,显然需要深入内核中AIO的实现,去看它在哪些场景会返还EAGAIN,以及所蕴含的意义。
内核中的AIO
为了集中笔墨介绍AIO的核心,这里忽略(percpu/global变量的差异)等其他数据结构。
主要数据结构
最核心的数据结构是 ring buffer, 由io_setup时创建,提交的时候把请求塞到ring buffer;收割的时候释放ring buffer里面的占位。
io_setup 过程
主要的步骤包括:
- 槽位超限比较
判断io_setup指定申请的events数量n,和当前系统已经使用的,是否超过了系统总数max_aio_num。如果超过了,就会报告资源不足(EAGAIN )。否则根据这个数量初始化长度为2*n+2的ring buffer.
max_aio_num在Linux内核中可以通过/sys/proc/fs/max_aio_number查看。
io_submit 过程
根据io_submit中指定的请求数量x,从ring buffer中去申请x个槽位:如果不够(ring buffer为空),就返回EAGAIN;否则成功。
当然中间还会申请page cache等,如果空间不足,会报其他错误。
io_getevents 过程
每收割y个events, 就从ring buffer中释放y个槽位。
返回值塞给res, res2 始终为0,实际没用到目前。
给应用程序的启迪
根据上面的分析,显然应用程序需要保证在飞的请求不要超过io_setup指定的events number太多,一种控制的办法时始终记录已经提交的请求数量-已经收割的请求数量,保证它不超过io_setup指定的events number.