Interrupts
“Interrupts 中断” 是从设备 (例如键盘或硬盘驱动器) 到CPU的信号,告诉CPU立即停止当前正在执行的操作并执行其他操作。 例如,键盘控制器可以在按下字符键时发送中断。 然后,操作系统可以立即在屏幕上显示该字符,即使CPU之前正在做完全不相关的事情,然后CPU可以返回到之后要做的事情。 当出现特定中断时,CPU会从操作系统提供的表中查找该特定中断的条目。 在 x86保护模式 中,该表称为 中断描述符表 (IDT),最多可以有256个条目,但是该表的名称和它可以拥有的最大条目数可能会因CPU架构而异。 CPU找到中断的条目后,跳转到条目指向的代码。 响应中断而运行的此代码称为 中断服务例程 (ISR) 或中断处理程序。
中断的类型
在大多数平台上,通常有三类中断:
- Exceptions: 这些是由CPU内部生成的,用于向正在运行的内核发出需要其注意的事件或情况的警报。 在x86 cpu上,这些包括双重故障(Double Fault)、页面故障(Page Fault)、通用保护故障(General Protection)等异常条件。
- Interrupt Request (IRQ) or Hardware Interrupt: 这种类型的中断由芯片组外部产生,并通过锁存到所讨论的CPU的 #INTR引脚或等效信号来发出信号。 现今常用的IRQ有两种。
- IRQ Lines, or Pin-based IRQs: 这些通常在芯片组上静态路由。 电线或线路从芯片组上的设备运行到 IRQ controller,该控制器将设备发送的中断请求串行化,并将它们逐个发送到CPU以防止竞争。 在许多情况下,IRQ控制器会根据设备的优先级一次向CPU发送多个IRQ。 一个非常著名的IRQ控制器的例子是 英特尔8259 控制器链,它存在于所有IBM-PC兼容的芯片组上,将两个控制器链接在一起,每个控制器提供8个输入引脚,用于传统IBM-PC上总共16个可用的IRQ信号引脚。
- Message Signaled Interrupts: 这些通过将值写入为关于中断设备、中断本身和矢量化信息的信息保留存储器位置来发出信号。 通过固件或内核软件为设备分配了写入的位置。 然后,设备使用特定于设备总线的仲裁协议生成IRQ。 提供基于消息的中断功能的总线的一个示例是PCI总线。
- Software Interrupt: 这是运行在CPU上的软件发出的中断信号,表明它需要内核的注意。 这些类型的中断一般用于 System Calls。 在x86 CPU上,用于启动软件中断的指令是 “INT” 指令。 由于x86 CPU可以使用256可用的中断向量中的任何一个来进行软件中断,所以内核通常选择一个。 例如,许多当代的Unices在基于x86的平台上使用vector 0x80。
作为一项规则,当一个CPU给开发人员自由选择哪些向量用于什么 (如在x86上),一个人应该避免在同一向量上有不同类型的中断。 通常的做法是,按照英特尔的要求,将前32个向量留给Exceptions。 但是,你对其余向量的划分取决于你。
从键盘的角度来看
基本上,当按键被按下时,键盘控制器会告诉称为 可编程中断控制器 或PIC的设备发起中断。 由于键盘和PIC的接线,IRQ #1是键盘中断,因此当按下一个键时,IRQ 1被发送到PIC。 PIC的作用是决定是否应立即通知CPU该IRQ,并将IRQ数转换为CPU表的 “中断向量” (即0和255之间的数字)。
操作系统应该通过 in 和 out 指令 (或 inportb/outportb) 与键盘通话来处理中断,inportw/outportw,以及 inportd/outportd 在C中,见 Inline Assembly/Examples),询问按下了什么键,对此做一些事情 (例如在屏幕上显示键,并通知当前应用程序已按下某个键),并返回到中断进入时正在执行的任何代码。 实际上,无法从缓冲区读取键将阻止键盘的任何后续IRQ。
从PIC的角度来看
实际上,大多数系统上都有两个PIC,每个PIC都有8个不同的输入,外加一个输出信号,用于告诉CPU发生了IRQ。 “从PIC”的输出信号连接到“主PIC”的第三个输入 (输入 #2); 因此,当“从PIC”想要告诉CPU发生中断时,它实际上先告诉“主PIC”,而由主PIC告诉CPU。 这被称为 “级联”。主PIC的第三个输入是为此配置的,而不是配置为普通的IRQ,这意味着IRQ 2不能发生。
设备向PIC芯片发送中断,PIC告诉CPU发生中断 (直接或间接)。 当CPU确认 “中断发生” 信号时,PIC芯片向CPU发送中断编号 (在00h和FFh之间,或十进制0和255之间)。 系统首次启动时,IRQ 0至7设置为中断08h至0Fh,IRQ 8至15设置为中断70h至77h。 因此,对于IRQ 6,PIC将告诉CPU服务INT 0Eh,该服务大概具有用于与连接到主PIC芯片的 “输入 #6” 的任何设备进行交互的代码。 当然,当两个或多个设备共享一个IRQ时,可能会遇到麻烦; 如果你想知道这是如何工作的,请查看 Plug and Play。 注意,中断按优先级处理: 0、1、2、8、9、10、11、12、13、14、15、3、4、5、6、7。 因此,如果IRQ 8和IRQ 3同时进来,则IRQ 8被发送到CPU。 当CPU处理完中断后,它告诉PIC可以继续发送中断:
mov al,20h
out 20h,al
或者如果中断来自从属PIC:
mov al, 20h
out A0h, al
out 20h, al
PIC发送分配给IRQ 3的中断,CPU处理该中断 (使用IDT查找该中断的处理程序)。
告警读取器会注意到CPU保留了中断0-31,但IRQs 0-7设置为中断08-0fh。 现在,当操作系统必须处理的可怕错误发生时,将调用保留的中断。 当计算机首次启动时,大多数此类错误都不会发生。 但是,当你进入保护模式 (并且每个操作系统都应使用保护模式,真实模式已过时) 时,这些错误随时可能发生,并且操作系统需要能够处理它们。 操作系统将如何区分INT 9,异常: 协处理器段超限和INT 9: IRQ 1之间的区别? 嗯,它可以问设备是否真的有中断。 但这是缓慢的,骇人听闻的,并不是所有的设备都能够做这种事情。 最好的方法是告诉PIC将IRQ映射到 “不同的” 中断,例如INT 78h-7Fh。 有关此信息,请参阅 PIC 常见问题。 请注意,IRQ只能映射到08h: 00h-07h,08h-0Fh,10h-17h,17h-1Fh的倍数的整数。 而且你最好使用20h-27h或更高,因为CPU保留了00h-1Fh。 此外,每个PIC都必须单独编程。 你可以告诉主PIC将IRQs 0-7映射到INTs 20h-27h,但是IRQs 8-F仍然是INTs 70h-77h,除非你告诉从PIC也把它们放在其他地方。
有关详细信息,请参阅 对PIC芯片进行编程。
从CPU的角度来看
Every time the CPU is done with one machine instruction, it will check if the PIC's pin has notified an interrupt. If that's the case, it stores some state information on the stack (so that it can return to whatever it is doing currently, when the INT is done being serviced by the OS) and jumps to a location pointed to by the IDT. The OS takes over from there. The current program can, however, prevent the CPU from being disturbed by interrupts by means of the interrupt flag (IF in status register). As long as this flag is cleared, the CPU ignores the PIC's requests and continues running the current program. Assembly instructions cli and sti can control that flag.
从操作系统的角度来看
当出现中断时,IDT (由操作系统预先设置) 用于跳转到操作系统的代码部分,该部分处理中断 (因此称为 “中断处理程序” 或 “中断服务例程”)。 通常,代码与设备交互,然后返回到它以前使用 iret 指令 (该指令告诉CPU从堆栈中加载其保存的状态信息)。 在 ret 之前,执行此代码,以告诉PIC可以发送任何新的或挂起的中断,因为当前中断已完成。 在cpu确认中断之前,PIC不会再发送任何中断:
mov al,20h
out 20h,al
在 键盘输入 的情况下,中断处理程序询问键盘按下了哪个键,对信息执行某些操作,然后确认并返回:
push eax ;; make sure you don't damage current state
in al,60h ;; read information from the keyboard
mov al,20h
out 20h,al ;; acknowledge the interrupt to the PIC
pop eax ;; restore state
iret ;; return to code executed before.
无论CPU以前做什么,然后需要重新恢复 (除非PIC在服务这个PIC时接收到另一个INT,在这种情况下,PIC告诉CPU,并且CPU再次将状态信息保存在堆栈上后,就开始执行一个新的中断处理程序)。
那么我该如何编程这些东西 ?
一步一步来,现在你已经抓住了整个事情,知道要做什么:
- 为中断描述符表腾出空间
- 告诉CPU该空间在哪里 (请参阅 GDT Tutorial: lidt 的工作方式与 lgdt 的工作方式非常相同)
- 告诉PIC你不再要使用BIOS默认值 (请参阅 对PIC芯片进行编程)
- 为IRQ和异常编写几个ISR处理程序 (请参阅 中断服务例程)
- 将ISR处理程序的地址放在适当的描述符中 (在 中断描述符表 中)
- 在 (PIC的) IRQ掩码中启用所有支持的中断
通用IBM-PC兼容中断信息
标准 ISA IRQs
IRQ | Description |
---|---|
0 | Programmable Interrupt Timer Interrupt |
1 | Keyboard Interrupt |
2 | Cascade (used internally by the two PICs. never raised) |
3 | COM2 (if enabled) |
4 | COM1 (if enabled) |
5 | LPT2 (if enabled) |
6 | Floppy Disk |
7 | LPT1 / Unreliable "spurious" interrupt (usually) |
8 | CMOS real-time clock (if enabled) |
9 | Free for peripherals / legacy SCSI / NIC |
10 | Free for peripherals / SCSI / NIC |
11 | Free for peripherals / SCSI / NIC |
12 | PS2 Mouse |
13 | FPU / Coprocessor / Inter-processor |
14 | Primary ATA Hard Disk |
15 | Secondary ATA Hard Disk |
默认PC中断向量分配
Int | Description |
---|---|
0-31 | Protected Mode Exceptions (Reserved by Intel) |
8-15 | Default mapping of IRQ0-7 by the BIOS at bootstrap |
70h-78h | Default mapping of IRQ8-15 by the BIOS at bootstrap |
Ports
Port | Description |
---|---|
20h & 21h | control/mask ports of the master PIC |
A0h & A1h | control/mask ports of the slave PIC |
60h | data port from the keyboard controller |
64h | command port for keyboard controller - use to enable/disable kbd interrupts, etc. |
S390中断
根据指令的上下文识别S390体系结构上的异常。
外部中断
当CPU计时器或外部设备触发所述中断时,产生外部中断。
Supervisor Call Interrupt
主管调用生成一个中断,该中断将当前的 PSW 放入 [[[PSA Mapping#FLCSOPSW|FLCSOPSW]] (服务旧PSW)。 然后从 FLCSNPSW (服务新PSW) 加载新的PSW。 SVC代码的位置放在 FLCESICODE
输入/输出中断
当设备完成命令程序的执行时,通常会触发此中断。
程序检查异常
当访问无效地址或执行无效指令时,将识别程序异常。
规格例外
通常在指令的给定参数或寄存器无效时产生。
机器检查异常
当部分设备报告故障时产生。 通常意味着所述设备应该被更换,这取决于用户。