资讯动态

键盘按下背后的中断秘密

发布时间:2026/10/15 1:57:38 来源:尧图企业网站定制
先记住一句话按下一个键键盘把它变成一个编号扫描码通过线路送到电脑硬件“拍一下 CPU 的肩膀”中断CPU 暂停手头的事跳去执行一小段处理程序把编号取走再回去继续干活。之后这个编号再一层层交给操作系统和程序最终变成屏幕上的一个字。一、为什么要用“中断”CPU 想知道你有没有按键有两种办法办法 1轮询Polling CPU 每隔一会儿问键盘“有键按下吗”“有吗”“有吗” → 你一分钟可能只按几十次键CPU 却问了几百万次 → 大部分时间白问 办法 2中断Interrupt CPU 只管干自己的事 键盘有消息时主动“拍一下 CPU 的肩膀” → CPU 只在真正有事时才处理比喻轮询 每隔 1 分钟去门口看看快递到了没 中断 快递员到了按门铃你再去开门二、全景图一个键从按下到显示以按下字母A为例先讲经典的 PS/2 键盘USB 键盘第八节单独讲┌────────────────── 硬件 ──────────────────┐ │ │ │ ① 按键接通 → 键盘内部芯片扫描发现 │ │ ② 生成扫描码A 的按下码 │ │ ③ 通过 PS/2 线串行发送到主板 │ │ ④ 主板键盘控制器8042收下放进缓冲区 │ │ ⑤ 拉起中断线 IRQ1 │ │ ⑥ 中断控制器PIC / APIC通知 CPU │ └──────────────────┬───────────────────────┘ ▼ ┌────────────────── CPU ───────────────────┐ │ ⑦ 执行完当前指令响应中断 │ │ ⑧ 保存现场查中断向量表跳到处理程序 │ └──────────────────┬───────────────────────┘ ▼ ┌────────────────── 操作系统 ───────────────┐ │ ⑨ 中断处理程序读出扫描码通知控制器“处理完了”│ │ ⑩ 恢复现场CPU 回去继续原来的工作 │ │ ⑪ 稍后扫描码 → 键码 → 字符按键盘布局 │ │ ⑫ 放进当前窗口程序的消息队列 │ └──────────────────┬───────────────────────┘ ▼ ┌────────────────── 应用程序 ───────────────┐ │ ⑬ 程序取出消息在屏幕上显示 “a” │ └──────────────────────────────────────────┘下面一步一步展开。三、第一段键盘内部发生了什么1. 按键矩阵键盘怎么知道哪个键被按下键盘上有一百多个键但不是每个键都单独接一根线而是排成行列矩阵列0 列1 列2 列3 行0 ── Q ── W ── E ── R ── 行1 ── A ── S ── D ── F ── 行2 ── Z ── X ── C ── V ──键盘里有一颗小单片机不停地扫描给 行0 通电 → 看哪一列有电 → 没有 给 行1 通电 → 列0 有电了→ 行1 列0 A 键被按下 给 行2 通电 → …… 每秒扫描几百到上千遍 注意键盘内部其实是轮询的。它自己有专门的芯片在不停扫描这样电脑的 CPU 就不用轮询了。2. 消抖Debounce机械按键接触瞬间会“抖”几下理想 ___________┌────────── 实际 ___________┌┐┌┐┌────── ← 几毫秒内通断好几次单片机会等信号稳定通常几毫秒才确认否则按一次会被当成按了好几次。3. 生成扫描码Scan Code确认后键盘给这个键一个编号。注意它发的是“哪个位置的键”不是“什么字母”按下 A发送“按下码”Make Code 松开 A发送“松开码”Break Code扫描码集A 按下A 松开Set 1最早的 IBM XT1E9E 1E 80Set 2PS/2 键盘默认1CF0 1C主板上的控制器默认会把 Set 2翻译成 Set 1再交给 CPU所以操作系统通常看到的是1E。为什么按下和松开都要发因为系统要知道 Shift、Ctrl 是不是一直按着游戏要知道 W 键按了多久。4. 长按连发TypematicPS/2 键盘长按一个键时键盘自己会每隔一段时间重复发送按下码先等约 500ms再每秒约 10~30 次。四、第二段数据怎么传到主板1. PS/2 线路PS/2 只有两根信号线时钟线和数据线。由键盘产生时钟一位一位地发一帧 11 位 ┌────┬────────────────────┬──────┬────┐ │起始│ 8 位数据低位在前│ 校验 │停止│ │ 0 │ 0 1 1 1 1 0 0 0 │ 奇校验│ 1 │ └────┴────────────────────┴──────┴────┘ ↑ 0x1E 从最低位开始发 时钟约 10~16.7 kHz一个字节约 1 毫秒2. 主板键盘控制器8042主板上有一个键盘控制器最早是 Intel 8042 芯片现在集成在南桥 / Super I/O 芯片里① 收齐 11 位检查校验 ② 把扫描码放进自己的“输出缓冲区”只有 1 个字节 ③ 把状态寄存器里的“有数据”标志置 1 ④ 拉起中断请求线 IRQ1CPU 之后会通过I/O 端口访问它端口作用0x60数据端口读出扫描码0x64状态 / 命令端口查看状态、发送命令 缓冲区只有 1 字节所以 CPU 必须尽快取走否则下一个扫描码就没地方放了。这正是需要中断“及时通知”的原因之一。五、第三段中断信号怎么到达 CPU1. 中断控制器总机接线员电脑里有很多设备都会发中断键盘、硬盘、网卡、时钟……CPU 不可能每个都接一根线。中间有一个中断控制器负责汇总时钟 → IRQ0 ┐ 键盘 → IRQ1 ├─→ 中断控制器 ──→ CPU 的中断引脚 串口 → IRQ4 │ 排优先级、决定发给谁 硬盘 → IRQ14┘它像公司前台接线员· 多个电话同时打进来 → 按优先级排队 · 告诉老板CPU“是 1 号线键盘找你” · 老板说“处理完了”之前同一条线先不再打扰控制器年代特点8259 PIC早期 PC两片级联共 15 条线单 CPUAPICIOAPIC 每个核的 Local APIC现代电脑支持多核可以把中断分发给不同的 CPU 核心2. 中断号中断向量控制器告诉 CPU 的不是“IRQ1”而是一个中断向量号0~255CPU 用它去查表实模式DOS / 老 BIOSIRQ1 → 中断号 9INT 09h 保护模式Windows / Linux 0~31 号被 CPU 自己的异常占用除零、缺页等 → 操作系统把硬件中断重新映射到 32 号之后3. 可屏蔽中断键盘中断是可屏蔽的CPU 标志寄存器里有一个 IF 位中断允许位 IF 1 → 接受中断 IF 0 → 暂时不理比如内核在做一段不能被打断的操作 → 中断会在控制器里等着等 IF 打开后再送达另外中断控制器也能单独屏蔽某一条线比如只屏蔽键盘。六、第四段CPU 如何响应核心假设 CPU 正在执行一个程序计算到一半。第 1 步执行完当前这条指令CPU 不会在一条指令执行到一半时停下 → 每执行完一条指令检查一下“有中断来了吗IF 是 1 吗” → 有就开始响应第 2 步获取中断向量8259 PICCPU 发出“中断确认”信号PIC 把向量号放到总线上 APIC 中断以消息形式直接送到核心的 Local APIC带着向量号第 3 步保存现场CPU 必须记住“我刚才干到哪了”回来才能接着干硬件自动压栈以 x86 为例 ┌─────────────┐ │ SS / ESP │ ← 原来的栈位置从用户态进入内核时才需要需切到内核栈 │ EFLAGS │ ← 标志寄存器包括 IF │ CS │ ← 原来的代码段 │ EIP │ ← 下一条要执行的指令地址书签 └─────────────┘如果当时正在运行普通程序用户态Ring 3CPU 还会切换到内核态Ring 0并换成内核专用的栈地址从 TSS 里取。第 4 步查中断描述符表跳转中断描述符表IDT一张 256 项的表 第 N 项 “收到 N 号中断时去执行哪个函数” CPU向量号 → 查 IDT → 得到处理程序地址 → 跳过去对于“中断门”CPU 还会自动把IF 清 0防止处理程序刚开始就又被别的中断打断。第 5 步执行中断处理程序ISR软件接手处理程序先把硬件没保存的寄存器也存起来然后① 从端口 0x60 读出扫描码0x1E → 读完后 8042 的缓冲区空了可以接收下一个 ② 把扫描码放进内核的一个缓冲区 ③ 向中断控制器发送 EOIEnd Of Interrupt“处理完了” → 不发的话控制器会认为键盘中断还没处理完不再送来新的 ④ 安排后续工作见下一节第 6 步返回恢复软件保存的寄存器 执行 IRET 指令 → CPU 从栈里弹出 EIP、CS、EFLAGS及 ESP、SS → 回到被打断的那条指令之后继续运行 → 被打断的程序完全不知道刚才发生过什么整个过程通常只有几微秒。时间线示意程序A: ██████████████▏ ▕████████████ ↑ ↑ 中断到达 IRET 返回 CPU: ├ 保存现场 ┤ ISR读扫描码 ├ 恢复 ┤ └──────── 几微秒 ────────┘七、第五段上半部与下半部中断处理程序运行时往往会屏蔽其他中断所以它必须极快。操作系统把工作拆成两半上半部硬中断立刻做 读扫描码、发 EOI、记录下来 → 越短越好 下半部延后做可以被打断 扫描码翻译、通知上层、唤醒等待输入的程序系统上半部下半部LinuxIRQ handlersoftirq / tasklet / workqueueWindowsISRDPC延迟过程调用比喻前台接到快递上半部签收放在桌上马上回去接电话 稍后有空时下半部拆包、分类、送到每个人工位八、USB 键盘其实不太一样现在大多数键盘是 USB 的这里有一个常见误解USB 键盘本身不能主动打断 CPU。USB 是“主机问、设备答”的主机不问设备不能说话。1. 轮询发生在 USB 控制器上USB 键盘使用的传输方式叫“中断传输”但它名字带中断实际是定时轮询USB 主机控制器硬件不是 CPU 每隔固定时间1ms ~ 10ms由键盘声明常见 8ms游戏键盘 1ms 甚至更短 问键盘“有新数据吗” → 没变化键盘回答“没有”NAK → 有变化键盘发来一份报告2. 报告格式直接告诉“现在哪些键按着”USB 键盘不是发“按下 / 松开”事件而是发当前状态快照标准启动协议 8 字节┌──────────┬──────┬───────────────────────────────┐ │ 修饰键位 │ 保留 │ 最多 6 个正在按下的键 │ │ Ctrl/Shift│ │ 04 00 00 00 00 00 │ └──────────┴──────┴───────────────────────────────┘ ↑ 04 A 键USB HID 编号和 PS/2 扫描码不同这也是“6 键无冲”6KRO这个说法的来源全键无冲的键盘会使用扩展的报告格式。系统对比前后两份报告才算出“A 刚被按下”或“A 刚被松开”。3. CPU 的中断来自 USB 控制器键盘 ──数据──→ USB 主机控制器xHCI │ ├─ 把数据通过 DMA 直接写进内存 └─ 向 CPU 发一个中断通常是 MSI靠写内存地址触发不走中断线 ↓ CPU 执行 USB 控制器驱动的处理程序 ↓ USB 驱动 → HID 驱动 → 解析报告 → 键盘事件4. 对比PS/2 键盘USB 键盘谁主动键盘主动发主机定时问谁触发 CPU 中断键盘控制器IRQ1USB 主机控制器MSI数据形式按下码 / 松开码事件当前按键状态快照键码扫描码A 1EHID 用途码A 04长按连发键盘硬件生成操作系统软件生成同时按键数理论上不受协议限制启动协议 6 键 修饰键 开机进 BIOS 时操作系统还没加载USB 键盘靠主板的“Legacy USB”功能在后台模拟成老式 8042 键盘让老程序也能读端口 0x60。九、第六段从编号到字符到这里系统只知道“某个位置的键被按下了”还不知道是什么字。1. 三层翻译扫描码 / HID 码硬件编号“第 2 排第 1 个键” ↓ 驱动统一 虚拟键码 / 键码与硬件无关“A 键” ↓ 根据键盘布局 Shift/CapsLock 状态 字符a 或 A ↓ 如果开了输入法 输入法组合成 “啊”为什么分开同一个物理位置 美式键盘 → A 法式键盘AZERTY→ Q 硬件只管位置布局由系统软件决定游戏里用 WASD 移动通常用的是键码 / 物理位置聊天框打字用的是字符。2. Windows 的路线i8042prt.sysPS/2或 kbdhid.sysUSB ↓ kbdclass.sys键盘类驱动 ↓ win32k 的原始输入线程RIT ↓ 判断当前前台窗口 该窗口所属线程的消息队列WM_KEYDOWN带虚拟键码 VK_A ↓ 程序的消息循环 GetMessage → TranslateMessage 生成 WM_CHAR字符 a ↓ 窗口过程处理显示在屏幕上3. Linux 的路线i8042 驱动 → atkbd 驱动PS/2或 usbhidUSB ↓ input 子系统统一成事件 EV_KEY, KEY_A, 按下 ↓ /dev/input/eventXevdev ↓ libinput → 桌面合成器Wayland或 X Server ↓ xkb 按布局翻译成字符 发给当前焦点窗口的程序可以用cat /proc/interrupts看各个中断发生了多少次用evtest实时看键盘事件。十、程序这一层最后又变成了“轮询”很有意思的是到了应用程序尤其是游戏通常又是每帧去查一次操作系统键盘事件一来就排进消息队列事件驱动 游戏引擎每帧开始时把队列里的事件全部取出更新按键状态 你的代码if (Input.GetKeyDown(KeyCode.A)) { ... } → 本质是每帧查一次状态所以一次按键的延迟可以粗略拆成环节大约延迟键盘扫描 消抖1 ~ 10 ms不同键盘差别大传输USB 轮询间隔0.125 ~ 8 ms中断 驱动 系统分发通常不到 1 ms等游戏下一帧读取0 ~ 16.7 ms60 帧时渲染 显示器刷新一到数帧中断本身是最快的那一环真正的延迟大头在键盘内部、轮询间隔和帧率上。这就是游戏键盘宣传“1000Hz 回报率”、电竞显示器宣传高刷新率的原因。十一、常见问题问题原因同时按很多键有的键没反应鬼键 / 冲突按键矩阵电路限制或 USB 启动协议只能报 6 个键按一下出两个字开关老化抖动超过消抖时间系统卡死时键盘没反应但大小写灯能亮PS/2 时代灯是系统控制的卡死时中断可能被屏蔽或系统无法处理CtrlAltDel 在卡死时仍能用Windows它在很底层被系统专门截获不经过普通程序的消息队列按键漏掉中断被屏蔽太久硬件缓冲区被覆盖PS/2 只有 1 字节一句话总结按下一个键时键盘内部芯片扫描矩阵、消抖后生成扫描码经线路送到主板控制器控制器通过中断控制器向 CPU 发出中断CPU 执行完当前指令后保存现场、查中断描述符表跳到处理程序处理程序从端口读出扫描码、发送 EOI 后立即返回剩下的翻译工作交给“下半部”慢慢做系统再把扫描码按键盘布局翻译成字符送进前台程序的消息队列。USB 键盘的区别在于设备是被 USB 控制器定时轮询的真正打断 CPU 的是 USB 控制器。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑