当 CPU 进入中断处理程序(Interrupt Handler)之前,硬件和操作系统会协同完成一系列关键操作,以确保上下文正确保存并安全切换到中断上下文。以下是 ARMv7架构(以 Linux 内核为例)的详细流程:
1. 硬件自动完成的操作
(1) 模式切换与状态保存
- 切换处理器模式:
根据中断类型(如 IRQ、FIQ),CPU 自动切换到对应的异常模式(如IRQ Mode
)。 - 保存返回地址:
PC
(当前被中断的指令地址)→LR_<mode>
(如LR_irq
)。
- 注意:ARM 架构的返回地址可能是
PC+4
或PC+8
,取决于具体子架构。
- 保存状态寄存器:
CPSR
→SPSR_<mode>
(保存中断前的处理器状态)。
(2) 禁用中断(可选)
- 自动关闭同级/低优先级中断:
某些架构会默认关闭中断(如 ARMv7 进入 IRQ Mode 后可能禁用进一步 IRQ)。
2. 软件(OS/Kernel)的关键操作
Linux 内核在正式执行中断处理函数前,通常会通过汇编入口代码完成以下步骤:
(1) 保存被中断任务的上下文
@ arch/arm/kernel/entry-armv.S
__irq_svc:
sub lr, lr, #4 @ 修正 LR_irq (ARMv7 IRQ返回地址调整)
srsdb sp!, #SVC_MODE @ 保存 LR_irq和SPSR_irq到SVC栈
stmfd sp!, {r0-r12} @ 保存通用寄存器
- 寄存器压栈顺序:通常为
R0-R12
,SP
,LR
,PC
,CPSR
。 - 栈选择:
若从用户态触发中断,内核会切换到线程的内核栈;若在内核态则复用当前内核栈。
(2) 识别中断源
- 读取中断控制器寄存器:如 GIC(Generic Interrupt Controller)的
ICC_IAR1_EL1
(ARMv8)。 - 获取中断号:确定是哪个外设触发了中断。
(3) 调用全局中断处理函数
// arch/arm/kernel/irq.c
handle_domain_irq(domain, hwirq, regs);
- 参数传递:包括中断号、设备信息、保存的寄存器上下文等。
3. ARMv8/AArch64的区别
在 ARMv8中,流程类似但寄存器命名和异常级别不同:
// arch/arm64/kernel/entry.S
el1_irq:
kernel_entry el=1 @ Save context to EL1 stack
bl irq_handler @ Call C handler
kernel_exit el=1 @ Restore context
- 共用通用寄存器:不再分组 X0-X30,但会保存到异常级别的栈中。
- 使用 SP_ELx:每个异常级别有独立的栈指针。
4. x86架构对比
以 Linux on x86为例:
// arch/x86/entry/entry_64.S
interrupt_entry:
swapgs @ Switch to kernel GS base (if from user)
pushq %rax @ Save registers...
call do_IRQ @ Call generic handler
- 硬件压栈:x86会在触发中断时自动将部分上下文压入内核栈。
- 区分用户/内核态堆栈:通过 TSS (Task State Segment)切换。
关键总结表
操作类型 | ARMv7 (e.g., Cortex-A9) | ARMv8 (e.g., Cortex-A72) | x86_64 |
---|---|---|---|
模式切换 | IRQ Mode → SVC Mode | EL0 → EL1 | Ring3 → Ring0 |
返回地址计算 | LR = PC + offset (depends on ISA) | ELR_ELx = PC + offset | RIP pushed to stack |
状态保存位置 | SPSR_ |
SPSR_ELx | RFLAGS pushed to stack |
上下文存储方式 | Banked SP/LR +手动存 R0-R12 | SP_ELx +手动存 X0-X30 | Hardware auto-push部分寄存器 |
调试建议
- 查看反汇编代码:
objdump -D vmlinux > disasm.txt # Linux内核反汇编
- 跟踪寄存器的变化:
使用 QEMU+GDB 单步调试观察 CPSR、SP、LR等寄存器的变化。
内容由零声教学AI助手提供,问题来源于学员提问