LWN: io_uring中的ioctl()!

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

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

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 深度文章以及开源社区的各种新近言论~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值