LWN: io_uring的快速成长!

io_uring作为一个新兴的异步I/O机制,自2019年首次引入Linux内核以来,迅速发展并扩展了其功能集。它通过用户空间和内核之间的共享环形缓冲区实现了高效的操作提交和结果收集,减少了系统调用的开销。除了基本的读写操作,io_uring还支持fsync、poll、网络操作、超时管理等,且仍在持续增加新特性,如fallocate、open/close文件、statx等。这一机制有望解决长期以来的线程阻塞问题,推动Linux系统向更加异步化的方向发展。

关注了就能看到更多这么棒的文章哦~

The rapid growth of io_uring

By Jonathan Corbet
January 24, 2020

一年前的时候,io_uring子系统在mainline kernel里面还根本不存在,是在2019年5月的时候第一次出现在5.1版本中。从根本上来讲,io_uring是一个用来做异步I/O(asynchronous I/O)的机制,不过慢慢地开始有了更多的应用场景,也增加了更强的能力。这里我们介绍一下io_uring的当前状况、发展方向,以及这个过程中出现的一两个有趣的问题。

传统的Unix I/O天生就是同步操作。从application的角度来看,一次I/O操作在read() / write()系统调用返回的时候就完成了,哪怕其实底层可能还在做一些处理,application并不需要关心这一点了。也没有什么方法来发起一个异步操作并等待(wait)后续的completion,这种操作其实在Unix出现之前,已经有其他一些操作系统支持多年了。

在Linux里面,自从有了asynchronous I/O (AIO) subsystem,算是补上了这个缺陷,不过这个方案大家一直没有完全满意。AIO需要在底层有一些支持,因此在一些核心的应用场景里(direct file I/O和networking)无法得到很好的应用。在过去几年里不断有讨论如何更好的解决asynchronous-I/O的问题。有很多种方案被提出来,例如fibrils, threadlets, syslets, acall, work-queue-based AIO等,不过都没能合入mainline。

最近一次尝试就是io_uring,它终于被合入了mainline。Io_uring跟此前的方案不同,它是基于user space和kernel之间共享的ring buffer来实现的,这样一来发起操作和收集结果的时候,很多情况下并不需要调用系统调用进入kernel了。这个接口看起来有点复杂,不过对大多数需要进行大量I/O操作的应用程序来说,是愿意为了提升性能而接受这个复杂性的。可以查看此pdf ( https://kernel.dk/io_uring.pdf  ) 来了解io_uring的API描述。如果使用liburing库的话,可以简化使用的API。

What io_uring can do

提交到io_uring submission ring里的每个entry都包含了一个opcode(操作代码)用来告知kernel需要做什么。当io_uring加入5.1 kernel的时候,可以使用的opcodes如下:

IORING_OP_NOP

这个OP什么都不会做,主要用在有些情况下的placeholder。

IORING_OP_READV

IORING_OP_WRITEV

提交一个readv()或者write()操作,这是io_uring在大多数情况下最核心的目标。

IORING_OP_READ_FIXED

IORING_OP_WRITE_FIXED

这些opcode也会提交I/O操作,不过他们使用了kernel里面早就map好的"registered" buffer,这样就能减少总共的overhead。

IORING_OP_FSYNC

发布一次fsync()调用,也就是一个异步的synchronization操作。

IORING_OP_POLL_ADD

IORING_OP_POLL_REMOVE

IORING_OP_POLL_ADD会发起一次针对一组文件描述符的poll()操作。这是一次性的操作,完成之后就必须再重新提交发起一次,也可以用IORING_OP_POLL_REMOVE来明确指定取消它。采用这种方式进行的polling可以异步地观察一组文件描述符。io_uring子系统也支持多个操作之间有互相依赖关系,这样可以用一个poll操作来暂时延后另一个操作,直到相关的文件描述符准备好之后再发出下一个操作。

io_uring功能引起了很多人对它的强烈兴趣。Io_uring的作者Jens Axboe本可以到此为止,歇息一段时间。不过他其实继续做了很多事情,在5.1版本之后,增加了如下的操作:

IORING_OP_SYNC_FILE_RANGE (5.2)

执行一次sync_file_range()操作 — 这是对现有的fsync()支持的一个改进,不过执行结果不如fsync()那么可靠,只能达到部分效果。

IORING_OP_SENDMSG (5.3)

IORING_OP_RECVMSG (5.3)

这些操作支持在网络上通过sendmsg()和recvmsg()来异步地进行packet发送和接收。

IORING_OP_TIMEOUT (5.4)

IORING_OP_TIMEOUT_REMOVE (5.5)

这个操作会在指定时长之后结束,可以指定是若干秒或者若干个完成的io_uring操作。这是一个强制唤醒正在等待的application的方式,哪怕它在唤醒后还会继续sleep等待其他的操作完成。

IORING_OP_ACCEPT (5.5)

IORING_OP_CONNECT (5.5)

accept某个socket上的connection,或者发起一个connection去连接远端对象。

IORING_OP_ASYNC_CANCEL (5.5)

试着取消目前已经发起过的一个操作。是否能够成功,则取决于这个操作的类型,以及它已经走了多元了。

IORING_OP_LINK_TIMEOUT (5.5)

创建一个timeout,跟ring buffer中某个特定的operation关联起来。如果在timeout时间到的时候,这个operation还在进行,那么kernel就试着去取消这个操作。如果此operation已经结束了,那么这个timeout操作本身会被取消掉。

以上就是5.5 kernel release里面io_uring的接口情况。

Coming soon

io_uring的开发还远未停止。可以通过查看一下linux-next里面等待合入5.6的功能就可以有一些了解:

IORING_OP_FALLOCATE

用fallocate()针对某个文件分配一些block。

IORING_OP_OPENAT

IORING_OP_OPENAT2

IORING_OP_CLOSE

打开关闭文件。

IORING_OP_FILES_UPDATE

可以把常用的文件注册到io_uring里面,从而加快访问速度。这个命令可以异步地把文件加到list里(或者从list里面拿掉)。

IORING_OP_STATX

利用statx()来查询某个文件的信息。

IORING_OP_READ

IORING_OP_WRITE

这些操作跟IORING_OP_READV 和 IORING_OP_WRITEV类似,不过他们用了更简单的接口,只能处理单个buffer。

IORING_OP_FADVISE

IORING_OP_MADVISE

异步地执行posix_fadvise() 和 madvise() 系统调用。

IORING_OP_SEND

IORING_OP_RECV

发送和接收网络数据。

IORING_OP_EPOLL_CTL

使用epoll_ctl()系统调用来操作一组epoll文件描述符。

不知道kernel 5.6里面会变成什么样。目前有在试图加ioctl()的支持,不过被否决了,因为担心可靠性和安全性。不过Axboe找到了一个方法可以今后逐个针对特定的ioctl()操作增加支持。举例来说,media subsystem中如果支持若干个对性能有帮助的ioctl()操作,肯定可以从中受益。

此前还有一个patchset增加了splice()的支持。

An asynchronous world

所有人都说io_uring在快速孵化出许多年前人们就期望出现的thread-based asynchronous机制。大家都非常希望避免event loop(事件队列)被阻塞的情况出现。很有可能这套API会继续发展,直到有许多情况下的task都可以自由执行而几乎不会被阻塞(blocking)。不过这个过程中还是有一些有趣的问题需要处理好。

首先,io_uring里面的命令部分只有8个bit,也就是最多只支持256个opcode。在5.6里已经有30个opcode被用到了,还有许多空余。不过在Linux里面,系统调用的个数已经超过256个了。如果io_uring最终发展得能支持大多数系统调用,那么就不够用了。

Stefan Metzmacher提出了另一个问题。Io_uring已经支持了多个command之间的互相依赖关系,比如可以直到某个前面的操作完成之后再开始另一个操作的初始化动作。不过更麻烦的是在多个operation之间传递信息。Metzmacher有个应用场景需要异步地调用openat(),然后针对结果里的文件描述符来发起I/O操作,不需要等open操作真正完成。

看起来有一个方案能解决这个问题:那就是利用BPF来把两个操作串起来。BPF有能力在kernel里面一系列异步操作中恰当的位置那里执行一些代码,这样一来,肯定可以实现更多的可能性了。Axboe说“这里有很大潜力”。确实如此,你可以想象一下把整个程序都用一个很小的C语言写的驱动放到ring里面,然后大多数时候都会自己执行不用管它了。

不过这里还有一个潜在问题,io_uring是一个无特权的接口,在执行各种操作的时候,会进行所有必要的权限检查。不过BPF目前看来越来越没有指望能让无特权的普通用户调用了,开发者明确指出今后不会支持BPF的unprivileged use。这样一来,BPF很难配合io_uring使用了。在Facebook倒可能有些潜在的计划来解决这个问题,不过目前还没提交到公众mailing list来讨论。总之,在2020 Linux Storage, Filesystem, and Memory-Management Summit上会需要讨论一些BPF相关话题了。

总之,尽管io_uring看起来势头很好,只有很少的一些成长的烦恼。在后续几个release里面,我们很期待能看到有哪些功能又加入这个subsystem里面去。从最近的历史记录来看,io_uring的成长仍然没有慢下来的迹象。

全文完

LWN文章遵循CC BY-SA 4.0许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注LWN深度文章以及开源社区的各种新近言论~

<think> 嗯,用户这次的问题非常具体,是关于kernel_device_modules-6.6构建过程中mgk_64_k66_customer_modules_install.user失败的解决方案。从问题描述看,用户应该是嵌入式Linux开发工程师,正在定制内核模块,遇到构建中断的问题。 上次对话中用户提到了“kernel_device_modules-6.6”这个关键词,说明是在处理内核设备模块的编译问题。这次报错文件扩展名.user比较特殊,像是某种自定义构建脚本。用户能准确提供文件名,说明已经有一定技术基础,可能排查过基础路径问题。 需要特别注意几个关键点:一是“kernel_device_modules-6.6”这个项目名称看起来像商业嵌入式方案,可能是客户定制内核;二是“mgk_64_k66”这个前缀,mgk可能是模块组缩写,64指64位架构,k66疑似指NXP的Kinetis K66微控制器;三是构建脚本后缀.user比较罕见,可能是企业内部自定义的构建系统。 构建失败通常有几类原因:环境配置问题(如交叉编译器缺失)、脚本逻辑错误(路径处理不当)、依赖缺失(头文件或库文件)、权限问题等。考虑到.user脚本的特殊性,应该优先检查:1)执行权限;2)脚本内部路径是否正确指向内核源码目录;3)交叉编译工具链是否配置。 用户没有提供具体错误日志是最棘手的。需要引导用户提供更多信息,比如构建时的终端输出、脚本开头部分的路径配置等。另外要注意这个模块可能是依赖特定内核配置选项的,需要确认.config文件中相关驱动是否启用。 最后要提醒用户注意风险——这类定制内核模块的构建失败往往涉及多方因素,企业内部的构建系统可能还有非公开依赖。建议用户先联系原厂支持渠道,同时提供尽可能详细的错误信息方便进一步排查。 </think> 针对 `kernel_device_modules-6.6` 中 `mgk_64_k66_customer_modules_install.user` 构建失败的解决方案,请按以下步骤排查: --- ### **排查步骤** #### 1. **检查构建环境** - **内核源码完整性** 确认 `kernel_device_modules-6.6` 源码完整且与当前运行内核版本一致: ```bash uname -r # 查看当前内核版本 ls /lib/modules/$(uname -r)/build # 验证内核头文件存在 ``` 若路径缺失,需安装对应内核头文件包[^1]。 - **交叉编译工具链** 若为嵌入式设备(如 `k66` 可能指 NXP Kinetis K66),检查交叉编译器是否配置: ```bash echo $CC # 查看环境变量 arm-none-eabi-gcc --version # 示例(具体工具链名称需确认) ``` #### 2. **分析构建日志** - 查找错误关键字(如 `Error`/`Failed`): ```bash grep -i "error\|failed" build.log # 替换为实际日志文件名 ``` **常见问题方向**: - **依赖缺失**:如 `undefined reference` 提示缺少库或头文件 - **语法错误**:内核 API 变更导致函数签名不兼容 - **权限问题**:`Permission denied` 提示需 `sudo` 或修复路径权限 #### 3. **验证脚本权限与路径** - 添加可执行权限: ```bash chmod +x mgk_64_k66_customer_modules_install.user ``` - 检查脚本内路径是否为**绝对路径**(避免相对路径嵌套错误): ```bash head -n 20 mgk_64_k66_customer_modules_install.user # 查看脚本头部配置 ``` #### 4. **依赖项检查** - 安装基础编译依赖: ```bash sudo apt-get install build-essential libssl-dev flex bison libelf-dev ``` - 确认模块特殊依赖(如厂商 SDK、闭源驱动)是否部署。 #### 5. **兼容性调整** - **内核符号导出问题**: 若日志含 `Unknown symbol`,需在模块代码中添加导出声明: ```c EXPORT_SYMBOL(your_function); // 内核源码中声明 ``` - **版本适配**: 若内核 ≥5.7,需替换弃用的 `get_user_pages()` 为 `get_user_pages_remote()`[^2]。 #### 6. **增量构建测试** - 分步编译定位故障点: ```bash make -C /lib/modules/$(uname -r)/build M=$(pwd) modules # 仅编译模块 sudo insmod your_module.ko # 手动加载测试 ``` --- ### **典型错误场景与修复** | **错误类型** | **解决方案** | |------------------------|------------------------------------------| | **`Makefile: No such file`** | 确认脚本中 `KERNEL_SRC` 路径正确指向内核源码目录 | | **`Invalid module format`** | 强制编译目标版本:`make KERNELRELEASE=$(uname -r)` | | **`undefined reference`** | 检查模块依赖链,添加 `obj-m += dependent_module.o` | | **`Function not implemented`** | 替换新版内核 API(如 `vmalloc_user()` 替代旧接口) | --- ### **补充建议** 1. **联系厂商支持**: `mgk_64_k66` 可能是定制模块,需获取厂商提供的补丁或更新脚本。 2. **版本回退测试**: 尝试旧内核版本(如 5.4 LTS)验证是否兼容性问题。 3. **调试符号分析**: 启用 `CONFIG_DEBUG_INFO=y` 后使用 `dmesg` 查看详细崩溃堆栈。 > 请优先提供 **完整构建日志** 以精准定位问题。若上述步骤未解决,建议提交日志到社区论坛(如 [Stack Overflow](https://stackoverflow.com))或内核邮件列表[^3]。 --- [^1]: 内核头文件安装参考:[Ubuntu Kernel Headers](https://wiki.ubuntu.com/Kernel/BuildYourOwnKernel) [^2]: 内核 API 变更说明:[Linux 5.7 API Changes](https://lwn.net/Articles/817101/) [^3]: 内核社区支持:[LKML (Linux Kernel Mailing List)](https://lkml.org/)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值