查看“Interrupts”的源代码
←
Interrupts
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
“Interrupts 中断” 是从设备 (例如键盘或硬盘驱动器) 到CPU的信号,告诉CPU应当立即停止当前正在执行的操作并去执行其他操作。 例如,键盘控制器可以在按下字符键时发送中断。 然后,操作系统可以立即在屏幕上显示该字符,即使CPU之前正在做完全不相关的事情,然后CPU可以再返回到本来要做的事情。 当出现特定中断时,CPU会从操作系统提供的表中查找该特定中断的条目。 在 [[Protected Mode|x86保护模式]] 中,该表称为 [[IDT|中断描述符表 (Interrupt Descriptor Table)]],最多可以有256个条目(Entry),但是该表的名称和它可以拥有的最大条目数可能会因CPU架构而异。 CPU找到中断的条目后,跳转到条目指向的代码。 响应中断而运行的此代码称为 [[Interrupt Service Routines| 中断服务例程 (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 controller|IRQ控制器]],该控制器将设备发送的中断请求串行化,并将它们逐个发送到CPU以防止信号冲突。 在许多情况下,IRQ控制器会根据设备的优先级一次向CPU发送多个IRQ。 一个非常著名的IRQ控制器的例子是 [[8259 PIC|Intel 8259]] 控制器链,它存在于所有IBM-PC兼容的芯片组上,将两个控制器链接在一起,每个控制器提供8个输入引脚,用于传统IBM-PC上总共16个可用的IRQ信号引脚。 ** ''[[Message Signaled Interrupts]]'': 消息信号中断通过将值写入为关于中断设备、中断本身和向量信息的保留存储器位置来直接发出信号。(译者注:本文的“向量-vector”愿意指的是某些特殊作用的列表中的独立值,例如这里是一系列中断(编号)中的一个) 设备被通过固件或内核软件分配了写入以上值的位置。 然后,设备使用特定于设备总线的仲裁协议生成IRQ。 PCI总线也是一种提供基于消息的中断功能的总线。 * '''Software Interrupt''': 这是运行在CPU上的软件发出的中断信号,一般表明软件需要内核做出某些响应。 这些类型的中断一般用于 [[System Calls|系统调用]]。 在x86 CPU上,用于启动软件中断的指令是 “INT” 指令。 由于x86 CPU可以使用256可用的中断向量中的任何一个来进行软件中断,所以内核通常需要先从中选择一个。 例如,许多当代的Unix衍生系统在基于x86的平台上使用向量 0x80。 作为一项规则,当一个CPU给开发人员自由选择哪些向量用于什么 (如在x86上),应该避免在同一向量上有不同类型的中断。 通常的做法是,按照Intel的要求,将前32个向量留给Exceptions。 但是,你对其余向量的划分取决于你。 == 从键盘的角度来看 == 基本上,当按键被按下时,键盘控制器会告诉被称为 [[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>,以及在C中使用<tt>inportd</tt>/<tt>outportd</tt> ,见 [[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时,可能会遇到麻烦; 如果你想知道这是如何工作的,请查看 [[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"> mov al,20h out 20h,al </source> 或者如果中断来自从属PIC: <source lang="asm"> mov al, 20h out A0h, al out 20h, al </source> 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#对PIC芯片进行编程|对PIC芯片进行编程]]。 == 从CPU的角度来看 == 每次CPU执行一条机器指令时,它都会检查PIC的引脚是否已通知中断。 如果是这种情况,它会在堆栈上存储一些当前状态信息(这样,当操作系统完成对INT中断的服务时,它就可以返回到当前正在做的任何事情),并跳转到IDT所指向的位置。 操作系统从这里开始接管工作。 同时,当前程序可以通过“中断标志”(IF状态寄存器)来防止CPU再受到中断的干扰。 只要清除该标志,CPU就会忽略PIC的请求,持续运行当前程序。 汇编指令 <tt>cli</tt> 和 <tt>sti</tt> 可以控制该标志。 == 从操作系统的角度来看 == 当出现中断时,[[IDT]] (由操作系统预先设置) 用于跳转到操作系统的相关代码部分,该部分处理中断 (因此称为 “中断处理程序” 或 “[[Interrupt Service Routines|中断服务例程]]”)。 通常,操作系统代码开始与设备交互,完成后通过使用 <tt>iret</tt> 指令返回到它以前的工作, (该指令告诉CPU从堆栈中加载其保存的状态信息)。 在上面 <tt>ret</tt> 之前,需要执行以下代码,以告诉PIC可以发送任何新的或挂起的中断,因为当前中断已完成。(译者注:以下代码是给8259a芯片发送EOI命令,通知其一个中断已完成) 在CPU确认中断之前,PIC是不会再发送任何中断的: <source lang="asm"> mov al,20h out 20h,al </source> 在 [[PS/2_Keyboard|键盘输入]] 的情况下,中断处理程序会询问键盘按下了哪个键,对信息执行某些操作,然后确认并返回: <source lang="asm"> push eax ;; 确保不损坏当前状态 in al,60h ;; 从键盘读取信息 mov al,20h out 20h,al ;; 确认对PIC的中断 pop eax ;; 恢复状态 iret ;; 返回到以前执行的代码。 </source> 无论CPU以前做什么,然后都需要重新恢复 (除非PIC在服务这个PIC时接收到另一个INT,在这种情况下,PIC会告诉CPU,并且CPU再次将状态信息保存在堆栈上后,就开始执行一个新的中断处理程序)。 == 那么我该如何编程实现这些 ?== 一步一步来,现在你已经抓住了整个事情的脉络,知道要做什么: * 为中断描述符表(IDT)腾出空间 * 告诉CPU该空间在哪里 (请参阅 [[GDT Tutorial|GDT教程]]: <tt>lidt</tt> 的工作方式与 <tt>lgdt</tt> 的工作方式非常相同) * 告诉PIC你不再要使用BIOS默认值 (请参阅 [[PIC# 对PIC芯片进行编程| 对PIC芯片进行编程]]) * 为IRQ和异常编写几个ISR处理程序 (请参阅 [[Interrupt Service Routines|中断服务例程]]) * 将ISR处理程序的地址放在适当的描述符中 (在 [[Interrupt Descriptor Table|中断描述符表]] 中) * 在 (PIC的) IRQ掩码中启用所有支持的中断 == 通用IBM-PC兼容中断信息 == === 标准 [[ISA]] 中断请求 === {| {{wikitable}} |- ! IRQ !! 描述 |- | 0 || 可编程中断定时器中断 |- | 1 ||键盘中断 |- | 2 || 级联 (两个芯片内部使用。不会发起) |- | 3 || 串口COM2 (如果开启) |- | 4 || 串口COM1 (如果启用) |- | 5 || 并口LPT2(如果启用) |- | 6 || 软盘 |- | 7 || 并口LPT1 / 通常是[[8259_PIC#Spurious_IRQs|“伪”中断]](不可靠) |- | 8 || CMOS实时时钟(如果开启) |- | 9 || 自由外设/legacy SCSI/网卡 |- | 10 || 自由外设/SCSI/网卡 |- | 11 || 自由外设/SCSI/网卡 |- | 12 || PS2鼠标 |- | 13 || FPU/协处理器/Inter-processor |- | 14 || 主ATA硬盘 |- | 15 || 辅ATA硬盘 |} === 默认PC中断向量分配 === {| {{wikitable}} |- ! Int !! 描述 |- | 0-31 || [[Protected Mode|保护模式]]异常(Intel保留) |- | 8-15 || 引导程序中BIOS对IRQ0-7的默认映射 |- | 70h-78h || 引导时BIOS对IRQ8-15的默认映射 |} === Ports === {| {{wikitable}} |- !Port !! 描述 |- | 20h & 21h || 主PIC的控制/屏蔽端口 |- | A0h & A1h || 从PIC的控制/屏蔽端口 |- | 60h || 键盘控制器的数据端口 |- | 64h || 键盘控制器的命令端口-用于启用/禁用kbd中断等。 |- |} == S390中断 == 根据指令上下文识别S390体系结构上的异常。 === 外部中断(External Interrupt) === 当CPU计时器或外部设备触发所述中断时,产生外部中断。 ===主管调用中断(Supervisor Call Interrupt)=== 主管调用生成一个中断,该中断将当前的 [[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) === 当部分设备报告故障时产生。 通常意味着所述设备应该被更换,这取决于用户自己。 == 另见 == === 文章 === * [[Ralf Brown's Interrupt List]] * [[Interrupts tutorial]] === 论坛主题 === === 外部链接 === * [[wikipedia:Interrupts|Interrupts Wikipedia Article]] * [http://os-development.000webhostapp.com/prerusenia.html Tutorial for setting interrupts in slovak langunge(Návod v slovenskom jazyku) ] [[Category:Interrupts]]
本页使用的模板:
模板:Wikitable
(
查看源代码
)
返回至“
Interrupts
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
变体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息