LWN:用户空间的中断!
共 3234字,需浏览 7分钟
·
2021-10-20 02:12
关注了就能看到更多这么棒的文章哦~
User-space interrupts
By Jonathan Corbet
September 30, 2021
LPC
DeepL assisted translation
https://lwn.net/Articles/871113/
"interrupt(中断)" 这个术语会让人联想到源自硬件的、由内核所处理的那个中断信号,甚至连软中断(software interrupt)也是属于内核的一个概念。但是,似乎有一个场景中会需要让用户空间的进程互相之间直接来发送 interrupt。在 2021 年的 Linux Plumbers 大会上,Sohil Mehta 主持了一场关于 Linux 如何支持这个功能的内核峰会。
Mehta 说,用户空间中断(user-spaceinterrupt,或简称 "user interrupt (用户中断)")这个功能的核心就是要以快速的方式来进行 event signaling (事件通知)。它可以绕过内核,直接向用户空间来传递 signal,从而实现更低的 latency。他说,我们现有的进程间通信机制都有一些局限。同步机制(synchronous mechanism)通常需要用一个专门的线程来做事,会有很高的延迟,而且通常效率很低。异步机制(比如说信号)的延迟甚至更高。所以往往我们唯一的选择就是轮询(polling),这种做法浪费了 CPU 时间。如果能有一个快速、高效的替代方案就更好了。
用户空间中断就是一个很好的选择,它将首先出现在英特尔的 "Sapphire Rapids" CPU 中。支持这一功能的 RFC patch 已于 9 月中旬发布了。这些 patch 支持 user-to-user 的信号发送,不需要经过内核,直接使用新的 SENDUIPI 指令就可以让一个进程直接向另一个进程发送中断了。未来的版本中还会包括 kernel-to-user 的信号发送,并最终支持让设备直接发送中断给到用户空间进程。
Mehta 提供了一些 benchmark 结果(可以在幻灯片中看到),数据显示用户空间中断比使用 eventfd()快 9 倍,比使用管道(pipe)或信号(signal)快 16 倍。如果接收进程在内核中被阻塞了的话,这个优势就会降低,因为在这种情况下无可避免地需要进行上下文切换。即使如此,用户空间中断对接收方来说也要快 10%,而对发送方来说则要快得更多,因为发送方根本不需要进入内核了。Florian Weimer 问道,用户空间中断与 futexes 相比如何,但看起来这方面还没有进行过测试。
这个功能的使用场景当然也会包括快速进程间通信(fast interprocess communication)。那些 user-mode CPU scheduler 调度器可以从中受益,用户空间的 I/O stack(例如 networking stack)也可以受益。要想充分利用这一特性,就需要对 libevent 和 liburing 等库进行修改。Mehta 说目前还没有真正在使用这个功能的应用,他很想听听大家的反馈看有哪些其他可能受益的应用。Ted Ts'o 建议在虚拟化环境中用来进行 host-to-guest 的唤醒;看起来这个用例正在进行调查,但目前还没有实际测试结果。
用户空间的进程出于各种原因不能随意向他人发送中断,这里需要进行一些设置。在接收方,相关的工作都始于一个调用:
uintr_register_handler(handler, flags);
其中 handler() 是用来处理用户空间中断的函数,flags 必须设为 0。handler 函数的定义需要特别注意,它的原型是这样的:
void __attribute__ ((interrupt))
handler(struct __uintr_frame *frame, unsigned long long vector);
接下来需要创建至少一个跟这个 handler 关联起来的文件描述符:
int uintr_create_fd(u64 vector, unsigned int flags);
这里,vector 是 0 到 63 之间的一个数字。每个 vector 都可以创建一个文件描述符。然后,该进程将该文件描述符交给发送方。如果发送方是同一进程中的另一个线程,那么这个传递就很简单,否则的话就需要使用 Unix 里的 socket 来传输这个文件描述符了。然后,发送方要通过以下方式来进行配置:
int uintr_register_sender(int fd, unsigned int flags);
其中 fd 是接收者传递过来的文件描述符,flags 和之前一样需要是 0。返回值是一个 handle,可以与 GCC 11 支持的 _senduipi() intrinsic 一起使用来实际进行中断发送到接收者。
中断的实际递交时机要取决于接收方当时正在做什么。如果该进程正在用户空间中运行,那么 handler 函数将会被立即调用,并取得相应的 vector 编号。一旦 handler 处理程序返回了,那么就会在之前被中断的位置继续执行。如果接收者此时被阻塞在内核的系统调用中,那么该中断将会在返回到用户空间时被触发,也就是说并不会打断当前正在进行的系统调用。在 patch set 中有一个 uintr_wait() 系统调用,它的作用是一直阻塞到收到一个用户空间中断,然后就立即返回,但此函数目前的行为还是一个示意性质的,需要等大家决定了这种情况下的理想行为之后再进行修改。
Prakesh Sangappa 询问大家是否真的有必要与所有相关的发送者来传递文件描述符。在一个可能有大量发送者的系统中,这个动作可能会耗费不少资源。Mehta 回答说这里有几个优化方案正在研究中。Ts'o 询问用户空间中断是否可以广播给多个接收者,回答是不支持广播。
Arnd Bergmann 想知道是否考虑过在老式 CPU 上模拟这一功能。答案似乎是肯定的,内核将捕获相关指令,并采取一种透明的方式来模拟实现它们的行为。Mehta 要求得到关于这个仿真机制的反馈,尤其是这个机制是否应该在其他架构上也实现。Bergmann 不赞成这个想法,他说,如果为这些架构也实现了用户空间中断的话,它们肯定与仿真出来的版本不兼容。他说,对其他架构的仿真应该只有在这些架构自己定义了它们自己的指令之后才能进行。
Greg Kroah-Hartman 询问 Clang 编译器是否支持 _senduipi() intrinsic。这个支持目前正在进行中,但尚未完成。Kroah-Hartman 还询问了从该功能中受益的工作场景的更多细节,Mehta 回答说他也还没有什么具体的东西可以提供。
时间已经不多了,结束这次会议之前,Mehta 问了一个问题,当接收者在系统调用中被阻止时应该发生什么。如前所述,目前的 patch set 在完成中断传递之前会一直等待系统调用返回。我们是否应该改变行为,使之更接近于信号,也就是立即会完成中断传递,而系统调用则被打断并返回 EINTR?没有人对这个问题发表意见,会议就此结束了。
这个讲座的视频可以在 YouTube 上找到。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~