为了改善posix aio目前存在的问题,新的异步IO驱动:io_uring如何解决了posix aio的问题。围绕这个问题,本文首先介绍了异步IO的概念,然后介绍了posix aio存在的问题,最后根据io_uring的源码分析io_uring为什么比posix aio优异。
posix定义如下:
同步IO:导致请求进程阻塞,知道IO操作完成。
异步IO:不导致请求进程阻塞。
下图详细描绘了五种不同的I/O模型。I/O操作被看成两个部分:等待数据和拷贝数据到用户空间。
摘自:unix网络编程-卷1
1.最大的问题就是只对O_DIRECT方式的IO支持异步,用户程序得自己实现I/O缓存;
2.Even if you satisfy all the constraints for IO to be async, it’s sometimes not. There are a number of ways that the IO submission can end up blocking - if meta data is required to perform IO, the submission will block waiting for that. For storage devices, there are a fixed number of request slots available. If those slots are currently all in use, submission will block waiting for one to become available. These uncertainties mean that applications that rely on submission always being async are still forced to offload that part.
3.每次I/O至少需要两次系统调用:submission和wait-for-completion。
1.增加对buffered io的支持,linux aio会在用户程序submit之后被阻塞,io_uring则创建多个内核线程(2*cpu个数)去处理IO,从而防止了阻塞。
以读取为例,下面是io_read函数,具体看注释:
static ssize_t io_read(struct io_kiocb *req, const struct io_uring_sqe *sqe,
bool force_nonblock)
{
...
ret = io_prep_rw(req, sqe, force_nonblock);
ret = io_import_iovec(req->ctx, READ, sqe, &iovec, &iter);
ret = rw_verify_area(READ, file, &kiocb->ki_pos, iov_iter_count(&iter));
if (!ret) {
ssize_t ret2;
/* Catch -EAGAIN return for forced non-blocking submission */
ret2 = call_read_iter(file, kiocb, &iter);
if (!force_nonblock || ret2 != -EAGAIN)
io_rw_done(kiocb, ret2); // linux aio会在这里等带执行完毕
else
ret = -EAGAIN; // io_uring则直接返回
}
return ret;
}
3.减少系统调用次数,io_uring中内核和用户程序共享三块内存,分别是:SQ ring、CQ ring 和 sqe array。采用producer和consumer模型,大大的降低了系统调用的次数。