5.5: Real world
Xv6, 就像许多操作系统一样,在内核执行期间允许中断甚至上下文切换(通过 yield)。这样做的原因是,在复杂的系统调用运行较长时间时,能够保持快速的响应时间。然而,如上所述,允许在内核中进行中断会引入一些复杂性;因此,一些操作系统只允许在执行用户代码时进行中断。
支持典型计算机上的所有设备并使其充分发挥作用需要大量工作,因为设备种类繁多,设备具有许多功能,并且设备与驱动程序之间的协议可能很复杂且文档不全。在许多操作系统中,驱动程序的代码量超过了核心内核的代码量。
UART 驱动程序通过读取 UART 控制寄存器逐字节获取数据;这种模式称为程序化I/O
,因为是软件在驱动数据移动。程序化I/O
很简单,但数据速率过高时它太慢,不能使用。需要高速度传输大量数据的设备通常使用直接内存访问(DMA)。DMA 设备硬件直接将传入数据写入 RAM,并从 RAM 读取传出数据。现代的磁盘和网络设备使用 DMA。DMA 设备的驱动程序会准备好 RAM 中的数据,然后通过写入控制寄存器的单次操作告诉设备处理这些准备好的数据。
当设备在不可预测的时间需要注意时,中断是有意义的,而且这种情况发生的频率不高。然而,中断会带来很高的 CPU 开销。
因此,高速设备(如网络和磁盘控制器)使用一些技巧来减少对中断的依赖。
一种技巧是为一整批进出请求引发单个中断。
另一种技巧是驱动程序完全禁用中断,并定期检查设备是否需要关注。这种技术叫做轮询。 轮询在设备执行高频率操作时很有意义,但如果设备大部分时间处于空闲状态,它会浪费 CPU 时间。有些驱动程序根据当前设备负载动态地在轮询和中断之间切换。
UART 驱动程序首先将传入的数据复制到内核中的一个缓冲区,然后再复制到用户空间。这在低数据速率时是合理的,但对于生成或消费数据非常快的设备,这种双重复制会显著降低性能。一些操作系统能够直接在用户空间缓冲区和设备硬件之间移动数据,通常使用 DMA。
如第 1 章所述,控制台对应用程序而言表现为一个普通文件,应用程序通过 read 和 write 系统调用读取输入和写入输出。应用程序可能需要控制一些无法通过标准文件系统调用表达的设备方面(例如,启用/禁用控制台驱动中的行缓冲)。Unix 操作系统通过 ioctl 系统调用来支持这种情况。
一些计算机使用场景要求系统必须在有限的时间内响应。例如,在安全关键系统中,错过截止日期可能导致灾难。Xv6 不适用于硬实时设置。硬实时操作系统通常是与应用程序链接的库,允许进行分析以确定最坏情况的响应时间。Xv6 也不适用于软实时应用程序,在这些应用程序中,偶尔错过截止日期是可以接受的,因为 Xv6 的调度器过于简单,而且它有内核代码路径,其中中断会被禁用较长时间。