“Interrupts”的版本间差异

来自osdev
跳到导航 跳到搜索
第1行: 第1行:
“Interrupts 中断” 是从设备 (例如键盘或硬盘驱动器) 到CPU的信号,告诉CPU立即停止当前正在执行的操作并执行其他操作。 例如,键盘控制器可以在按下字符键时发送中断。 然后,操作系统可以立即在屏幕上显示该字符,即使CPU之前正在做完全不相关的事情,然后CPU可以返回到之后要做的事情。 当出现特定中断时,CPU会从操作系统提供的表中查找该特定中断的条目。 在 [[Protected Mode| x86保护模式]] 中,该表称为 [[IDT|中断描述符表 (IDT)]],最多可以有256个条目,但是该表的名称和它可以拥有的最大条目数可能会因CPU架构而异。 CPU找到中断的条目后,跳转到条目指向的代码。 响应中断而运行的此代码称为 [[Interrupt Service Routines| 中断服务例程 (ISR) 或中断处理程序]]。
“Interrupts 中断” 是从设备 (例如键盘或硬盘驱动器) 到CPU的信号,告诉CPU应当立即停止当前正在执行的操作并执行其他操作。 例如,键盘控制器可以在按下字符键时发送中断。 然后,操作系统可以立即在屏幕上显示该字符,即使CPU之前正在做完全不相关的事情,然后CPU可以再返回到之后要做的事情。 当出现特定中断时,CPU会从操作系统提供的表中查找该特定中断的条目。 在 [[Protected Mode|x86保护模式]] 中,该表称为 [[IDT|中断描述符表 (Interrupt Descriptor Table)]],最多可以有256个条目(Entry),但是该表的名称和它可以拥有的最大条目数可能会因CPU架构而异。 CPU找到中断的条目后,跳转到条目指向的代码。 响应中断而运行的此代码称为 [[Interrupt Service Routines| 中断服务例程 (ISR) 或中断处理程序]]。


== 中断的类型 ==
== 中断的类型 ==
第6行: 第6行:
* '''[[Exceptions]]''': 这些是由CPU内部生成的,用于向正在运行的内核发出需要其注意的事件或情况的警报。 在x86 cpu上,这些包括双重故障(Double Fault)、页面故障(Page Fault)、通用保护故障(General Protection)等异常条件。
* '''[[Exceptions]]''': 这些是由CPU内部生成的,用于向正在运行的内核发出需要其注意的事件或情况的警报。 在x86 cpu上,这些包括双重故障(Double Fault)、页面故障(Page Fault)、通用保护故障(General Protection)等异常条件。


* '''Interrupt Request (IRQ) or Hardware Interrupt''': 这种类型的中断由芯片组外部产生,并通过锁存到所讨论的CPU的 #INTR引脚或等效信号来发出信号。 现今常用的IRQ有两种。
* '''Interrupt Request (IRQ) Hardware Interrupt''': 这种类型的中断由芯片组外部产生,并通过锁存到目前CPU的#INTR引脚或等效信号来发出信号。 现今常用的IRQ有两种。
** ''IRQ Lines, or Pin-based IRQs'': 这些通常在芯片组上静态路由。 电线或线路从芯片组上的设备运行到 [[IRQ controller]],该控制器将设备发送的中断请求串行化,并将它们逐个发送到CPU以防止竞争。 在许多情况下,IRQ控制器会根据设备的优先级一次向CPU发送多个IRQ。 一个非常著名的IRQ控制器的例子是 [[8259 PIC|英特尔8259]] 控制器链,它存在于所有IBM-PC兼容的芯片组上,将两个控制器链接在一起,每个控制器提供8个输入引脚,用于传统IBM-PC上总共16个可用的IRQ信号引脚。
** ''IRQ Lines, Pin-based IRQs'': 这些通常在芯片组上静态路由。 设备上的芯片通过连线或电路将信号发送到[[IRQ controller|IRQ控制器]],该控制器将设备发送的中断请求串行化,并将它们逐个发送到CPU以防止信号冲突。 在许多情况下,IRQ控制器会根据设备的优先级一次向CPU发送多个IRQ。 一个非常著名的IRQ控制器的例子是 [[8259 PIC|Intel 8259]] 控制器链,它存在于所有IBM-PC兼容的芯片组上,将两个控制器链接在一起,每个控制器提供8个输入引脚,用于传统IBM-PC上总共16个可用的IRQ信号引脚。
** ''[[Message Signaled Interrupts]]'': 这些通过将值写入为关于中断设备、中断本身和矢量化信息的信息保留存储器位置来发出信号。 通过固件或内核软件为设备分配了写入的位置。 然后,设备使用特定于设备总线的仲裁协议生成IRQ。 提供基于消息的中断功能的总线的一个示例是PCI总线。
** ''[[Message Signaled Interrupts]]'': 消息信号中断通过将值写入为关于中断设备、中断本身和向量信息的保留存储器位置来直接发出信号。(译者注:本文的“向量-vector”愿意指的是某些特殊作用的列表中的独立值,例如这里是一系列中断(编号)中的一个) 设备被通过固件或内核软件分配了写入以上值的位置。 然后,设备使用特定于设备总线的仲裁协议生成IRQ。 PCI总线也是一种提供基于消息的中断功能的总线。


* '''Software Interrupt''': 这是运行在CPU上的软件发出的中断信号,表明它需要内核的注意。 这些类型的中断一般用于 [[System Calls]]。 在x86 CPU上,用于启动软件中断的指令是 “INT” 指令。 由于x86 CPU可以使用256可用的中断向量中的任何一个来进行软件中断,所以内核通常选择一个。 例如,许多当代的Unices在基于x86的平台上使用vector 0x80。
* '''Software Interrupt''': 这是运行在CPU上的软件发出的中断信号,一般表明软件需要内核做出某些响应。 这些类型的中断一般用于 [[System Calls|系统调用]]。 在x86 CPU上,用于启动软件中断的指令是 “INT” 指令。 由于x86 CPU可以使用256可用的中断向量中的任何一个来进行软件中断,所以内核通常需要先从中选择一个。 例如,许多当代的Unix衍生系统在基于x86的平台上使用向量 0x80。


作为一项规则,当一个CPU给开发人员自由选择哪些向量用于什么 (如在x86上),一个人应该避免在同一向量上有不同类型的中断。 通常的做法是,按照英特尔的要求,将前32个向量留给Exceptions。 但是,你对其余向量的划分取决于你。
作为一项规则,当一个CPU给开发人员自由选择哪些向量用于什么 (如在x86上),应该避免在同一向量上有不同类型的中断。 通常的做法是,按照Intel的要求,将前32个向量留给Exceptions。 但是,你对其余向量的划分取决于你。


== 从键盘的角度来看 ==
== 从键盘的角度来看 ==
基本上,当按键被按下时,键盘控制器会告诉称为 [[PIC|可编程中断控制器]] 或PIC的设备发起中断。 由于键盘和PIC的接线,IRQ #1是键盘中断,因此当按下一个键时,IRQ 1被发送到PIC。 PIC的作用是决定是否应立即通知CPU该IRQ,并将IRQ数转换为CPU表的 “中断向量” (即0和255之间的数字)。
基本上,当按键被按下时,键盘控制器会告诉被称为 [[PIC|可编程中断控制器(PIC)]] 的设备发起中断。 由于键盘和PIC的固有接线方式,IRQ #1是键盘中断,因此当按下一个键时,IRQ 1被发送到PIC。 PIC的作用是决定是否应立即通知CPU该IRQ,并将IRQ数转换为CPU表的 “中断向量” (即0和255之间的数字)。


操作系统应该通过 <tt>in</tt> 和 <tt>out</tt> 指令 (或 <tt>inportb</tt>/<tt>outportb</tt>) 与键盘通话来处理中断,<tt>inportw</tt>/<tt>outportw</tt>,以及 <tt>inportd</tt>/<tt>outportd</tt> 在C中,见 [[Inline Assembly/Examples]]),询问按下了什么键,对此做一些事情 (例如在屏幕上显示键,并通知当前应用程序已按下某个键),并返回到中断进入时正在执行的任何代码。 实际上,无法从缓冲区读取键将阻止键盘的任何后续IRQ。
操作系统应该通过 <tt>in</tt> 和 <tt>out</tt> 指令 (或 <tt>inportb</tt>/<tt>outportb</tt>) 与键盘通话来处理中断,<tt>inportw</tt>/<tt>outportw</tt>,以及 <tt>inportd</tt>/<tt>outportd</tt> 在C中,见 [[Inline Assembly/Examples|内联汇编示例]]),询问按下了什么键,再对此做一些工作 (例如在屏幕上显示键,并通知当前应用程序已按下某个键),然后返回到中断进入前正在执行的代码。 实际上,如果无法从缓冲区读取键将阻止键盘的任何后续IRQ。


== 从PIC的角度来看 ==
== 从PIC的角度来看 ==
实际上,大多数系统上都有两个PIC,每个PIC都有8个不同的输入,外加一个输出信号,用于告诉CPU发生了IRQ。 “从PIC”的输出信号连接到“主PIC”的第三个输入 (输入 #2); 因此,当“从PIC”想要告诉CPU发生中断时,它实际上先告诉“主PIC”,而由主PIC告诉CPU。 这被称为 “级联”。主PIC的第三个输入是为此配置的,而不是配置为普通的IRQ,这意味着IRQ 2不能发生。
实际上,大多数系统上都有两个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时,可能会遇到麻烦; 如果你想知道这是如何工作的,请查看 [[Where Can I Find Info On PNP|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可以继续发送中断:
设备向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时,可能会遇到麻烦; 如果你想知道这是如何工作的,请查看 [[Where Can I Find Info On PNP|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可以继续发送中断:


<source lang="asm">
<source lang="asm">
第38行: 第38行:
PIC发送分配给IRQ 3的中断,CPU处理该中断 (使用IDT查找该中断的处理程序)。
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也把它们放在其他地方。
警觉的读者可能会注意到CPU保留了中断0-31,但IRQs 0-7设置为中断08-0fh。 现在,当操作系统必须处理的可怕错误发生时,CPU将调用保留的中断。 当计算机首次启动时,大多数此类错误都不会发生。 但是,当你进入保护模式 (并且每个操作系统都应使用保护模式,实模式已过时) 时,这些错误随时可能发生,并且操作系统需要能够处理它们。 操作系统将如何区分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#Programming the PIC chips| 对PIC芯片进行编程]]。
有关详细信息,请参阅 [[PIC#对PIC芯片进行编程|对PIC芯片进行编程]]。


== 从CPU的角度来看 ==
== 从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.
每次CPU执行一条机器指令时,它都会检查PIC的引脚是否已通知中断。 如果是这种情况,它会在堆栈上存储一些当前状态信息(这样,当操作系统完成对INT中断的服务时,它就可以返回到当前正在做的任何事情),并跳转到IDT所指向的位置。 操作系统从这里开始接管工作。
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 <tt>cli</tt> and <tt>sti</tt> can control that flag.
同时,当前程序可以通过“中断标志”(IF状态寄存器)来防止CPU再受到中断的干扰。 只要清除该标志,CPU就会忽略PIC的请求,持续运行当前程序。 汇编指令 <tt>cli</tt> and <tt>sti</tt> 可以控制该标志。


== 从操作系统的角度来看 ==
== 从操作系统的角度来看 ==
当出现中断时,[[IDT]] (由操作系统预先设置) 用于跳转到操作系统的代码部分,该部分处理中断 (因此称为 “中断处理程序” 或 “[[Interrupt Service Routines|中断服务例程]]”)。 通常,代码与设备交互,然后返回到它以前使用 <tt>iret</tt> 指令 (该指令告诉CPU从堆栈中加载其保存的状态信息)。 <tt>ret</tt> 之前,执行此代码,以告诉PIC可以发送任何新的或挂起的中断,因为当前中断已完成。 在cpu确认中断之前,PIC不会再发送任何中断:
当出现中断时,[[IDT]] (由操作系统预先设置) 用于跳转到操作系统的相关代码部分,该部分处理中断 (因此称为 “中断处理程序” 或 “[[Interrupt Service Routines|中断服务例程]]”)。 通常,操作系统代码开始与设备交互,完成后通过使用 <tt>iret</tt> 指令返回到它以前的工作, (该指令告诉CPU从堆栈中加载其保存的状态信息)。 在上面 <tt>ret</tt> 之前,需要执行以下代码,以告诉PIC可以发送任何新的或挂起的中断,因为当前中断已完成。(译者注:以下代码是给8259a芯片发送EOI命令,通知其一个中断已完成) 在CPU确认中断之前,PIC是不会再发送任何中断的:


<source lang="asm">
<source lang="asm">
第55行: 第55行:
</source>
</source>


在 [[PS/2_Keyboard|键盘输入]] 的情况下,中断处理程序询问键盘按下了哪个键,对信息执行某些操作,然后确认并返回:
在 [[PS/2_Keyboard|键盘输入]] 的情况下,中断处理程序会询问键盘按下了哪个键,对信息执行某些操作,然后确认并返回:


<source lang="asm">
<source lang="asm">
push eax    ;; make sure you don't damage current state
push eax    ;; 确保不损坏当前状态
in al,60h  ;; read information from the keyboard
in al,60h  ;; 从键盘读取信息


mov al,20h
mov al,20h
out 20h,al  ;; acknowledge the interrupt to the PIC
out 20h,al  ;; 确认对PIC的中断
pop eax    ;; restore state
pop eax    ;; 恢复状态
iret        ;; return to code executed before.
iret        ;; 返回到以前执行的代码。
</source>
</source>


无论CPU以前做什么,然后需要重新恢复 (除非PIC在服务这个PIC时接收到另一个INT,在这种情况下,PIC告诉CPU,并且CPU再次将状态信息保存在堆栈上后,就开始执行一个新的中断处理程序)。
无论CPU以前做什么,然后都需要重新恢复 (除非PIC在服务这个PIC时接收到另一个INT,在这种情况下,PIC会告诉CPU,并且CPU再次将状态信息保存在堆栈上后,就开始执行一个新的中断处理程序)。


== 那么我该如何编程这些东西 ?==
== 那么我该如何编程实现这些 ?==


一步一步来,现在你已经抓住了整个事情,知道要做什么:
一步一步来,现在你已经抓住了整个事情的脉络,知道要做什么:
* 为中断描述符表腾出空间
* 为中断描述符表(IDT)腾出空间
* 告诉CPU该空间在哪里 (请参阅 [[GDT Tutorial]]: <tt>lidt</tt> 的工作方式与 <tt>lgdt</tt> 的工作方式非常相同)
* 告诉CPU该空间在哪里 (请参阅 [[GDT Tutorial|GDT教程]]: <tt>lidt</tt> 的工作方式与 <tt>lgdt</tt> 的工作方式非常相同)
* 告诉PIC你不再要使用BIOS默认值 (请参阅 [[PIC#Programming the PIC chips| 对PIC芯片进行编程]])
* 告诉PIC你不再要使用BIOS默认值 (请参阅 [[PIC# 对PIC芯片进行编程| 对PIC芯片进行编程]])
* 为IRQ和异常编写几个ISR处理程序 (请参阅 [[Interrupt Service Routines|中断服务例程]])
* 为IRQ和异常编写几个ISR处理程序 (请参阅 [[Interrupt Service Routines|中断服务例程]])
* 将ISR处理程序的地址放在适当的描述符中 (在 [[Interrupt Descriptor Table|中断描述符表]] 中)
* 将ISR处理程序的地址放在适当的描述符中 (在 [[Interrupt Descriptor Table|中断描述符表]] 中)
第80行: 第80行:


== 通用IBM-PC兼容中断信息 ==
== 通用IBM-PC兼容中断信息 ==
=== 标准 [[ISA]] IRQs ===
=== 标准 [[ISA]] 中断请求 ===
{| {{wikitable}}
{| {{wikitable}}
|-
|-
! IRQ !! Description
! IRQ !! 描述
|-
|-
| 0 || Programmable Interrupt Timer Interrupt
| 0 || 可编程中断定时器中断
|-
|-
| 1 || Keyboard Interrupt
| 1 ||键盘中断
|-
|-
| 2 || Cascade (used internally by the two PICs. never raised)
| 2 || 级联 (两个芯片内部使用。不会发起)
|-
|-
| 3 || COM2 (if enabled)
| 3 || 串口COM2 (如果开启)
|-
|-
| 4 || COM1 (if enabled)
| 4 || 串口COM1 (如果启用)
|-
|-
| 5 || LPT2 (if enabled)
| 5 || 并口LPT2(如果启用)
|-
|-
| 6 || Floppy Disk
| 6 || 软盘
|-
|-
| 7 || LPT1 / Unreliable [[8259_PIC#Spurious_IRQs|"spurious" interrupt]] (usually)
| 7 || 并口LPT1 / 通常是[[8259_PIC#Spurious_IRQs|“伪”中断]](不可靠)
|-
|-
| 8 || CMOS real-time clock (if enabled)
| 8 || CMOS实时时钟(如果开启)
|-
|-
| 9 || Free for peripherals / legacy SCSI / NIC
| 9 || 自由外设/legacy SCSI/网卡
|-
|-
| 10 || Free for peripherals / SCSI / NIC
| 10 || 自由外设/SCSI/网卡
|-
|-
| 11 || Free for peripherals / SCSI / NIC
| 11 || 自由外设/SCSI/网卡
|-
|-
| 12 || PS2 Mouse
| 12 || PS2鼠标
|-
|-
| 13 || FPU / Coprocessor / Inter-processor
| 13 || FPU/协处理器/Inter-processor
|-
|-
| 14 || Primary ATA Hard Disk
| 14 || 主ATA硬盘
|-
|-
| 15 || Secondary ATA Hard Disk
| 15 || 辅ATA硬盘
|}
|}


第121行: 第121行:
{| {{wikitable}}
{| {{wikitable}}
|-
|-
! Int !! Description
! Int !! 描述
|-
|-
| 0-31 || [[Protected Mode]] Exceptions (Reserved by Intel)
| 0-31 || [[Protected Mode|保护模式]]异常(Intel保留)
|-
|-
| 8-15 || Default mapping of IRQ0-7 by the BIOS at bootstrap
| 8-15 || 引导程序中BIOS对IRQ0-7的默认映射
|-
|-
| 70h-78h || Default mapping of IRQ8-15 by the BIOS at bootstrap
| 70h-78h || 引导时BIOS对IRQ8-15的默认映射
|}
|}


第133行: 第133行:
{| {{wikitable}}
{| {{wikitable}}
|-
|-
! Port !! Description
!Port !! 描述
|-
|-
| 20h & 21h || control/mask ports of the master PIC
| 20h & 21h || 主PIC的控制/屏蔽端口
|-
|-
| A0h & A1h || control/mask ports of the slave PIC
| A0h & A1h || 从PIC的控制/屏蔽端口
|-
|-
| 60h || data port from the keyboard controller
| 60h || 键盘控制器的数据端口
|-
|-
| 64h || command port for keyboard controller - use to enable/disable kbd interrupts, etc.
| 64h || 键盘控制器的命令端口-用于启用/禁用kbd中断等。
|-
|-
|}
|}


== S390中断 ==
== S390中断 ==
根据指令的上下文识别S390体系结构上的异常。
根据指令上下文识别S390体系结构上的异常。


=== 外部中断 ===
=== 外部中断(External Interrupt) ===
当CPU计时器或外部设备触发所述中断时,产生外部中断。
当CPU计时器或外部设备触发所述中断时,产生外部中断。


=== Supervisor Call Interrupt ===
===主管调用中断(Supervisor Call Interrupt)===
主管调用生成一个中断,该中断将当前的 [[PSW]] 放入 [[[PSA Mapping#FLCSOPSW|FLCSOPSW]] (服务旧PSW)。 然后从 [[PSA Mapping#FLCSNPSW|FLCSNPSW]] (服务新PSW) 加载新的PSW。 SVC代码的位置放在 [[PSA Mapping#FLCESICODE|FLCESICODE]]
主管调用生成一个中断,该中断将当前的 [[PSW]] 放入 [[PSA Mapping#FLCSOPSW|FLCSOPSW]] (服务旧PSW)。 然后从 [[PSA Mapping#FLCSNPSW|FLCSNPSW]] (服务新PSW) 加载新的PSW。 SVC代码的位置放在 [[PSA Mapping#FLCESICODE|FLCESICODE]]


=== 输入/输出中断 ===
=== 输入/输出中断(Input/Ouput Interrupt) ===
当设备完成命令程序的执行时,通常会触发此中断。
当设备完成命令程序的执行时,通常会触发此中断。


=== 程序检查异常 ===
=== 程序检查异常(Program Check Exception) ===
当访问无效地址或执行无效指令时,将识别程序异常。
当访问无效地址或执行无效指令时,将识别程序异常。


=== 规格例外 ===
=== 规格例外(Specification Exception) ===
通常在指令的给定参数或寄存器无效时产生。
通常在指令的给定参数或寄存器无效时产生。


=== 机器检查异常 ===
=== 机器检查异常(Machine Check Exception) ===
当部分设备报告故障时产生。 通常意味着所述设备应该被更换,这取决于用户。
当部分设备报告故障时产生。 通常意味着所述设备应该被更换,这取决于用户自己。


== 另见 ==
== 另见 ==

2022年3月8日 (二) 02:44的版本

“Interrupts 中断” 是从设备 (例如键盘或硬盘驱动器) 到CPU的信号,告诉CPU应当立即停止当前正在执行的操作并执行其他操作。 例如,键盘控制器可以在按下字符键时发送中断。 然后,操作系统可以立即在屏幕上显示该字符,即使CPU之前正在做完全不相关的事情,然后CPU可以再返回到之后要做的事情。 当出现特定中断时,CPU会从操作系统提供的表中查找该特定中断的条目。 在 x86保护模式 中,该表称为 中断描述符表 (Interrupt Descriptor Table),最多可以有256个条目(Entry),但是该表的名称和它可以拥有的最大条目数可能会因CPU架构而异。 CPU找到中断的条目后,跳转到条目指向的代码。 响应中断而运行的此代码称为 中断服务例程 (ISR) 或中断处理程序

中断的类型

在大多数平台上,通常有三类中断:

  • Exceptions: 这些是由CPU内部生成的,用于向正在运行的内核发出需要其注意的事件或情况的警报。 在x86 cpu上,这些包括双重故障(Double Fault)、页面故障(Page Fault)、通用保护故障(General Protection)等异常条件。
  • Interrupt Request (IRQ) 或 Hardware Interrupt: 这种类型的中断由芯片组外部产生,并通过锁存到目前CPU的#INTR引脚或等效信号来发出信号。 现今常用的IRQ有两种。
    • IRQ Lines, 或 Pin-based IRQs: 这些通常在芯片组上静态路由。 设备上的芯片通过连线或电路将信号发送到IRQ控制器,该控制器将设备发送的中断请求串行化,并将它们逐个发送到CPU以防止信号冲突。 在许多情况下,IRQ控制器会根据设备的优先级一次向CPU发送多个IRQ。 一个非常著名的IRQ控制器的例子是 Intel 8259 控制器链,它存在于所有IBM-PC兼容的芯片组上,将两个控制器链接在一起,每个控制器提供8个输入引脚,用于传统IBM-PC上总共16个可用的IRQ信号引脚。
    • Message Signaled Interrupts: 消息信号中断通过将值写入为关于中断设备、中断本身和向量信息的保留存储器位置来直接发出信号。(译者注:本文的“向量-vector”愿意指的是某些特殊作用的列表中的独立值,例如这里是一系列中断(编号)中的一个) 设备被通过固件或内核软件分配了写入以上值的位置。 然后,设备使用特定于设备总线的仲裁协议生成IRQ。 PCI总线也是一种提供基于消息的中断功能的总线。
  • Software Interrupt: 这是运行在CPU上的软件发出的中断信号,一般表明软件需要内核做出某些响应。 这些类型的中断一般用于 系统调用。 在x86 CPU上,用于启动软件中断的指令是 “INT” 指令。 由于x86 CPU可以使用256可用的中断向量中的任何一个来进行软件中断,所以内核通常需要先从中选择一个。 例如,许多当代的Unix衍生系统在基于x86的平台上使用向量 0x80。

作为一项规则,当一个CPU给开发人员自由选择哪些向量用于什么 (如在x86上),应该避免在同一向量上有不同类型的中断。 通常的做法是,按照Intel的要求,将前32个向量留给Exceptions。 但是,你对其余向量的划分取决于你。

从键盘的角度来看

基本上,当按键被按下时,键盘控制器会告诉被称为 可编程中断控制器(PIC) 的设备发起中断。 由于键盘和PIC的固有接线方式,IRQ #1是键盘中断,因此当按下一个键时,IRQ 1被发送到PIC。 PIC的作用是决定是否应立即通知CPU该IRQ,并将IRQ数转换为CPU表的 “中断向量” (即0和255之间的数字)。

操作系统应该通过 inout 指令 (或 inportb/outportb) 与键盘通话来处理中断,inportw/outportw,以及 inportd/outportd 在C中,见 内联汇编示例),询问按下了什么键,再对此做一些工作 (例如在屏幕上显示键,并通知当前应用程序已按下某个键),然后返回到中断进入前正在执行的代码。 实际上,如果无法从缓冲区读取键将阻止键盘的任何后续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。 现在,当操作系统必须处理的可怕错误发生时,CPU将调用保留的中断。 当计算机首次启动时,大多数此类错误都不会发生。 但是,当你进入保护模式 (并且每个操作系统都应使用保护模式,实模式已过时) 时,这些错误随时可能发生,并且操作系统需要能够处理它们。 操作系统将如何区分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的角度来看

每次CPU执行一条机器指令时,它都会检查PIC的引脚是否已通知中断。 如果是这种情况,它会在堆栈上存储一些当前状态信息(这样,当操作系统完成对INT中断的服务时,它就可以返回到当前正在做的任何事情),并跳转到IDT所指向的位置。 操作系统从这里开始接管工作。 同时,当前程序可以通过“中断标志”(IF状态寄存器)来防止CPU再受到中断的干扰。 只要清除该标志,CPU就会忽略PIC的请求,持续运行当前程序。 汇编指令 cli and sti 可以控制该标志。

从操作系统的角度来看

当出现中断时,IDT (由操作系统预先设置) 用于跳转到操作系统的相关代码部分,该部分处理中断 (因此称为 “中断处理程序” 或 “中断服务例程”)。 通常,操作系统代码开始与设备交互,完成后通过使用 iret 指令返回到它以前的工作, (该指令告诉CPU从堆栈中加载其保存的状态信息)。 在上面 ret 之前,需要执行以下代码,以告诉PIC可以发送任何新的或挂起的中断,因为当前中断已完成。(译者注:以下代码是给8259a芯片发送EOI命令,通知其一个中断已完成) 在CPU确认中断之前,PIC是不会再发送任何中断的:

mov al,20h
out 20h,al

键盘输入 的情况下,中断处理程序会询问键盘按下了哪个键,对信息执行某些操作,然后确认并返回:

push eax    ;; 确保不损坏当前状态
in al,60h   ;; 从键盘读取信息

mov al,20h
out 20h,al  ;; 确认对PIC的中断
pop eax     ;; 恢复状态
iret        ;; 返回到以前执行的代码。

无论CPU以前做什么,然后都需要重新恢复 (除非PIC在服务这个PIC时接收到另一个INT,在这种情况下,PIC会告诉CPU,并且CPU再次将状态信息保存在堆栈上后,就开始执行一个新的中断处理程序)。

那么我该如何编程实现这些 ?

一步一步来,现在你已经抓住了整个事情的脉络,知道要做什么:

  • 为中断描述符表(IDT)腾出空间
  • 告诉CPU该空间在哪里 (请参阅 GDT教程: lidt 的工作方式与 lgdt 的工作方式非常相同)
  • 告诉PIC你不再要使用BIOS默认值 (请参阅 对PIC芯片进行编程)
  • 为IRQ和异常编写几个ISR处理程序 (请参阅 中断服务例程)
  • 将ISR处理程序的地址放在适当的描述符中 (在 中断描述符表 中)
  • 在 (PIC的) IRQ掩码中启用所有支持的中断

通用IBM-PC兼容中断信息

标准 ISA 中断请求

IRQ 描述
0 可编程中断定时器中断
1 键盘中断
2 级联 (两个芯片内部使用。不会发起)
3 串口COM2 (如果开启)
4 串口COM1 (如果启用)
5 并口LPT2(如果启用)
6 软盘
7 并口LPT1 / 通常是“伪”中断(不可靠)
8 CMOS实时时钟(如果开启)
9 自由外设/legacy SCSI/网卡
10 自由外设/SCSI/网卡
11 自由外设/SCSI/网卡
12 PS2鼠标
13 FPU/协处理器/Inter-processor
14 主ATA硬盘
15 辅ATA硬盘

默认PC中断向量分配

Int 描述
0-31 保护模式异常(Intel保留)
8-15 引导程序中BIOS对IRQ0-7的默认映射
70h-78h 引导时BIOS对IRQ8-15的默认映射

Ports

Port 描述
20h & 21h 主PIC的控制/屏蔽端口
A0h & A1h 从PIC的控制/屏蔽端口
60h 键盘控制器的数据端口
64h 键盘控制器的命令端口-用于启用/禁用kbd中断等。

S390中断

根据指令上下文识别S390体系结构上的异常。

外部中断(External Interrupt)

当CPU计时器或外部设备触发所述中断时,产生外部中断。

主管调用中断(Supervisor Call Interrupt)

主管调用生成一个中断,该中断将当前的 PSW 放入 FLCSOPSW (服务旧PSW)。 然后从 FLCSNPSW (服务新PSW) 加载新的PSW。 SVC代码的位置放在 FLCESICODE

输入/输出中断(Input/Ouput Interrupt)

当设备完成命令程序的执行时,通常会触发此中断。

程序检查异常(Program Check Exception)

当访问无效地址或执行无效指令时,将识别程序异常。

规格例外(Specification Exception)

通常在指令的给定参数或寄存器无效时产生。

机器检查异常(Machine Check Exception)

当部分设备报告故障时产生。 通常意味着所述设备应该被更换,这取决于用户自己。

另见

文章

论坛主题

外部链接