深入理解系统中断(INTERUPT)
什么是中断
计算机中的“中断”(Interruption),广义上理解,是指打断(interrupt)正在执行的程序使得处理器去执行其他程序。(BTW:“中断”一词,听上去莫名其妙,感觉叫“打断”更贴切,只不过有点难听。。。)
整个操作系统就是一个中断驱动的死循环,操作系统原理如果用一行代码解释,下面这样再合适不过了。
while(true) { doNothing(); }
其他所有事情都是由操作系统提前注册的中断机制和其对应的中断处理函数完成,我们点击一下鼠标,敲击一下键盘,执行一个程序,都是用中断的方式来通知操作系统帮我们处理这些事件,当没有任何中断事件时,它就乖乖停在死循环里不出来。
所以,中断,非常重要,它也是理解整个操作系统的根基。
Linux 操作系统中大多数设备都采用中断处理方式来控制数据 I/O 。
打断程序执行的方式
总结——硬中断和软中断
从中断实现的角度看,分为硬中断和软中断。
硬中断:由硬件CPU实现。
实现:CPU 在每个指令周期的最后,会留一个 CPU 周期去查看是否有中断信号,若有,则把中断号取出、去中断向量表中寻找中断处理程序、跳过去执行。
触发:外部硬件直接给CPU引脚发送中断号信息,从而引起中断。
分类:根据硬中断触发的方式的不同,硬中断可进一步分类:直接给CPU INTR引脚发信号触发中断、CPU自身执行指令触发中断、软件通过int n等指令触发中断。详见后文。
软中断:完全由软件实现。
实现:OS有一个单独的守护进程,不断轮询内存中的一组标志位(如BitMap),如果哪个标志位有值了,那去这个标志位对应的软中断向量表数组的相应位置,找到软中断处理函数,然后跳过去执行。
触发:其他软件通过设置这些标记位触发中断。
相关源码:
asmlinkage void __init start_kernel(void) { ... trap_init(); sched_init(); time_init(); ... rest_init(); } static void rest_init(void) { kernel_thread(init, NULL, CLONE_KERNEL); } static int init(void * unused) { do_pre_smp_initcalls(); } static void do_pre_smp_initcalls(void) { spawn_ksoftirqd(); } // spawn kernel soft irt daemon 创建内核软中断守护进程 __init int spawn_ksoftirqd(void) { cpu_callback(&cpu_nfb, CPU_ONLINE, (void *)(long)smp_processor_id()); register_cpu_notifier(&cpu_nfb); return 0; } static int __devinit cpu_callback(...) { kernel_thread(ksoftirqd, hcpu, CLONE_KERNEL); } static int ksoftirqd(void * __bind_cpu) { for (;;) { while (local_softirq_pending()) { do_softirq(); cond_resched(); } } } asmlinkage void do_softirq(void) { h = softirq_vec;//软中断列表首地址 pending = local_softirq_pending(); //软中断标记字,int 32类型 do { if (pending & 1) { h->action(h);//执行中断处理程序 h++; pending >>= 1; } while (pending); }
两者的实现机制和触发机制不同。但目的和最终效果一样:都是为了产生一个信号,从而让CPU暂止当前正在运行的程序、转而去执行中断处理程序、执行完之后再返回继续执行原程序。
Linux 会把中断分成上下两半部分执行,上半部分用硬中断处理最简单的逻辑(通常是改标记位触发软中断),下半部分异步执行软中断处理函数。因为对于耗时操作(如接收网络数据),如果完全由硬中断处理会影响对其他硬中断的响应。
硬中断的分类
Intel手册中说明的打断程序运行的机制
总结
这些硬中断方式都可以打断程序或任务的执行,它们的触发都是为了给CPU一个中断信号,且CPU收到信号后的后续处理流程一样——即暂停执行当前程序或任务、根据中断号去中断表中找到对应的中断处理程序并执行、执行完后返回继续执行原程序或任务。
从硬中断触发的角度看,硬中断可进一步分类:
一种分类方式,分为三类:前两者为硬件中断(硬件产生的中断)、第三个为软件中断(软件产生的中断);第一者为外部中断、后两者为内部中断。
Interrupt —— 外部设备通过CPU引脚触发:可进一步分为可屏蔽中断(连接CPU INTR引脚触发)和不可屏蔽中断(连接CPU NMI引脚触发)。
An interrupt is an asynchronous event that is typically triggered by an I/O device.
INTR:硬盘、鼠标、打印机、网卡等设备发出的中断信号,可通过 eflags 寄存器的 IF 位将所有这些外部设备的中断屏蔽。
NMI:电源掉电、内存读写错误、总线奇偶校验错误等灾难性的错误,不可屏蔽,CPU 必须立刻处理。
Exception —— CPU执行出现异常时触发:CPU执行指令时产生的异常引起。如除法指令分母为0、未定义的指令分别引起0、6 号中断。分为三类:
An exception is a synchronous event that is generated when the processor detects one or more predefined conditions while executing an instruction.
Fault(故障):可恢复的错误。发生此中断时,CPU 将机器状态恢复到异常之前的状态,之后调用中断处理程序,结束后返回。常见的如缺页异常。
Trap(陷阱):有意的异常。通常是调试程序中用 int3 指令主动触发。例如系统调用时通过陷阱指令触发上下文切换。
Abort(终止):不可恢复的异常。直接将此程序从进程表中去掉。
通过软件(通常是系统调用)触发:由程序或任务显式通过 INT n 等指令触发。
int 3:中断向量号3,调试断点指令
into:中断向量号4,中断溢出指令
bound:中断向量号5,检查数组索引越界指令
ud2:中断向量号6,未定义指令,常用于软件测试中主动发起这个中断
注:上面所述中,硬中断和硬件中断(类似地,软中断和软件中断)不是一个事,前者从实现的角度说的,指用硬件实现的中断;后者从触发的角度说的,指通过硬件触发的中断。实际中需结合语境判断,或者为了避免歧义,最好指出是从中断实现还是中断触发哪个角度而言的。
参考资料