了解操作系统store/retrieve IO 设备如何输入

Understanding how operating systems store/retrieve IO device input

我对 I/O 设备(如键盘)如何存储其输入以供操作系统或应用程序使用感到有点困惑。如果我有一台单处理器计算机(CPU 单核计算机),并且当前正在执行的进程是游戏,那么游戏如何能够“感知”键盘输入?即使按键会强制硬件中断(并因此进行上下文切换),然后每当 OS 将控制权交还给游戏进程时将键值“反馈”给游戏,也不能保证游戏当时循环甚至会检查玩家输入,它也可能会更新游戏对象位置或渲染游戏世界。

所以我的问题是...

  1. I/O 键盘等设备将输入存储到某种板载硬件特定微处理器或板载内存存储缓冲区队列中,稍后可以由外部进程或OS 本身?非常感谢任何见解,谢谢!

Do I/O devices like keyboards store there input to some kind of on-board hardware specific microprocessors or on-board memory storage buffer queues, which can later be "read" and flushed by external processes or the OS itself?

让我们把它分成 3 个部分..

设备特定部分

旧键盘(USB 之前);键盘中的微控制器定期扫描开关网格并检测何时按下或释放键,将其转换为代码(可能是多个字节),然后一次将代码按字节发送到计算机。计算机还有一个小微控制器来接收这些字节。该微控制器有一个 1 字节缓冲区(甚至不足以容纳 multi-byte 代码)。

对于较新的键盘 (USB);键盘的内部构造基本相同(微控制器扫描开关网格等);但是 USB 控制器询问键盘“有什么事吗?”定期(通常每 8 毫秒一次)键盘的微控制器回复。

无论如何;键盘驱动程序获取来自键盘的代码并对其进行处理;通常将其转换为固定长度的“键码”,将其与其他数据合并(如果 shift 或 capslock 或...当时处于活动状态;如果存在对键有意义的 unicode codepoint/s 等)和将所有这些捆绑到一个数据结构中。

OS 特定部分

该数据结构(由键盘驱动程序创建)通常由 OS 标准化为“用户输入事件”(因此,键盘、鼠标、触摸屏、操纵杆、 ...).

“用户输入事件”是通过驱动程序发送的。某种形式的 inter-process 通信(管道、消息等)到其他东西(例如 GUI)。这种 inter-process 通信有 2 个常见行为 - 如果接收程序被阻塞等待接收事件,那么调度程序会解除它的阻塞(取消等待)并安排它再次获得 CPU 时间;如果接收程序没有等待,事件通常会被放入待处理事件的队列(在内存中)。

当然经常涉及很多进程,“用户输入事件”可能从一个进程(例如输入法编辑器)转发到另一个进程(例如GUI)再到另一个进程(例如哪个window有键盘焦点)。此外(对于旧的遗留命令行内容)它可能最终在翻译层(例如终端仿真器)将事件转换为字符流(stdin)同时破坏大部分信息(例如当释放键时).

语言特定部分

要从高级代码中获取事件,这取决于语言是什么,有时还取决于所使用的库。最常见的是某种“getEvent()”,它会导致程序从其队列(从内存)中获取下一个事件;如果还没有任何事件,可能会导致程序等待(并且不使用任何 CPU 时间)。然而,通常它被隐藏得更深,这样你注册一个回调,然后当其他东西调用“getEvent()”时,当它收到一个偶数时,它调用你注册的回调;所以它最终可能会像(例如 Java)public boolean handleEvent(Event evt) { switch (evt.id) { case Event.KEY_PRESS: ....

今天的键盘大多是 USB。在包括 ARM 计算机在内的大多数计算机上,您都有一个 USB 控制器,它实现了由几家主要科技公司开发的 xHCI 规范。如果你 google "xhci intel spec" 或类似的东西,第一个 link 左右应该是完整规范的 link。

xHCI 规范要求实现采用 PCI 设备的形式。 PCI 是由 PCI-Seg 小组开发的另一个规范。该规范指定了硬件要求的所有内容。它不是像 xHCI 这样的免费规范。获得它实际上相当昂贵(大约 3k $)。

OS 检测使用 ACPI 或类似规范的 PCI 设备,这些规范有时可能是板特定的(特别是对于 ARM,因为所有基于 x86 的计算机都实现 ACPI)。在 RAM 的常规位置找到的 ACPI 表提到在哪里可以找到每个 PCI 设备的内存映射配置 space 的基址。

OS 使用内存映射到 RAM 的寄存器与 PCI 设备交互。 OS reads/writes 位于 ACPI 表和配置 space 本身指定的位置,以收集有关某个设备的信息并使该设备代表它进行操作。 PCI 设备的配置 space 有一个通用部分(每个设备都相同)和一个更具体的部分(取决于设备)。在一般部分,有 BAR 寄存器,其中包含配置的设备相关部分的地址 space。公约的每个实施者都可以对设备特定部分做任何他们想做的事情。每个设备的一般部分必须有些相似,以便 OS 可以正确检测和初始化设备。

今天,每种类型的设备都必须遵守特定的约定才能与当前的计算机一起工作。例如,hard-disks 将实现 SATA,并且主板将具有 AHCI(PCI SATA 控制器)。同样,键盘将实现 USB 并且开发板具有 xHCI。

xHCI本身就有复杂的交互机制。键盘的总结是 OS 将“激活”键盘的中断 IN 端点。 OS 然后将传输请求 (TRB) 放置在 RAM 中的圆环上。对于中断端点,xHCI 将在每个由特定 xHCI 寄存器中的 OS 指定的时间间隔读取一个传输请求。当 xHCI 执行 TRB 时(当它进行传输时),它将 post 一个事件发送到 RAM 中另一个称为事件环的圆环。当一个新事件被 posted 时,xHCI 触发一个中断。在中断处理程序中,OS 将读取 Event Ring 以确定发生了什么。对于键盘,OS 可能会看到传输已完成并读取数据。大多数情况下,键盘将 return 8 个字节。每个字节的解释是传输时按下的键。字节包含常规扫描码。所以它们不是直接采用 UTF-8 或 ASCII 格式。每个键盘键有一个扫描码。由 OS 决定根据键盘键做什么。例如,如果数据说 'A' 键被按下,那么 OS 可以查看 'SHIFT' 键是否被按下来确定 'A' 应该是大写还是小写。如果下一个报告说 'A' 键没有被按下,那么 OS 应该认为这是键的释放(用户释放了键)。换句话说,OS 以一定的时间间隔轮询键盘。

中断处理程序可能会将密钥传递给内核的其他部分,并将密钥保存到特定于进程的结构中。该进程可能会轮询一个包含事件的受锁保护缓冲区。它也可以是一个系统范围的缓冲区,只包含所有事件。

更高级别的实现可能在 OS 之间有所不同。如果你了解底层的内部工作原理,你可能会想象 OS 是如何工作的。可以肯定的是,整个事情非常复杂,因为现在的 CPU 有多个内核和缓存以及其他复杂的机制。 OS 必须确保与所有这些机制无缝协作,因此它非常复杂但可以实现。要了解整个事情,您可能需要了解整个 OS。它在其他 OS 概念中有很多分支。