3.4.2 Linux对异常和中断的处理

上面给出了硬件对异常和中断进行处理的一般步骤,下面将概要描述Linux对异常和中断的处理,具体的实现过程将在后面介绍。

1.异常处理

  Linux利用异常来达到两个截然不同的目的:

·      给进程发送一个信号以通报一个反常情况

·      管理硬件资源

     对于第一种情况,例如,如果进程执行了一个被0除的操作,CPU则会产生一个“除法错误”异常,并由相应的异常处理程序向当前进程发送一个SIGFPE信号。当前进程接收到这个信号后,就要采取若干必要的步骤,或者从错误中恢复,或者终止执行(如果这个信号没有相应的信号处理程序)。

对于第二种情况,内核使用两种异常来有效地管理硬件资源,相应的处理程序也就更复杂。在这种情况下,异常并不表示一种错误情况:

·      用“设备不可用”异常来推迟装载浮点寄存器。

·      用“缺页”异常推迟把新页框分配给进程。

内核对异常处理程序的调用有一个标准的结构,它由以下三部分组成:

·      在内核中保存大多数寄存器的内容(由汇编语言实现)

·      调用C编写的异常处理函数

·      通过ret_from_exception()函数从异常退出。

关于内核对异常的具体处理在此不进行详细介绍,在第六章内存管理中我们将涉及到“缺页”异常处理程序,本节的重点放在中断处理。

 2.中断处理

   当一个中断发生时,并不是所有的操作都具有相同的急迫性。事实上,把所有的操作都放进中断处理程序本身并不合适。需要时间长的、非重要的操作应该推后,因为当一个中断处理程序正在运行时,相应的IRQ中断线上再发出的信号就会被忽略。更重要的是,中断处理程序是代表进程执行的,它所代表的进程必需总处于TASK_RUNNING状态,否则,就可能出现系统僵死情形。因此,中断处理程序不能执行任何阻塞过程,如I/O设备操作。因此,Linux把一个中断要执行的操作分为下面的三类:

1)紧急的(Critical

这样的操作诸如:中断到来时中断控制器做出应答,对中断控制器或设备控制器重新编程,或者对设备和处理器同时访问的数据结构进行修改。这些操作都是紧急的,应该被很快地执行,也就是说,紧急操作应该在一个中断处理程序内立即执行,而且是在禁用中断的状态下。

2)非紧急的(Noncritical

这样的操作如修改那些只有处理器才会访问的数据结构(例如,按下一个键后,读扫描码)。这些操作也要很快地完成,因此,它们由中断处理程序立即执行,但在启用中断的状态下。

3)非紧急可延迟的(Noncritical deferrable

    这样的操作如,把一个缓冲区的内容拷贝到一些进程的地址空间(例如,把键盘行缓冲区的内容发送到终端处理程序的进程)。这些操作可能被延迟较长的时间间隔而不影响内核操作:有兴趣的进程会等待需要的数据。非紧急可延迟的操作由一些被称为“下半部分”(bottom halves)的函数来执行。我们将在后面讨论“下半部分”。

    所有的中断处理程序都执行四个基本的操作:

·      在内核中保存IRQ的值和寄存器的内容。

·      给与IRQ中断线相连的中断控制器发送一个应答,这将允许在这条中断线上进一步发出中断请求。

·      执行共享这个IRQ的所有设备的中断服务例程(ISR)。

·      跳到ret_from_intr(  )的地址后终止。