汇编语言简易教程(14):中断与恢复
从一般意义上讲,中断是工作流的暂停或保持。 例如,如果您正在打电话,门铃响了,则电话通话将处于暂停状态,门将应答。 销售人员被送走后,电话交谈恢复(对话中断的地方)
在计算机编程中,中断也是当前正在执行的过程的暂停或保持。 通常,当前进程会中断,以便可以执行其他一些工作。 中断通常被定义为改变处理器执行的指令序列的事件。 此类事件对应于软件和/或硬件生成的信号。 例如,大多数输入/输出 (I/O) 设备都会生成中断以传输或接收数据。 软件程序还可以生成中断,以根据需要启动 I/O、请求操作系统服务或处理意外情况
处理中断是一项敏感任务。 中断可能随时发生;内核尝试尽快解决中断问题。 此外,一个中断可以被另一个中断中断
多用户操作系统
现代多用户操作系统 (OS) 支持多个程序同时执行或似乎正在执行,方法是根据需要共享资源。 操作系统负责管理和共享资源。 这些资源包括 CPU内核、主内存(即 RAM)、辅助存储(即磁盘或 SSD)、显示屏、键盘和鼠标。 例如,多个程序必须共享可用的 CPU 资源(内核或内核,如适用)
当中断发生时,当前进程被中断(即被搁置),中断被处理(这取决于中断的具体原因),然后最终恢复该进程。 操作系统可以选择在恢复原始进程之前执行其他任务或进程。 中断由称为中断服务例程(也称为中断处理程序、设备驱动程序等)的特殊软件例程处理。 通过使用中断并在各种进程之间快速切换,操作系统能够提供所有进程同时执行的错觉。
并非所有代码都可以中断。 例如,由于某些操作系统内核功能的性质,内核中的某些区域完全不能中断。 这包括更新一些敏感的操作系统数据结构。
异步中断
在计算机中断的上下文中,异步发生的中断意味着中断可以在程序执行的任意时间发生。
相对于执行过程中的任何特定位置,异步中断都是不可预测的。 例如,外部硬件设备可能会在不可预知的位置中断当前正在执行的进程。
同步中断
同步发生的中断通常发生在 CPU 控制下,并且由当前正在执行的进程引起或代表当前正在执行的进程。 同步性质与中断发生的位置有关,而不是特定的时钟时间或 CPU 周期时间。同步中断通常在同一位置重复发生(假设没有任何变化来解决原始原因
硬件中断
硬件中断通常由硬件生成。 硬件中断可以由以下方式发出
- IO设备 (键盘 / 网卡)
- 间隔计时器
- 其他 CPU(在多处理器系统上)
硬件中断是异步发生的。 硬件中断的一个示例是在键盘上键入键时。 操作系统无法提前知道何时按下该键,甚至是否按下该键。 为了处理这种情况,键盘支持硬件将生成中断。 如果操作系统正在执行不相关的程序,则在处理密钥时,该程序会暂时中断。 在此示例中,该特定处理包括将密钥存储在缓冲区中并返回到中断的进程。 理想情况下,这种短暂的中断对中断的进程影响不大
异常
异常是由当前进程引起并需要内核注意的中断的术语。 异常正在同步发生。 在这种情况下, synchronous 意味着异常将以可预测或可重复的方式发生, 通常分为以下:
- 故障 (faults)
一个例子是页面错误,它是将部分程序从磁盘存储加载到内存中的请求。 中断的进程将重新启动,而不会丢失连续性
- 陷阱 (Traps)
陷阱通常用于调试。 该过程将重新启动,而不会失去连续性。
- 中止 (Abort)
中止通常表示发生了严重的错误情况,必须加以处理。 这包括除以零、尝试访问无效的内存地址或尝试执行无效/非法指令。 非法指令可能是只允许由特权/授权进程执行的指令
软件中断
CPU 在处理指令时会产生软件中断。 这通常是程序员明确请求的编程异常。
此类中断通常是同步发生的,通常用于从操作系统请求系统服务。 例如,请求 I/O 等系统服务
中断类型及等级
中断具有与之关联的各种类型和权限。
以下各节提供了类型和权限的说明。 中断的进程可能以低于中断处理代码的权限执行。 为了使中断有效,操作系统必须安全、快速地处理此权限提升和降级。
类型区分
- 可屏蔽中断
- 不可屏蔽中断
可屏蔽中断通常由 I/O 设备发出。 顾名思义,“可屏蔽”意味着,可在短时间内忽略或屏蔽可屏蔽的中断。 这允许延迟关联的中断处理
不可屏蔽中断 (NMI)。 这包括一些操作系统功能和严重故障,例如硬件故障。 不可屏蔽的中断始终由 CPU 处理。
权限级别
权限级别是指执行中断代码的权限级别。 这可能是比中断的代码正在执行的更高的权限级别。 处理器以四个权限级别之一执行代码,如下所示
中断处理
处理中断时,必须安全、迅速且正确地进行处理。基本思想是,当当前正在执行的过程被中断时,必须将其暂停,并找到并执行相应的中断处理代码。所需的具体中断处理取决于中断的原因或目的。一旦服务了中断,最终将恢复原始过程的执行。
中断服务例程(ISR)
响应中断执行的代码通常被称为中断服务例程或ISR。这段代码有时被称为中断处理器、处理器、服务例程或ISR代码。为了一致性,本文档将使用ISR这个术语。
由于并发和竞态条件相关的问题,ISR代码的开发具有挑战性。此外,隔离问题和调试ISR代码也很困难。
处理步骤
处理中断涉及的一般步骤在以下各节中概述。
暂停
当前程序的执行被暂停。至少需要将rip和rFlags寄存器保存到系统栈。其余寄存器可能会被保留(作为进一步的步骤),这取决于具体的中断。rFlags标志寄存器必须立即保留,因为中断可能是异步生成的,且这些寄存器会随着连续指令的执行而改变。这个多阶段过程确保程序上下文可以完全恢复。
获取ISR地址
ISR地址存储在一个称为中断描述符表(IDT)的表中。对于每个ISR,IDT包含ISR地址和一些附加信息,包括ISR的任务门(优先级和权限信息)。IDT中的每个条目总共有8字节,每个IDT条目有16字节。IDT中最多可能有256(0-255)个条目。
要获取ISR的起始地址,中断号乘以16(因为每个条目是16字节),这个值用作IDT中的偏移量,从中获得ISR地址(对于该中断)。
IDT的起始位置由一个专用寄存器IDTR指向,该寄存器仅可由操作系统访问,并且需要0级权限才能访问。
ISR例程的地址特定于操作系统版本和系统的特定硬件组合。系统最初启动时创建IDT,并反映特定的系统配置。这对于操作系统能够在不同的系统硬件配置上正确且一致地工作至关重要。
跳转到ISR
从IDT获取ISR地址后,会执行一些验证。这包括确保中断来自合法/有效的源,以及是否需要和允许更改权限级别。一旦验证成功完成,ISR的地址从IDT放入rip寄存器,从而实现跳转到ISR例程。
暂停执行ISR
此时,根据特定ISR,可能会执行完整的进程上下文切换。进程上下文切换涉及保存中断进程的所有CPU寄存器。
在基于Linux的操作系统中,ISR通常分为两部分,称为上半部和下半部。其他操作系统将这些称为一级中断处理器(FLIH)和二级中断处理器(SLIH)。
上半部或FLIH立即执行,且在此进行任何关键活动。这些活动特定于ISR,但可能包括确认中断、重置硬件(如必要)以及记录只在中断时刻可用的任何信息。上半部可能会执行一些阻塞其他中断的操作(这需要尽量减少)。
下半部是执行任何处理活动的地方(如果有的话)。这有助于确保上半部快速完成,并将任何非关键处理推迟到更方便的时间。如果存在下半部,上半部将创建并安排执行下半部。
一旦上半部完成,操作系统调度器将选择一个新的进程。
恢复
当操作系统准备恢复中断的进程时,将恢复程序上下文,并执行iret指令(以弹出rFlags和rip寄存器,从而完成恢复)。
示意图
- 执行当前程序暂停 (Execution of current program is suspended) :
- 当前正在执行的程序被暂停,系统将
rip
(指令指针寄存器)和rFlags
(标志寄存器)保存到栈中。
- 获取中断服务例程的起始地址 (Obtain starting address of Interrupt Service Routine (ISR)) :
- 使用中断号乘以16,这个结果用作中断描述符表(IDT)的偏移量。
- 从IDT中获取该中断的ISR地址。
- 跳转到中断服务例程 (Jump to Interrupt Service Routine) :
- 将
rip
设置为从IDT中获取的地址,这样就实现了对ISR的跳转。
- 中断服务例程执行 (Interrupt Service Routine Executes) :
- 保存上下文(即,保存所有被改变的附加寄存器)。
- 处理中断(具体操作取决于生成的中断类型)。
- 安排任何后续的数据处理活动。
- 中断进程恢复 (Interrupted Process Resumption) :
- 根据操作系统的调度器恢复调度。
- 恢复之前保存的上下文。
- 执行
iret
指令,从栈中弹出rFlags
和rip
寄存器,完成恢复过程。这种中断处理机制允许动态地、在运行时查找ISR地址。