我目前正在总体了解 Linux 内核和操作系统,虽然我发现了许多有关 IRQ、驱动程序、调度和其他重要操作系统概念的优秀资源,以及与键盘相关的资源,但我很难将它们放在一起全面概述 Linux 内核如何处理键盘上的按钮按下。在这个阶段,我并不试图理解每一个细节,而是试图在某种程度上全面地连接概念。
我想到了以下场景:
- 我使用的是带有单处理器的 x64 机器。
- 有几个进程正在运行,特别是编辑器
VIM
(Process #1
) 和 sayLibreOffice
(Process #2
)。 - 我在里面
VIM
并按a
-键。但是,当前正在运行的进程Process #2
(VIM
接下来将被安排)。
这就是我想象的事情现在会发生的情况:
- 键盘通过一系列步骤生成电信号(USB 协议编码)并通过 USB 线发送。
- 信号由 USB 控制器处理,并通过 PCI-e(可能还有其他控制器/总线?)发送到中断控制器 (
APIC
)。触发处理器的APIC
。INT Pin
- 处理器切换到
Kernel Mode
并IRQ-Number
从 请求APIC
,并将其用作Interrupt Descriptor Table Register
(IDTR
) 的偏移量。获得一个描述符,然后使用该描述符来获得中断处理程序例程的地址。据我了解,这个中断处理程序最初是由键盘驱动程序注册的? - 调用中断处理程序(在本例中为键盘处理程序)。
这引出了我的主要问题:中断处理程序例程通过哪种机制将按下的键传达给正确的进程(Process #1
)?它实际上是这样做的,还是只是将按下的键写入缓冲区(可通过char-device
?),该缓冲区一次对一个进程只读(目前“随附的”到Process #1
)?我不明白什么时候Process #1
收到钥匙。它是否立即处理数据,作为中断处理程序时间表立即处理,还是下次调度程序调度时处理关键数据?
- 当此处理程序返回 (
IRET
) 时,上下文将切换回先前执行的进程 (Process #2
)。
答案1
到目前为止,您的理解是正确的,但您错过了在此基础上构建的大部分复杂性。内核中的处理发生在多个层中,并且按键通过这些层“冒泡”。
USB 通信协议本身涉及的内容要多得多。 USB 的中断处理程序例程会处理此问题,并根据需要从多个片段组装一个完整的 USB 数据包。
按键使用所谓的高压气体放电管(“人机接口设备”)协议,构建在 USB 之上。因此较低的USB内核层检测到完整的消息是USB HID事件,并将其传递给内核中的HID层。
HID 层根据初始化时设备所需的 HID 描述符来解释此事件。然后它将事件传递到输入层。单个 HID 事件可以生成多个按键事件。
输入层使用内核键盘布局表将扫描代码(键盘上按键的位置)映射到键代码(如A
)并解释Shift、Alt等。此解释的结果可通过/dev/input/event*
用户态进程获得。您可以用来evtest
实时观看这些事件。
但到这里处理还没有结束。 X Server(负责图形)有一个通用evdev
驱动程序,可以从设备读取事件/dev/input/event*
,然后映射它们再次根据第二组键盘布局表(您可以部分使用 XKBD 扩展查看这些键盘布局表,xmodmap
并完全通过 XKBD 扩展查看这些键盘布局表)。这是因为 X 服务器早于内核输入层,并且在早期有直接处理鼠标和 PS/2 键的驱动程序。
然后,X 服务器向 X 客户端(应用程序)发送包含键盘事件的消息。您可以使用该应用程序查看这些消息xev
。LibreOffice
将直接处理此事件,VIM
将在将处理该事件的 an 中运行xterm
,并且(您猜对了)再次向其添加一些额外的处理,最后将其传递给VIM
via stdin
。
够复杂吗?
答案2
它只是将按下的键写入缓冲区(通过字符设备可用吗?)
是的,我应该说。
然后有一种从(低级)控制台到 tty(虚拟)再到伪 tty 的级联。按键操作将写入 /dev/tty1 或 /dev/tty5,具体取决于哪个“控制台”处于活动状态。
在 xterm 中(ps axf 输出):
467 tty1 Ss 0:38 \_ -bash
5820 tty1 S+ 0:00 \_ xinit fvwm -- vt9
5821 tty9 S<sl+ 54:15 \_ /usr/lib/Xorg :0 vt9
5831 tty1 S 0:00 \_ xterm -geometry +1+1 -n login fvwm
5833 pts/0 Ss+ 0:38 \_ fvwm
...
...
773 pts/0 S 0:07 \_ xterm
775 pts/2 Ss+ 0:00 | \_ bash
14452 pts/0 S 0:04 \_ xterm
14454 pts/1 Ss 0:00 | \_ bash
14507 pts/1 S 0:00 | \_ xfontsel
31044 pts/1 R+ 0:00 | \_ ps ax f
19549 pts/0 S 0:00 \_ xterm
19551 pts/3 Ss+ 0:00 \_ bash
这显示了 Xorg 如何从 tty1 在 tty9 上启动,以及 fvwm(窗口管理器)和 xterm(终端仿真器)如何“获取”/dev/pts/0,并且是新的 shell 获取 /dev/pts/1、pts /2。 pts/3 等等。
现在,无论我是否通过以下方式激活 pid 19551 pts/3 bash 进程指点在它的 xterm 窗口中,然后按一个键,或者如果我echo hello >/dev/pts/3
从像 /dev/tty5 这样的控制台 vt 执行此操作,则字符会转到正确的进程。
man ps
在“进程状态代码”下解释(嗯,它确实列出了它们):
S interruptible sleep (waiting for an event to complete)
s is a session leader
+ is in the foreground process group
我给你留下这些关键词...