驱动程序与跨进程通信

用户态驱动

在 Linux 系统中,大部分硬件驱动位于内核代码仓库中,与内核共同编译、运行。这种设计在安全性、 扩展性、可维护性均存在一定的问题。尽管与微内核中,调用驱动需要经过 IPC 的方式相比, 将驱动放在内核态的性能通常较高,但仍然存在系统调用带来的上下文切换开销, 在频繁访问外设时会带来显著的性能下降。

为了进一步提高性能,一些实现方案(如 SPDK )将硬件驱动完全移入用户态,作为应用程序的一部分。 通过将外设的内核驱动重新绑定到 uio 或 vfio ,用户进程可以直接访问外设的地址空间,操作外设, 省去了系统调用和 IPC 的开销。这些方案通常是基于轮询实现的,一个重要原因在于, 现有的硬件不支持将外设中断交由用户态程序处理。

Linux 中的信号机制

信号是 Linux 中的一种基本的 IPC 机制,当进程从内核态返回用户态时,如果有待处理的信号, 内核将在用户栈上构造一个信号上下文,调用用户的信号处理函数;信号处理完成后,由跳板代码再调用 sys_sigreturn() ,进入内核中,恢复进程的正常执行流的上下文,返回用户态,进程继续执行。

站在用户程序的视角,信号与中断机制有诸多相似之处:产生的时间不确定、会中断当前执行流、 通常需要额外的上下文切换。可以说信号是由内核软件模拟的一种中断机制。这种模拟的代价则是多次特权级切换的开销。

对 IPC 优化的一个重要思路则是在通信路径上减少乃至完全消除内核的参与,例如 XPC 和 SkyBridge 都设计了方案允许发送方直接切换到接收方的地址空间访问数据和执行代码,同时在正常执行流中仍然保持一定的隔离性。 我们的方案中则希望应用程序之间的用户态中断的发送和接收可以无需内核介入。