如果你对中断是什么都不清楚的话,还是先Google一下中断的定义和基本概念吧,这里给出一个链接: http://zh.wikipedia.org/zh/%E4%B8%AD%E6%96%B7

好了,看完了这个链接,我想你已经大致明白了什么是中断,还有中断的作用了吧?我们再来总结下,其实简单说中断就是一种通知机制罢了。我们知道操作系统的一个核心任务就是和连接在主板上的所有的硬件设备进行通信,但是CPU和这些外设的速率根本就不在一个数量级上,倘若CPU向某一个设备发出一个请求并且一直等待反馈结果的话,这样带来的性能损失是不可接受的。而且CPU在运行期间需要得知外设所发生的事件,轮询显然是不可取的,那么就迫切需要一种机制来帮助我们解决这个问题。

肩负着这一伟大使命,中断应运而生。当中断发生时,典型的处理方式就是打断CPU目前正在做的事情,CPU会保留当前的执行现场,转移到该中断事先安排好的中断处理函数去执行,执行结束之后再回来恢复之前的执行现场去执行。

从物理学的角度看,中断其实就是一种电信号,一般由硬件设备生成并送入中断控制器统一协调(当然需要一个“协调机构”了,试想所有设备不区分轻重缓急的和CPU发送中断信号的恐怖场景…)。中断控制器就是个简单的电子芯片,其作用就是将汇集的多路中断管线,采用复用技术只通过一条中断线和CPU相连接。既然中断控制器这里只有一条线和CPU相链接,那么为了区分各个设备,中断自然就有编号了。

补充一下,其实CPU的中断管脚并非只有一根,其实是有NMI和INTR两个管脚,因为从严重性上来看,中断是分为两类的,首先NMI管脚触发的中断是需要无条件立即处理的,这种类型的中断是不会被阻塞和屏蔽的,所以叫做非屏蔽中断(Non Maskable Interrupt, NMI)。事实上一旦产生了NMI中断,就意味着CPU遇到了不可挽回的错误,一般不会进行处理,只是给出一个错误信息。而我们之前所说的中断控制器连接的管脚叫做INTR,这类中断有两个特点,分别是数量多和可屏蔽。而我们主要关注的正是INTR中断。

我举一个通俗的例子,假设你就是CPU,你正在看书(执行任务),突然间你的鼻涕流下来了(一个NMI中断),这个自然是不可以屏蔽的,不然会流到嘴里的…(好恶心),你现在把书反着扣在桌子上避免找不到页码(保留当前执行现场),取出纸巾…(此处省略几十个字),OK,你处理完后把书拿起来继续看(恢复之前的执行现场)。这就是一个中断的处理过程,其实很简单是不是?这是不可屏蔽中断,那么可屏蔽的呢?还是刚刚那个场景,你在看书,手机响了(一个INTR中断),但是你在学习期间不想被打扰,就无视它了…这就是可屏蔽中断了。

通俗的例子举完了,我们还是专业一点好了。在x86PC中,我们熟知的中断控制芯片就是8259了,它就是我们说的中断控制器了。Intel的处理器允许256个中断,中断号范围是0~255。8259芯片负责15个,但是并不固定中断号,允许通过IO端口设置以避免冲突。所以,它的全称是可编程中断控制器(Programmable Interrupt Controller,PIC)。关于8259的资料网上铺天盖地的,至于8259的结构,如何屏蔽中断什么的我就不多说了,请读者自行了解。

其实从上面的描述中我们基本上能理解中断的概念了。再简单说就是硬件发生了某个事件后告诉中断控制器,中断控制器汇报给CPU,CPU从中断控制器处得知了中断号,根据这个中断号找到对应的中断处理程序并转移过去执行,完成后重新回到之前的执行流程去。

至于实模式下的中断处理,我简单说下吧。既然Intel支持256个中断,理论上就需要256段对应的中断处理程序了,至于它们放在哪里并不重要,重要的是如何找到入口。实模式下很简单,实模式下一个地址由段地址+偏移地址构成,一个函数入口地址正好就是4字节,256个函数的地址数组就是1KB了。CPU要求直接把中断处理函数的地址从0~255按顺序放置在物理内存地址的0x00000~0x003ff,占据了内存最前面的1KB。就这么简单,CPU可以简单的通过一个中断号码乘以4找到相应的处理函数的地址并执行了。

我们之前一直说的都是硬件中断,其实除了硬件中断之外还有软件中断,也就是软件系统也可以利用中断机制来完成一些任务,比如有些OS的系统调用的实现就采用了中断的方式。

我们的重点是保护模式下的中断处理。中断处理程序是运行在ring0层的,这就意味着中断处理程序拥有着系统的全部权限,那么我们就不能简单的像实模式下类似函数指针数组这样的方式了。仿照内存段描述符表的思路,Intel设置了一个叫做中断描述符表(IDT, Interrupt Descriptor Table)的东西,和段描述符表一样放置在主存中,类似地,也有一个中断描述符表寄存器(IDTR)记录这个表的起始地址。我们给出一张IA32平台上的中断编号的定义表。

这是0号到19号中断,20~31号中断Intel保留了,32~255号中断留给了用户去定义和使用。在Linux系统下我们可以查阅当前的中断定义和映射表,方法是查看/proc/interrupts 文件即可,如图所示:

至于保护模式下的中断描述符的结构,分类等等细节问题,我就不多说了。本文的定位就是对中断概念的科普,写到这里也算是完成使命了。接下来怎么继续研究呢?我推荐Intel的CPU开发第三卷的中断章节,《深入理解计算机系统》第8章—异常控制流,《x86汇编—实模式到保护模式》最后一章供大家参考。

相信有了本文的简单介绍,对大家研究上面基本书的相关章节会有所帮助。而且你会发现本文为了通俗易懂,简化了很多的东西,甚至有的地方严格说是有问题的。不管怎么样,本文权当抛砖引玉,路还很长,我们一起走吧。

赏杯咖啡鼓励下~