53

io_uring(1) – 我们为什么会需要 io_uring

 4 years ago
source link: https://www.tuicool.com/articles/uu6zaab
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

当前 Linux 对文件的操作有很多种方式,最古老的最基本就是 readwrite 这样的原始接口,这样的接口简洁直观,但是真的是足够原始,效率什么自然不是第一要素,当然为了符合 POSIX 标准,我们需要它。一段时间之后,程序员们发现,人们需要更为简单的 API,于是出现了 preadpwrite 它允许我们在读写时直接传递 offset,显而易见它表现的更为优秀,在减少编码的同时,提高了代码的健壮性。后来又出现了 preadvpwritev 这种可以一次性发送多个 IO 的高效接口;接着又出现了变种函数 preadv2pwritev2 他们不仅仅可以发送向量型的 IO,offset,还能设置本次 IO 的标志,比如 RWF_DSYNC、RWF_HIPRI、RWF_SYNC 等等(暂时没有其他)。

上面介绍的一系列的接口全部都是同步接口,意思就是在读写 IO 时,caller 一定会阻塞起来等待结果返回,对于普通的传统编程模型,这其实没有什么大不了的,编程简单且结果可以预测;但是在高效情况下呢?同步导致的后果就是 caller 不再能够继续执行其他的操作,只能静静的等待 IO 结果返回,其实他明明可以利用这段时间继续处理下一个操作,好比是一个 ftp 服务器,当接收到客户机上传的文件,然后将文件写入到本机的过程中时,假设 ftp 服务程序忙于等待文件读写结果的返回,那么就会拒绝到其他正在需要连接的客户机请求。有没有更好的方式?当然有,那就是采用异步 IO 模型,当一个客户机上传文件时,直接将 IO 的 buffer 提交给内核即可,然后 caller 继续接受下一个客户请求,在内核处理完毕 IO 之后,主动调用各种通知机制,告诉 caller 上一个 IO 已经完成,完成状态保存在某某位置,请查看。

Great,原来我们是如此迫切的需要异步 IO,他能帮助我们做更多的事情而无需增加 caller 更多的复杂度,AIO 应运而生,POSIX 也适时的添加了 aio_readaio_write 这样的标准接口,好像一切都那么顺利,世界来到了一个完美的位面。Unfortunately,aio 满足了我们的要求,但是他也存在很多的缺陷。

  • 最大的缺陷就是不支持 buffer-io,也就是说,在采用 aio 的时候,你只能使用 O_DIRECT 来发送这一个 IO,带来的影响就是你不再能够借助文件系统缓存来缓存当前的 IO 请求,于是你在得到的同时失去了一些东西。
  • 尽管你强迫所有的 IO 都采用异步 IO,但是有时候确做不到,你的 caller 尽管将任务发送给了内核,但是内核还是通过工作队列或者线程完成的提交工作,假设在写元数据区域的时候,submission 会被挂起等待,假设存储设备的所有通道都很忙的时候,submission 需要挂起等待。于是,这些不确定性的存在,导致你的 caller 在处理完成状态的时候也不得不妥协。
  • API 函数并不是很友好,基本上每一个 IO 的提交都需要要拷贝 64 + 8 个字节,而完成状态需要拷贝 32 个字节,这里就是 104 个字节的拷贝,当然,这个消耗是否可以承受是和你的 IO 大小有关的,如果你发送的大的 IO 的话,这点消耗可以忽略。同时,每一个 IO 至少需要两次系统调用才能完成(submit 和 wait-for-completion),这在有 spectre/meltdown 的机器上是一个严重的灾难。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK