因为一直依赖都是写的应用,所以对于底层的一些东西有点生疏了,于是乎就需要时不时回来回顾一番,今天我想来聊聊中断。对中断这个东西其实说实话,以前有两个地方我对它印象是比较深的,一个是讲嵌入式系统的时候,里面有各种中断;另外一个就是讲计算机组成的时候,也是各种中断。这两个地方讲的中断可能有一些重叠的地方,但是不全是一样,而这些应该都会在我的回顾里面。

首先,还是先来说说什么是中断吧。我们平时在使用计算机的时候,不知道你想过这么一个问题没有,那就是我在键盘/鼠标上按下一个键之后,计算机是怎么知道我已经按下了键盘,并且知道我按下的是哪个键盘的?这其实就是一个典型的涉及到“中断”的问题了,而且这里也隐含了中断的非常重要的概念:上下半部。

那么当我们按下一个按键的时候,在计算机里面发生了什么呢?事实可能是这样的( Linux 系统),当你在键盘按下一个键的时候,键盘会触发出一系列电信号,一般是多个,然后这个电信号随着你的键盘线(或者蓝牙等等)发送给了计算机中的输入模块(DMA),然后 DMA 会发一个信号给 CPU,告诉 CPU 有键盘来信号了。可能在你的理解中,按一个键盘的速度很快,手快的人一秒钟可以按下十几甚至更多的按键,但是,对于计算机来说,这速度太慢了,如果每当 CPU 接收到这个一个信号之后,都去读取你的键盘输入的信号是什么,那么你的计算机可能是无法使用的,当然,除非你的计算时是专门用来打字的另说。

在 Linux 中,现代的内核(2.6 以后)的处理方式一般都是收到有键盘的信号(硬中断)的之后,然后立即将现在忙的进程上下文保存起来,然后看你的硬中断是什么,调用对应的硬中断处理程序,这就是所谓的上半部。上半部一般很短,而且很简单,所以速度很快,一般都是为后面的下半部做准备,例如创建 tasklet 等,然后就完成了任务。在硬中断处理程序中,中断是运行在中断上下文中的,并且同时会屏蔽中断,并且不能在中断处理程序中执行睡眠操作,因为睡眠操作会导致交出 CPU 执行权,从而导致中断上下文错乱,从而导致中断不能得到正确的处理,进而导致等待该中断处理的外设等出错。

硬中断和内核代码的联系是通过 Linux 内核的 IRQ 中断号进行的,而外设等的中断是使用的硬件中断号,而硬件中断号和内核中断号的联系是通过内核头文件的定义关联的。这里所谓的 “硬中断” 就是硬件产生的中断。

软中断

和硬中断相对应的就是所谓的软中断了,和硬中断是硬件产生的中断对应,我们可以理解“软中断”是软件产生的中断,但是这里的“软件”意义要更广义一些,“软中断“应该理解为指令产生的中断。这里的所谓的指令不仅仅是主动调用的中断指令,还可能是指令的附加产物,例如获取一个虚拟内存对应的数据,但是这导致了“缺页”,那么“缺页”也是一个 “软中断”。

软中断运行的时候同样有软中断上下文,软中断上下文优先级是高于进程上下文的,所以软中断的执行总是在抢占进程上下文的,这里有一个实际的场景就是在网络收发很频繁的系统中,很可能会因为软中断(Network)过多,从而导致系统进程得不到调度而成为瓶颈的问题,所以这也是一个抢占式 OS 就是银弹的反驳例子。

Linux 系统状态查看

想要了解系统的软中断情况,一个简单的办法就是查看系统文件: /proc/softirqs,例如下面这个我的例子:

这里各行的信息分别代表:

如果想了解硬中断的情况,可以查看: /proc/interrupts 系统文件:

小结

本文就概念上和与原理上介绍了一下 Linux 中关于中断的一些知识,但是并未从代码的角度进行中断的处理尝试,这个先留一个坑,后面来填。