io_uring 社区开发报告 - 2021.1

io_uring 作为一种新型高性能异步编程框架,代表着 Linux 内核未来的方向,当前仍处于快速发展中。本文先简单整理了各版本内核 io_uring 所支持特性,再介绍 io_uring 社区发展思路及当前社区在推进的几个比较有价值的特性,最后介绍阿里云内核存储团队围绕 io_uring 所开展的社区工作。

各版本内核 io_uring 支持特性列表

以下信息主要参考每个内核版本发布的 Release Notes,相关特性可参考 io_uring 三个系统调用的 man page 以获得更详细的信息。

  • v5.1
    首次进入内核主线;
    不仅支持异步 direct IO,同时也支持异步 buffered IO;
    支持 polling;
    多个丰富特性支持。
  • v5.2
    支持 eventfd 通知机制;
    支持 sync_file_range(2);
    支持 drain (IOSQE_IO_DRAIN)。
  • v5.3
    支持 recvmsg();
    支持 sendmsg();
  • v5.4
    支持 IORING_OP_TIMEOUT;
    支持同时分配 SQ/CQ 以优化 mmap 系统调用;
    支持在单个 single io_uring_enter() 中 SQ poll 唤醒 + 事件获取;
    支持 linked drain。
  • v5.5
    支持 connect(2);
    支持 accept(4) (IORING_OP_ACCEPT);
    支持 sparse file sets (IORING_REGISTER_FILES_UPDATE);
    支持 overflowed CQ ring;
    支持 IORING_OP_ASYNC_CANCEL;
    支持取消现有超时的请求(IORING_OP_TIMEOUT_REMOVE);
    支持 linked timeouts (IORING_OP_LINK_TIMEOUT);
    引入 io_uring 的专有 workqueue io-wq;
    支持 NOMMU archs mapping。
  • v5.6
    支持 fallocate(2),openat(2),and close(2);
    支持普通版本的 read(2)/write(2);
    支持 statx(2);
    支持 epoll 操作(epoll_ctl(2));
    支持 openat2(2);
    支持 madvise(2) and fadvise(2);
    支持 send(2) and recv(2);
    支持查询当前内核版本是否支持给定的 opcode(IORING_REGISTER_PROBE)。
  • v5.7
    重构 pollable 异步 IO 的处理机制,不再额外线程来处理(IORING_FEAT_FAST_POLL);
    支持 splice(2)。
  • v5.8
    支持 tee(2);
    支持启动/禁用注册 eventfd 的通知机制;
    通过 IORING_SQ_CQ_OVERFLOW 导出 cq overflow 状态到用户态。
  • v5.9
    异步读优化,不再依赖额外线程来处理。
    支持 EPOLLEXCLUSIVE。
  • v5.10
    支持对特定操作的限制(sqe opcode and flags, register opcode),主要用于非信任的场景使用 io_uring 的 ring buffer,如虚拟化的 guest;
    支持 sqpoll offload 模式下 blkcg 的记账;
    sqpoll 模式支持应用等待内核消费 SQ ring,而不是 busy polling。

Alibaba Cloud Linux 2 io_uring 特性基本上与社区主线 v5.8 版本保持同步,同时包含了我们推送中的特性,并在稳定性上进行了加固。

io_uring 社区开发现状

当前 io_uring 社区开发主要聚焦在以下几个方面。

  • io_uring 框架自身的迭代优化
    前面提到,io_uring 作为一种新型高性能异步编程框架,当前仍处于快速发展中。因此随着特性越来越丰富,以及各种稳定性问题的修复等等,代码也变得越来越臃肿。因此 io_uring 框架也需要同步”进化“,不断进行代码重构优化。
    通常异步编程一般是将工作交由线程池来做,但对于 io_uring 来说,这只是最坏的 slow path,例如异步读优化,就是想尝试尽量在当前上下文中处理。另外,在 sqpoll 模式下,io_uring 接管用户提交的系统调用,一个系统调用的执行与特定的进程上下文相关,因此 io_uring 需要维护系统调用进程的内存上下文,文件系统上下文等大量信息。同时 io_uring 作为一种框架,框架本身的开销应该尽可能的小,才能与用户态高性能框架 SPDK 对标,因此需要持续优化。
  • 特性增强

  • IO 栈协同优化
    借助 io_uirng 这一高性能异步编程框架,能比较容易地发现其他内核子系统的软件性能瓶颈,从而对其进行优化,以提高内核 IO 栈的整体性能。我们之前也基于这个思路发现了 block 层的几处优化,同时提出了 device mapper polling 打通的 RFC。

    我们的 Upstream 工作

    阿里云基础软件内核存储团队从 2020 年 2 月开始参与 io_uring 社区开发,目前累计贡献 40+ io_uring 内核补丁,10+ liburing 补丁,多项社区工作得到维护者 Jens Axboe的认可。主要涉及:

  • io_uring file registration 特性优化
    io_uring file registration 特性用来减少文件操作时 fget / fput 原子操作开销。初始 file registration 特性实现在某些场景会导致其并不能减少 fget / fput 原子操作开销,我们采用一种比较巧妙的设计重构优化 io_uring 的 files registration 特性。前面介绍的 Oracle 同学在推进的 io_uring buffer registration 特性增强也用到了我们这种设计思路。

  • 多 io_uring 实例共享 sqthread 特性
    根据我们在业务场景的实践经验,在 io_uring 社区推进多 io_uring 实例共享同一个 sqthread 特性,在保证性能的前提下优化多实例 sqpoll 模式下的 cpu 开销。经过与社区的多轮讨论,社区最终也意识到该特性的业务价值,并在 5.10 内核版本中提供支持。我们后续又对该特性进行进一步重构优化,进一步改善性能。

  • io_uring_enter() 系统调用支持 timeout
    初始的 io_uring 实现通过单独发送一个超时请求系统调用的方式来监测请求的超时情况,此方案在多线程环境使用时性能非常低。我们的方案只需要通过一个系统调用即可,相比于原有实现有着更好的性能,且用户态的编程友好。

  • io_uring 支持 EPOLLEXCLUSIVE 标记
    采用该标记,可以有效的防止基于io_uring的网络应用出现惊群效应。

  • 稳定性加固
    我们主要将 io_uring 用于基于高速块设备的 IO 领域,利用 io_uring 的 sqpoll 和 iopoll 特性来加速内核 IO 性能。在实践过程中我们修复多个严重 BUG,如死锁,IO hang等。

  • 内核其他子系统的协同
    io_uring 本身只是一个异步编程框架,具体工作还是依赖 IO 栈上其他子系统。我们也积极对内核其他子系统进行优化,以更好的适配 io_uring。如 ext4 支持 iopoll,block split bio 场景 iopoll 协同等。

此外,我们目前还有一些重要工作处于社区推进中:

OpenAnolis 社区

io_uring 目前仍然处于高速发展中,快速开发节奏较容易引入一些回归。目前上游社区功能回归测试基本依赖 liburing 自带的测试用例集覆盖,但性能回归测试仍然是空白。因此我们为 io_uring 开发了性能测试框架,以及时发现 io_uring 的性能回归。目前该项目已开源到 OpenAnolis 社区高性能存储技术 SIG
此外,我们也积极探索了 io_uring 在网络应用上的优化,同时也开源到 OpenAnolis 社区,如 echo server benchmark,Redis,Nginx 等。欢迎对 io_uring 技术感兴趣的同学一起加入到 OpenAnolis 社区,围绕 io_uring 打造基于标准 Linux 内核的高性能 IO 栈。

上一篇:IOS学习和总结KVO


下一篇:Ruby 开发社区重量级程序员 Jim Weirich 2月19日去世