【 tulaoshi.com - Linux 】
第七章 中断及中断处理
本章主要描叙Linux核心的中断处理过程。尽管核心提供通用机制与接口来进行中断处理,大多数中断处理细节都是CPU体系结构相关的。
图7.1 中断路由的逻辑图
Linux通过使用多种不同硬件来执行许多不同任务。包括驱动显示器的视频设备、驱动硬盘的IDE设备等。 我们可以同步驱动这些设备,即我们可以发送一个请求执行一组操作(比如说将一块内存数据写入到磁盘)然后等待到执行完毕。这种方式虽然可以工作,但是效率很低,因为操作系统必须等待每个操作的完成,所以操作系统将花费大量时间在“忙等待”上。更为有效的方式是执行请求,然后转去执行其它任务。当设备完成请求时再通过中断通知操作系统。这样系统中可以同时存在多个未完成的任务。
不管CPU在作什么工作,为了让设备产生中断必须提供一些必要的硬件支持。几乎所有的通用处理器如 Alpha AXP都使用近似的方法。CPU的一些物理引脚被设计成可以改变电压(如从+5V变成-5V)从而引起CPU停止当前工作并开始执行处理中断的特殊代码:中断处理程序。这些引脚之一被连接到一个周期性时钟上并每隔千分之一秒就接收一次中断,其它引脚则可连接到系统中其它设备如SCSI控制器上。
系统常使用中断控制器来在向CPU中断引脚发送信号之前将设备中断进行分组。这样可以节省CPU上中断引脚个数,同时增加了系统设计的灵活性。此中断控制器通过屏蔽与状态寄存器来控制中断。通过设置屏蔽寄存器中的某些位可以使能或者关闭中断,读取状态寄存器可得到系统当前处于活动状态的中断。
系统中有些中断是通过硬连线连接的,如实时时钟的周期性定时器可能被固定连接到中断控制器的引脚3上。而其它连接到控制器的引脚只能由插到特定ISA或PCI槽中的控制卡来决定。例如中断控制器中的引脚4可能被连接到PCI槽号0,但可能某天此槽中插入一块以太网卡而过几天又会换成SCSI控制器。总之每个系统都有其自身的中断路由机制,同时操作系统还应该能灵活处理这些情况。
多数现代通用微处理器使用近似的方法来处理中断。硬件中断发生时,CPU将停止执行当前指令并将跳转到内存中包含中断处理代码或中断处理代码指令分支的位置继续执行。这些代码在一种特殊CPU模式: 中断模式下执行。通常在此模式下不会有其它中断发生。但是也有例外;有些CPU将中断的优先级进行分类,此时更高优先级的中断还可能发生。这样意味着必须认真编写第一级中断处理代码,同时中断处理过程应该拥有其自身的堆栈,以便存储转到中断处理过程前的CPU执行状态(所有CPU的普通寄存器和上下文)。一些CPU具有一组特殊的寄存器-它们仅存在于中断模式中,在中断模式下可以使用这些寄存器来保存执行所需要的执行上下文。
当中断处理完毕后CPU状态将被重储,同时中断也将被释放。CPU将继续做那些中断发生前要做的工作。中断处理代码越精炼越好,这样将减少操作系统阻塞在中断上的时间与频率。
7.1 可编程中断控制器
系统设计者可以自由选择中断结构,一般的IBM PC兼容将使用Intel 82C59A-2 CMOS可编程中断控制器或其派生者。这种控制器在PC诞生之前便已经产生,它的可编程性体现在那些位于众所周知ISA内存位置中的寄存器上。非Intel系统如基于Alpha AXP的PC不受这些体系结构限制,它们经常使用各种不同的中断控制器。
图7.1给出了两个级连的8位控制器,每个控制器都有一个屏蔽与中断状态寄存器:PIC1和PIC2。这两个屏蔽寄存器分别位于ISA I/O空间0x21和0xA1处,状态寄存器则位于0x20和0xA0。对此屏蔽寄存器某个特定位置位将使能某一中断,写入0则屏蔽它。但是不幸的是中断屏蔽寄存器是只写的,所以你无法读取你写入的值。这也意味着Linux必须保存一份对屏蔽寄存器写入值的局部拷贝。一般在中断使能和屏蔽例程中修改这些保存值,同时每次将这些全屏蔽码写入寄存器。
当有中断产生时,中断处理代码将读取这两个中断状态寄存器(ISR)。它将0x20中的ISR看成一个16位中断寄存器的低8位而将0xA0中的ISR看成其高8位。这样0xA0中ISR第1位上的中断将被视作系统中断9。PIC1 上的第二位由于被用来级连PIC2所以不能作其它用处,PIC2上的任何中断将导致PIC1的第二位被置位。
7.2 初始化中断处理数据结构
核心的中断处理数据结构在设备驱动请求系统中断控制时建立。为完成此项工作,设备驱动使用一组Linux核心函数来请求中断,使能中断和屏蔽中断。
每个设备驱动将调用这些过程来注册其中断处理例程地址。
有些中断由于传统的PC体系结构被固定下来,所以驱动仅需要在其初始化时请求它的中断。软盘设备驱动正是使用的这种方式;它的中断号总为6。有时设备驱动也可能不知道设备使用的中断号。对PCI设备驱动来说这不是什么大问题,它们总是可以知道其中断号。但对于ISA设备驱动则没有取得中断号的方便方式。Linux通过让设备驱动检测它们的中断号来解决这个问题。
设备驱动首先迫使设备引起一个中断。系统