1. 中断概念:中断的定义、中断向量表、中断服务程序(ISR)的基本概念与硬件触发流程
1.1 中断的定义
中断(Interrupt)是嵌入式系统与通用计算机中一种核心的硬件机制,它允许外设或软件通过一个电信号或消息,异步地通知处理器发生了需要立即关注的事件。当处理器接收到中断信号后,会暂停当前正在执行的程序流,保存现场,然后跳转到一个预先定义好的处理程序去响应这个事件,处理完成后再恢复原来的程序继续执行。
核心特征:中断的“异步性”意味着它可以在主程序执行的任意时刻发生,不受主程序控制。这与轮询(Polling)方式形成鲜明对比——轮询需要CPU主动、周期性地检查设备状态,效率低下且实时性差。
1.2 中断向量表(Interrupt Vector Table, IVT)
中断向量表是一张存储在内存固定区域的表格,其每个表项(称为中断向量)存放着对应中断服务程序(ISR)的入口地址。当硬件中断发生时,CPU通过中断控制器获取中断号,然后以该中断号为索引在IVT中查找对应的ISR地址,并跳转执行。
| 概念 | 说明 |
|---|---|
| 中断向量号 | 每个中断源被分配一个唯一的数字标识(0~255),用于索引IVT。 |
| 中断向量 | IVT中的每一个表项,存储着对应ISR的起始地址(段基址+偏移量)。 |
| IVT基址 | IVT在内存中的起始物理地址。在x86实模式下通常位于0x0000:0x0000;在ARM Cortex-M中位于向量表偏移寄存器(VTOR)指定的地址。 |
| 表项大小 | 在x86实模式下每个向量占4字节(段:偏移);在ARM Cortex-M中每个向量占4字节(函数地址)。 |
注意:在Linux内核启动早期,系统会从引导加载程序(如U-Boot)设置的IVT切换到内核自己管理的中断描述符表(IDT)。IDT是IVT在保护模式下的进化版本,除了地址外还包含权限级别(DPL)和门类型等信息。
1.3 中断服务程序(ISR)的基本概念
中断服务程序(Interrupt Service Routine, ISR)是响应特定中断事件而执行的专用函数。ISR必须满足以下关键约束:
- 快速执行:ISR执行期间,同级或更低优先级的中断通常被屏蔽,过长的ISR会严重影响系统的实时响应能力。
- 不可阻塞:ISR中不能调用可能导致睡眠(sleep)或阻塞(block)的内核函数(如
mutex_lock、copy_from_user)。 - 保存/恢复上下文:ISR入口必须保存所有可能被破坏的寄存器,出口必须恢复,确保被中断的程序能正确继续。
- 可重入性:在支持中断嵌套的系统中,高优先级ISR可能打断低优先级ISR,因此ISR应尽量设计为可重入或使用锁保护共享数据。
1.4 硬件触发流程
一个典型的中断从硬件触发到CPU开始执行ISR,经历以下标准步骤:
- 外设事件发生:硬件设备(如网卡、定时器、GPIO引脚)产生一个中断请求信号。
- 中断控制器仲裁:中断控制器(如x86的APIC、ARM的GIC)接收多个中断源信号,根据优先级、使能状态和当前CPU状态,选择一个最高优先级的中断向CPU核心发送。
- CPU响应中断:CPU在每条指令执行的最后阶段检查中断请求线。若检测到有效中断且中断允许标志(IF)为1,则进入中断响应周期。
- 保存现场:CPU自动将当前程序计数器(PC/CS:IP)、处理器状态字(PSW/EFLAGS)以及部分寄存器压入堆栈。
- 获取中断向量:CPU向中断控制器发送中断响应信号(INTA),中断控制器返回中断向量号。
- 查表跳转:CPU以中断向量号为索引,从IVT/IDT中读取ISR入口地址,并跳转执行。
- 执行ISR:CPU开始执行中断服务程序,处理硬件事件。
- 中断返回:ISR执行完毕后执行中断返回指令(如x86的
iret),CPU从堆栈恢复之前保存的现场,继续执行被中断的程序。
关键时序指标:中断延迟(Interrupt Latency) = 从硬件中断信号产生到CPU开始执行ISR第一条指令的时间。该指标直接影响系统的实时性能,通常由中断控制器仲裁时间、CPU响应时间、现场保存时间共同决定。
1.5 中断向量表结构示例(ARM Cortex-M)
以ARM Cortex-M系列处理器为例,中断向量表的前16个向量为系统异常(如复位、NMI、硬fault),从向量号16开始为外部中断(IRQ)。向量表基址由VTOR寄存器控制,支持运行时重定位。
/* ARM Cortex-M 向量表示例(部分) */
__attribute__((section(".isr_vector")))
void (* const g_pfnVectors[])(void) = {
/* 系统异常(向量号 0~15) */
(void (*)(void)) &__StackTop, /* 0: 初始栈指针 */
Reset_Handler, /* 1: 复位 */
NMI_Handler, /* 2: NMI */
HardFault_Handler, /* 3: 硬fault */
/* ... 其他系统异常 ... */
SVC_Handler, /* 11: SVC */
/* ... */
/* 外部中断(向量号 16~255) */
TIM2_IRQHandler, /* 28: TIM2 全局中断 */
TIM3_IRQHandler, /* 29: TIM3 全局中断 */
I2C1_EV_IRQHandler, /* 31: I2C1 事件中断 */
/* ... */
};
Linux内核视角:在ARM Linux中,内核启动时会通过
early_trap_init() 或 vectors_fixup() 将异常向量表复制到高地址(0xffff0000)或由VTOR指向的地址,并设置中断处理入口为 asm_do_IRQ,该函数最终调用注册到内核的通用中断处理框架(Generic IRQ layer)。
1.6 中断与异常的区别
| 特性 | 中断(Interrupt) | 异常(Exception) |
|---|---|---|
| 触发源 | 外部硬件设备(异步) | CPU内部指令执行(同步) |
| 发生时机 | 指令边界之间,不可预测 | 指令执行过程中,可预测 |
| 典型例子 | 网卡收到数据包、定时器超时 | 除零错误、缺页、系统调用(trap) |
| 返回行为 | 返回到被中断指令的下一条 | 可能返回到当前指令(fault)、下一条(trap)或终止(abort) |
| Linux处理 | 通过 request_irq() 注册ISR |
通过 set_trap_handler() 或do_page_fault()处理 |
总结:中断是嵌入式Linux系统响应外部事件的第一推动力。理解中断向量表的布局、ISR的约束条件以及硬件触发流程,是后续学习中断下半部(softirq、tasklet、workqueue)的基础。在下一章中,我们将深入分析Linux内核如何将中断处理拆分为上半部(Top Half)和下半部(Bottom Half),以解决ISR必须快速执行与复杂处理逻辑之间的矛盾。