游客发表

因为计算机丢失xinuput 1

发帖时间:2026-10-17 05:09:11

```html 因为计算机丢失xinuput 1

因为计算机丢失xinuput 1

一个关于输入设备、内核恐慌与深夜调试的计算机丢故事

屏幕上那行字冷冰冰地戳在那儿:因为计算机丢失xinuput 1。没有蓝屏,计算机丢没有转圈,计算机丢连个错误代码都不给。计算机丢我盯着这台老ThinkPad的计算机丢终端,光标闪了七下,计算机丢我才反应过来——这不是计算机丢我敲的命令,是计算机丢系统自己吐出来的。准确地说,计算机丢是计算机丢那个跑在裸机上的微型内核在串口控制台里留下的遗言。

事情得从三天前说起。计算机丢我在折腾一个玩具操作系统,计算机丢xinuput 是计算机丢我自己写的输入驱动模块,名字起得随意,计算机丢xinu 是“Xinu is not Unix”的递归梗,put 就是 input 的尾巴。整个模块负责从PS/2键盘控制器里抠出扫描码,翻译成字符,塞进环形缓冲区。代码不到两百行,但耦合了中断处理、端口IO和一点点状态机。编译通过那天我挺得意,觉得自己终于摸到了硬件交互的门槛。

然后它就丢了。不是文件丢失——ls 显示 xinuput.o 好好躺在 build 目录里。是运行时丢失。具体表现是:系统启动后能响应前几个按键,敲个 help 没问题,但一旦快速连按或者按住某个键超过两秒,整个输入链路就断了。串口那边安静得像被拔了线,再敲什么都是石沉大海。更诡异的是,如果这时候触发一个软中断,内核会打印出那行字:因为计算机丢失xinuput 1。我翻遍源码,根本没写过这句中文。它像是从某个被覆盖的内存页里冒出来的幽灵字符串。

昨晚两点,我决定用最笨的办法定位。在键盘中断处理函数的第一行加了个 outb(0x3F8, 'K'),往串口直接吐一个字符。重新编译,启动。前五次按键,串口收到五个 K。第六次,没有 K。中断没进来。可 IRQ1 明明在 PIC 里是使能的,IDT 表项也没动过。我盯着 QEMU 的 -d int 日志,看到第六次按键时 CPU 确实收到了中断向量 0x21,但 handler 的地址被改成了 0x00000000。谁改的?我翻出中断描述符表的初始化代码,发现一个手滑:在设置键盘中断门的时候,我把 idt_set_gate(1, (uint32_t)keyboard_handler, 0x08, 0x8E) 写成了 idt_set_gate(1, (uint32_t)keyboard_handler, 0x08, 0x8E)——看起来一样对吧?但 keyboard_handler 这个符号在链接脚本里被放到了 .text 段,而我的 .text 段起始地址是 0x100000。问题出在 idt_set_gate 的第四个参数:我传了 0x8E,但函数原型里这个参数是 uint8_t flags,而 0x8E 被隐式转换成了 int,然后……算了,细节不重要。重要的是,那个门描述符的高 16 位地址被写成了 0。因为 keyboard_handler 的地址是 0x100abc,低 16 位是 0x0abc,高 16 位是 0x0010。但我的赋值语句里,高 16 位那部分被一个多余的 & 0xFFFF 给抹掉了。于是中断一来,CPU 跳转到 0x00000abc——一个未映射的页。缺页异常,然后内核 panic,打印出那行字。

至于为什么是中文?因为 panic 函数里我偷懒,直接 printf("因为计算机丢失%s %d\n", "xinuput", 1)。那个 %s 本来想输出模块名,结果字符串常量池里恰好有个“xinuput”,而 1 是中断号。整句话连起来就是“因为计算机丢失xinuput 1”。一个完全由我自己制造的、语法正确但语义荒谬的报错。

“丢失”这个词其实不准确。xinuput 没丢,它只是被一个错误的门描述符挡在了门外。计算机不会丢东西,它只会忠实地执行你写错的指令。

修好之后,我盯着那行消失的报错想了很久。我们总说“计算机丢失了数据”,好像机器有个口袋,东西会从破洞里漏出去。但真相往往是:你给了它一个错误的地址,它跳过去,摔死了。xinuput 1 没丢,是我把它推下了悬崖。现在键盘敲起来很顺,但我偶尔会故意快速连按,看串口还会不会吐出那个 K。它每次都吐。稳得很。

那个玩具内核还在跑,xinuput 模块被我重写了一遍,中断门设置改成了宏,再也不会手滑。只是每次看到 0x8E 这个数字,我都会想起那个凌晨三点,屏幕上孤零零的中文报错,和一杯凉透的咖啡。计算机不会丢东西,丢东西的永远是人。

```

    热门排行

    友情链接