背景

很多人都使用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.