LWN: io_uring中的ioctl()!

本文探讨了在io_uring子系统中如何实现类似ioctl()的系统调用,以解决传统ioctl的多义性和设备依赖性问题。Jens Axboe的RFC patchset展示了一种可能的解决方案,通过新的command机制,尽管效率有所牺牲,但仍满足了异步控制设备的需求。

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

ioctl() for io_uring

By Jonathan Corbet
February 4, 2021
DeepL assisted translation
https://lwn.net/Articles/844875/

在 Unix 传统的所有系统调用中,很少有像 ioctl()这样经受那么多诟病的。但是 ioctl()的存在是很有必要的,其实有很多情况都需要 ioctl,不能指望这些需求很快就消失。因此,在 io_uring 子系统中也就需要提供类似 ioctl() 这样的功能。Jens Axboe 最近的一个 RFC patch set 就给大家展示了在 io_uring 中可能会采取什么方式来实现 ioctl。

ioctl()的名字来自于 "I/O control"。这个系统调用是为了在外设上执行那些不属于读写数据这种类型的操作而加入的。例如,它可以用来命令磁带机倒带、设置串口的波特率、或者弹出移动磁盘。多年来,ioctl()的用途已经远远超出了这些简单应用,在某些 API(例如 media)中已经提供了数百种操作。

对 ioctl()的批判主要是针对它的多义性(multlipexed)和设备依赖性,几乎所有可以用文件描述符表示的东西都支持 ioctl(),但他们各自实际能支持的操作却各不相同。虽然在添加新的系统调用到内核之前人们都会进行严格审查(至少理论上是这样),但 ioctl()命令通常不会得到这类审查。所以没有人能完全了解所有可以用 ioctl()做的事情。此外,更麻烦的是同一个命令在不同的设备上含义可能会不一样,这意味着如果对错误的文件描述符进行了 ioctl()调用可能会产生完全意想不到麻烦。人们曾经试图避免这个问题,但并没能完全达到目的。

在跟这些问题搏斗多年之后,有些开发者希望 ioctl() 能完全被消灭掉,但从来没有人提出过什么好方案。不可能去为每一个 ioctl()实现的功能都添加一个新的系统调用,而让设备驱动程序对 write() 写进来的 command stream 是更加糟糕的做法。在告知摄像头传感器应该使用哪个 color space 的时候,没有什么别的更好的方法了。

自然,我们也就需要在 io_uring 中支持 ioctl()了。经常能看到人们将 ioctl()调用与常规的 I/O 读写混合在一起交错调用,而如果能够异步地完成这些工作的话就会很有价值了。但是每个 ioctl()调用都是互不相同的,从来没有为异步执行的这种场景而进行过专门设计,所以在 io_uring 中要实现 ioctl()的话没有什么别的好选择,只能是在一个独立线程中执行每个调用。这可能比完全不支持异步操作要好一些,但效率却差得很远,尤其是那些本可以马上得到执行的系统调用现在没法立刻执行了。在 io_uring 中正确使用 ioctl(),本质上是需要重新发明一个 ioctl()接口。

io_uring 中的各个操作是通过一个 ring buffer 从 user space 传递到内核的,每个操作都是通过一个有点复杂的 io_uring_sqe structure 结构来代表的。在该结构中设置 opcode 为 IORING_OP_URING_CMD 就可以使用新的 command 机制。其中 fd 字段必须像往常一样包含要操作的文件描述符。不过该结构的其余部分(从 off 字段开始)就使用了完全不同的定义:

struct io_uring_pdu {
  __u64 data[4];  /* available for free use */
  __u64 reserved; /* can't be used by application! */
  __u64 data2;  /* available for free use */
};

reserved 字段覆盖了原来结构中的 user_data 部分,用于其他目的了,因此,与 command 相关的数据不能存储在这里了。不过,应用程序一般不会直接使用这个结构,而是会看到一个再次 overlay 过的结构,这个结构才是跟这个即将执行的 command 相关的内容。例如,对于 block-subsystem 的 command 来说,会用到这样一个结构:

  struct block_uring_cmd {
__u16   op;
__u16 pad;
union {
    __u32 size;
    __u32 ioctl_cmd;
};
__u64 addr;
__u64 unused[2];
__u64 reserved; /* can never be used */
__u64 unused2;
  };

在这个结构中就藏着 ioctl_cmd 了,应用程序会在这里设置希望执行的 ioctl() 命令号(command code), op 字段需要设置为 BLOCK_URING_OP_IOCTL (暂时如此,将来可能会有其他跟 ioctl() 不相关的 op)。目前的 patch set 中,唯一支持的命令是 BLKBSZGET,会返回底层块设备的块大小(block size),显然,这个操作可以不需要进行实际 I/O 操作就能完成,也不需要 sleep。patch set 还使用了一些其他的 structure 来实现了几个网络相关的 ioctl command。

内核中,任何想要支持 io_uring 操作的子系统都必须在 file_operations 结构(这个结构永远都在增长)中添加一个新的字段:

struct io_uring_cmd {
  struct file *file;
  struct io_uring_pdu pdu;
  void (*done)(struct io_uring_cmd *, ssize_t);
};

int (*uring_cmd)(struct io_uring_cmd *, enum io_uring_cmd_flags);

不用说,io_uring 中 IORING_OP_URING_CMD 的处理程序都不应该是阻塞操作,它们必须要能立即完成操作,如果会阻塞(block)的话就直接返回一个错误值,或者采用异步方式来运行这部分处理代码并在完成操作后调用相应的 done() 函数表示结束。

这是一个可能会产生长期影响的改动,目前还仅仅是个开始,所以在它进入 mainline 之前,很可能会发生改头换面的变化。事实上,为了回应 Darrick Wong 的一个意见,Axboe 已经对接口进行了调整,在 struct io_uring_pdu 中多提供了 8 个字节的空间。因为 Wong 指出,这对于 xfs_scrub 程序中能够批量提交 "数以百万计的 ioctl 调用" 会非常有用。

io_uring 正在迅速发展成为 Linux 的一种 shadow、asynchronous 的系统调用接口,目前还不清楚给 io_uring 增加这个类似 ioctl()的接口是否会引起争议,最初发布的邮件并没有得到任何回应。Axboe 表示希望新的 command 能比现有的 ioctl() command "a lot more sane and useful (更加合理、更加有用)",但似乎没有什么方法能确保这一点。和 ioctl()一样,新的 io_uring command 也都是完全在其他子系统中添加的,这些新增命令的审查程度也会各有不同。但是 io_uring 需要这种 "miscellaneous command" 的能力,就像整个系统也需要 ioctl()一样,所以如果这个功能如果最终没能合入 mailine,那才是出乎预料的事。

全文完
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、付费专栏及课程。

余额充值