[iouring] Automatic buffer selection for io_uring

  • Post author:
  • Post category:其他

[iouring] Automatic buffer selection for io_uring

io_uring子系统在去年重新定义了Linux系统上的异步I/O的方式。然而,随着这个子系统的能力和用户的增长,它开始遇到了可以表达的操作类型的限制。这就推动了一些操作编程方式的变化。一个例子是三月初在这里介绍的在操作之间携带文件描述符的机制。另一个例子与如何为操作选择I/O缓冲区有关。
正如io_uring开发者Jens Axboe在这个补丁集中所描述的那样,处理大量文件描述符的程序的通常模式是使用poll()来找出哪些描述符已经准备好进行I/O,然后单独调用来实际执行I/O。我们可以在这种模式下使用io_uring,但它违背了整个练习的目的之一:尽可能地避免系统调用。io_uring的做法是在每个文件描述符上排队进行异步操作,然后在其中一个操作被执行时对产生的事件做出反应。
这样的工作方式确实可以减少系统调用--如果请求环一直是满的,就可以一直减少到零。但是,这也需要为每一个排队的操作分配一个单独的I/O缓冲区,尽管其中许多操作可能在不确定的时间段内不会执行。相反,poll()方法允许应用程序推迟分配缓冲区,直到真正需要缓冲区。对于那些保持大量未完成操作的应用程序来说,失去这种灵活性会导致内存使用量大大增加。
这里需要的是某种机制,允许在操作在环中排队后为其分配缓冲区。答案当然是显而易见的:为BPF程序添加一个钩子,它可以在任何特定操作能够进行的时候在内核中执行缓冲区管理。为什么还要尝试其他东西呢?不幸的是,Axboe说,"我很难想象如何通过[BPF]来管理缓冲区的生命时间",所以这个想法被搁置了。
幸运的是,还有一个想法在等待着:让应用程序向io_uring提供一个或多个缓冲池,然后在需要时从这些缓冲池中选择一个缓冲。这就是Axboe最终实现的东西。
为了使用这个机制,一个应用程序开始排队一个或多个IORING_OP_PROVIDE_BUFFERS操作,给内核一组I/O缓冲区。每个操作包括缓冲区的基地址、缓冲区的数量、大小(这个操作中的所有缓冲区都是一样的)、基本缓冲区ID和组ID。如果请求中包括一个以上的缓冲区,缓冲区的ID将在第一个缓冲区之后逐一递增。没有要求一个给定组中的所有缓冲区都是相同大小的,但这似乎是该机制的使用方式。
随后,可以在提交时不提供缓冲区来排队操作;相反,使用特殊值IOSQE_BUFFER_SELECT。队列条目中新的buf_group字段应该被设置为组的ID,在需要时应该从该组获得缓冲区。当一个操作解除阻塞并可以继续进行时,内核将从指定的组中抓取一个缓冲区并使用它。在选择过程中不考虑缓冲区的大小,所以,如果缓冲区太小,操作将不能正常完成。被选中的缓冲区的ID会和操作的完成状态一起返回。
如果请求的缓冲区组是空的,操作将以ENOBUFS错误失败。一旦一个操作消耗了一个缓冲区,内核将不再使用它,直到它被另一个IORING_OP_PROVIDE_BUFFERS请求送回。
在当前的补丁集中,只有一些操作支持缓冲区选择;它仅限于read()、readv()、recv()和recvmsg()。早期版本的补丁集支持write(),尽管>  > 你的编辑会坦率地承认,即使看了代码,也不明白这到底是怎么工作的;该支持在第3版中被删除。
这项工作还没有进入linux-next,但离5.7合并窗口开启还有一段时间。因此,缓冲区选择的功能有可能在下一个开发周期中出现。这将增加io_uring操作的灵活性,而且不需要BPF挂钩。

reference


版权声明:本文为weixin_38734472原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。