从零写OS(五):中断,内核的神经系统
键盘按下一个键,CPU 是怎么知道的? 不是轮询——CPU 不会没事一直问"键盘有没有按键"。而是靠中断(Interrupt,硬件或软件触发的信号,让 CPU 暂停当前任务去处理紧急事件)。发生了什么事,硬件主动通知 CPU,CPU 暂停手头的事,跳去处理,处理完再回来。 这一章要让内核有能力响应中断。没有这个机制,内核什么都干不了。 中断的分类 x86 的 256 个中断号分三段: 范围 类型 例子 0~31 CPU 异常 除零、缺页、非法指令 32~47 硬件中断 时钟、键盘 48~255 软件中断 系统调用 CPU 异常是 CPU 自己触发的,比如访问了不该访问的内存,CPU 就抛一个缺页异常(Page Fault,访问未映射的虚拟地址时 CPU 触发的异常,操作系统据此做内存按需分配)。 硬件中断是外设触发的,经过 PIC(Programmable Interrupt Controller,可编程中断控制器,负责管理外设中断请求并转发给 CPU)发给 CPU。 IDT:中断的路由表 要处理中断,首先要告诉 CPU:每种中断发生时,去哪个函数处理? 这张路由表叫 IDT(Interrupt Descriptor Table,中断描述符表,256 项,每项对应一个中断号及其处理函数地址)。跟之前的 GDT 一样,是内存里的一张表,用 lidt 指令告诉 CPU 位置。 每个表项里存的是处理函数的地址,还有权限信息。CPU 一旦收到中断,就查这张表,跳到对应函数去执行。 中断处理的细节 CPU 进入中断处理函数之前,会自动把当前的执行状态压栈:rip(下一条指令地址)、cs、rflags、rsp、ss。处理完之后用 iretq(64 位中断返回指令)恢复状态,接着跑。 有一个坑:某些异常 CPU 会自动压入错误码(比如缺页异常),某些不会(比如除零)。为了让处理函数收到的栈结构一致,对没有错误码的异常,要手动压一个 0 占位。这件事用汇编宏做,每种异常一行,干净利落。 第一个测试:故意除以零 IDT 装好之后,最简单的验证方式是故意触发一个异常,看内核能不能捕获到: ...