深入Linux 0.11键盘驱动:从敲下按键到程序读取的完整数据流分析

张开发
2026/6/7 1:59:26 15 分钟阅读

分享文章

深入Linux 0.11键盘驱动:从敲下按键到程序读取的完整数据流分析
深入Linux 0.11键盘驱动从敲下按键到程序读取的完整数据流分析当你在终端输入abc时这三个字符究竟经历了怎样的旅程才被程序读取本文将带你深入Linux 0.11内核追踪键盘输入背后的完整数据流。不同于简单的操作指南我们将聚焦内核机制通过分析关键数据结构和函数调用揭示字符设备访问的底层原理。1. 键盘输入的整体流程概览键盘输入的处理流程可以概括为以下几个关键阶段硬件中断触发物理按键触发键盘控制器中断扫描码获取键盘驱动读取扫描码并转换为ASCII字符缓冲区写入字符被存入tty设备的环形缓冲区系统调用读取用户程序通过read系统调用从缓冲区获取字符让我们用一个简单的例子来说明当你在Bochs模拟器中输入abc并按下回车时内核中发生的完整处理流程。注意Linux 0.11作为早期版本其键盘驱动实现相对简单但基本架构与现代Linux内核一脉相承。2. 从硬件中断到字符转换2.1 中断处理入口当按键被按下时键盘控制器会触发IRQ1中断。在Linux 0.11中这个中断的处理函数是keyboard_interrupt定义在kernel/chr_drv/keyboard.S中。该函数的主要工作流程如下keyboard_interrupt: pushl %eax pushl %ebx pushl %ecx pushl %edx push %ds push %es movl $0x10,%eax mov %ax,%ds mov %ax,%es xor %al,%al /* %eax is scan code */ inb $0x60,%al /* 读取扫描码 */ ...这段汇编代码完成了以下关键操作保存寄存器状态设置数据段寄存器从端口0x60读取扫描码2.2 扫描码到ASCII的转换获取扫描码后内核需要将其转换为对应的ASCII字符。这一转换过程通过key_table和shift_map等映射表完成static unsigned char key_map[] { 0, 27, 1, 2, 3, 4, 5, 6, 7, 8, /* 0-9 */ 9, 0, -, , \b, /* Backspace */ \t, /* Tab */ q, w, e, r, /* 13-16 */ t, y, u, i, o, p, [, ], \n, /* Enter */ ... };对于我们的例子按键a、b、c的扫描码分别被转换为对应的ASCII码97、98、99。提示Shift键等修饰键的状态会影响最终生成的字符这通过shift_map等辅助映射表处理。3. 字符缓冲与tty子系统3.1 tty缓冲区结构转换后的字符不会立即传递给用户程序而是先被存入tty子系统的缓冲区。Linux 0.11中使用tty_buffer结构管理输入struct tty_buffer { struct tty_buffer *next; unsigned long data[1024]; unsigned long head; unsigned long tail; };关键字段说明字段名类型描述dataunsigned long[1024]存储字符数据的环形缓冲区headunsigned long写入位置指针tailunsigned long读取位置指针当输入abc时这三个字符会被依次写入data缓冲区head指针相应移动。3.2 缓冲区的写入过程字符写入缓冲区的核心逻辑在con_write函数中实现。简化后的关键代码如下void con_write(struct tty_struct *tty) { while (!FULL(tty-write_q)) { int c get_key(); PUTCH(c, tty-write_q); if (c \n) break; } }对于我们的例子字符a、b、c被依次放入缓冲区回车键(\n)触发行结束处理4. 从缓冲区到用户程序4.1 read系统调用的处理链当用户程序调用read读取键盘输入时内核会通过以下函数链处理请求sys_read(系统调用入口)tty_read(tty设备通用读取函数)con_read(控制台特定读取实现)con_read的关键操作是从缓冲区取出字符并复制到用户空间int con_read(struct tty_struct *tty, char *buf, int nr) { int c, i 0; while (i nr !EMPTY(tty-read_q)) { GETCH(c, tty-read_q); put_fs_byte(c, buf); i; } return i; }4.2 实际数据流追踪让我们用mygdb调试器观察输入abc时的实际数据流在Bochs中输入abc并回车在gdb中设置断点b keyboard_interrupt b con_write b con_read跟踪缓冲区状态变化操作head位置tail位置缓冲区内容初始00空输入a10a输入b20a,b输入c30a,b,c回车40a,b,c,\n5. 密码输入的特殊处理在第三关练习中输入secret时字符不会回显这涉及到tty的ECHO标志控制。关键代码在do_tty_interrupt中if (L_ECHO(tty)) { put_char(c); if (c \n) put_char(\r); }当终端设置为不回显模式时如密码输入场景L_ECHO标志被清除因此put_char不会被调用。通过mygdb可以验证这一行为设置断点在do_tty_interrupt观察tty-termios.c_lflag中的ECHO位状态变化第一次输入passwd时ECHO置位字符回显第二次输入secret时ECHO清除无回显理解这一机制对开发需要敏感信息输入的应用尤为重要。

更多文章