当前位置:Linux教程 - Linux - Interrupts and Interrupt Handling(中断和中断处理)

Interrupts and Interrupt Handling(中断和中断处理)



        

    本章探讨Linux核心如何处理中断。虽然核心有用于处理中断的通用机制和接口,大部分中断处理的细节还是和体系结构相关的。

     

    Linux使用大量不同的硬件来完成许多不同的任务。显示设备驱动显示器,IDE设备驱动磁盘等等。你可以同步地驱动这些设备,就是你可以发出一个请求执行一些操作(比如把一块内存写到磁盘)然后等待操作结束。这种方式,虽然可以工作,但是非常没有效率,操作系统当它等待每一个操作完成的时候会花费大量时间“忙于什么也不做”(busy doing nothing)。一个好的,更有效的方法是做出了请求然后去作其他更有用的事情,然后当设备完成请求的时候被设备中断。在这种方案下,系统中同一时刻可能有许多设备的请求在同时发生。

     

    让设备中断CPU当前的工作必须有一些硬件的支持。大多数,如果不是所有的话,通用目的的处理器比如Alpha AXP都使用相似的方法。CPU的一些物理管脚的电路只要改变电压(例如从+5V到-5V)就会让CPU停止正在做的工作,开始执行处理中断的特殊代码:中断处理代码。这些管脚之一可能连接一个内部适中,每一个1000分之一秒就接收一个中断,其他的也许连接到系统的其他设备,比如SCSI控制器。

     

    系统通常使用一个中断控制器把设备的中断集合在一起,然后把信号传送到CPU的一个单一的中断管脚。这可以节省CPU的中断管教,也给设计系统带来了灵活性。中断控制器有掩码和状态寄存器,用于控制这些中断。设置掩码寄存器的位可以允许和禁止中断,状态寄存器返回系统中当前的中断。

     

    一些系统中的中断可能是硬连接的,例如实时时钟的内部时钟可能永久地连接到中断控制器的第3管脚。但是,另一些管脚连接什么可能由在特定的ISA或者PCI槽位插入什么控制卡决定。例如,中断控制器的第4管脚可能和PCI槽位0相连,可能某一天有一个以太网卡,当时后来可能是一块SCSI控制卡。每一个系统都有它自己的中断中转机制,操作系统必须足够灵活才能处理。

     

     

    大多数现代的通用目的微处理器用相同的方式处理中断。发生硬件中断的时候,CPU停止它正在运行的指令,跳到内存中一个位置运行,这里或者包含中断处理代码或者是跳到中断处理代码的指令。这种代码通常在CPU的特殊模式下工作:中断模式,通常,这种模式下其他中断不能产生。这里也有例外:一些CPU将中断划分级别,更高级别的中断可以发生。这意味着写第一级的中断处理程序必须非常小心。中断处理程序通常都有自己的堆栈,用来存放CPU的执行状态(CPU所有的通用寄存器和上下文)并处理中断。一些CPU有一组只在中断模式下存在的寄存器,中断处理代码可以使用这些寄存器来存储它需要保存的大部分上下文信息。



    当处理完中断,CPU的状态恢复,中断结束。CPU会继续做它在中断发生之前做的事情。重要的事中断处理程序必须尽可能地有效,通常操作系统不能经常或者长时间阻塞中断。

     

    7.1 Programmable Interrupt Controllers(可编程中断控制器)

     

    系统设计师可以任意使用他们希望用的中断体系结构,但是IBM PC都使用Intel 82C59A-2 CMOS可编程中断控制器或者它的衍生物。这种控制器在PC最初的时候就使用了。它可通过寄存器编程,这些寄存器在ISA地址空间的众所周知的位置。甚至很现代的逻辑芯片组都在ISA内存的相同位置保留了等价的寄存器。非Intel的系统,例如Alpha AXP PC不受这些体系限制,通常使用不同的中断控制器。

     

    图7.1显示了两个串联在一起的8位控制器:每一个都有一个掩码和一个中断状态寄存器,PIC1和PIC2。掩码寄存器位于地址0x21和0xA1,而状态寄存器位于0x20和0xA0。在掩码寄存器的一个特殊位写1允许一种中断,写0可以禁止它。所以向位3写1允许中断3,写0会禁止它。不幸的是(也是让人气恼的),中断掩码寄存器只可以写,你无法读回你所写的值。这意味着Linux必须为它设置的掩码(mask)寄存器保留一份本地拷贝。它在中断允许和禁止的例程中修改这些保存的掩码,每一次都要把整个掩码写到寄存器中。

     

    当产生中断信号,中断处理程序读取两个中断状态寄存器(ISR)。它把0x20的ISR看作16位的中断寄存器的第8位,0xA0中的ISR看作高8位。所以,发生在0xA0的ISR的第1位的中断被看作是中断9。PCI1的第2位不可用,因为它用作串联PIC2的中断,任何PIC2的中断都会使PIC1的第2位置位。

     

    7.2 Initializing the Interrupt Handling Data Structures(初始化中断处理数据结构)

     

    当设备驱动程序要求控制系统的中断的时候建立核心的中断处理数据结构。为此,设备驱动程序使用一系列Linux核心服务,用来请求一个中断、允许它和禁止它。这些设备驱动程序调用这些例程来登记它们的中断处理例程的地址。

    参见arch/*/kernel/irq.c request_irq() enable_irq() and disable_irq()

     

    PC体系结构为了方便把一些中断固定下来,所以驱动程序在初始化的时候只需要简单地请求它的中断。软盘设备驱动程序就是这样:它总是请求中断6。但是也可能一个设备驱动程序不知道设备会使用什么中断。对于PCI设备驱动程序这不是问题,因为它们总是知道它们的中断编号。不幸的是对于ISA设备没有什么简单的办法找到它们的中断号码,Linux允许设备驱动程序探查它们的中断来解决这个问题。

     

    首先,设备驱动程序让设备产生中断,然后系统中所有没有分配的中断都允许了。这意味着设备等待处理的中断现在会通过可编程中断控制器传递。Linux读取中断状态寄存器然后把它的内容返回到设备驱动程序。非0 的结果表示在探查中发生了一或多个中断。驱动程序现在关闭探查,并禁止所有位分配的中断。如果ISA设备驱动程序成功地找到了它的IRQ号,它就可以想平常一样地请求控制它。

    参见arch/*/kernel/irq.c irq_probe_*()

     

    PCI系统比ISA系统更加动态。ISA设备的中断通常用硬件设备上的跳线来设置,对于设备驱动程序是固定的。反过来,PCI设备的中断是在系统启动的时候由PCI BIOS或者PCI子系统在PCI初始化的时候分配的。每一个PCI设备可以使用4个中断管脚其中之一:A、B、C或D。这时设备制造的时候确定的,大多数设备缺省用中断管脚A。每一个PCI槽位的PCI中断线(interrupte line)A、B、C和D都转到中断控制器。所以槽位4的管脚A可能转到了中断控制器的第6管脚,槽位4的管脚B可能转到了中断控制器的管脚7,依此类推。

     



    PCI中断如何被转发(路由route)完全是和系统相关的,必须有一些理解这种PCI中断路由拓扑的设置代码。在Intel PC上,这是启动的时候的系统BIOS代码完成的。但是对于没有BIOS的系统(例如Alpha AXP系统),Linux进行这种设置。PCI设置代码把中断控制器的管脚编号写到每一个设备的PCI配置头中。它使用它知道的PCI中断路有拓扑和设备的PCI槽位以及它正在使用的PCI中断管脚来决定中断管脚(或者说IRQ)编号。设备使用的中断管脚就确定下来并放到PCI配置头的一个域。它把这个信息写到中断线(interrupte line)域(这是为此目的保留的)。当设备驱动程序运行的时候,它读取这个信息,并使用它向Linux核心请求对中断的控制。

    参见arch/alpha/kernel/bios32.c

     

    系统中可能使用许多PCI中断资源。例如,当使用PCI-PCI桥的时候。中断来源的数目可能超过系统的可编程中断控制器的管脚数目。这种情况下,PCI设备可以共享中断:中断控制器上的一个管脚接收来自多于一个PCI设备的中断。Linux让第一个请求一个中断的源宣称(declare)它是否可以共享,这样来支持中断共享。共享中断结果是irq_action向量表中的一个条目可以指向几个irqaction的数据结构。当发生了一个共享的中断的时候,Linux会调用这个源的所有的中断处理程序。所有可以共享中断的设备驱动程序(都应该是PCI设备驱动程序)必须预备在没有中断服务的时候被调用。

     

    7.3 Interrupt Handling(中断处理)

     

    Linux中断处理子系统的一个主要任务是把中断转送到(route)正确的中断处理代码段。这种代码必须了解系统的中断拓扑。例如,如果软驱控制器在中断控制器的管脚6发生中断,它必须可以识别出中断是来自软驱,并把它转送到软驱设备驱动程序的中断处理程序代码。Linux使用一系列数据结构的指针,包含了处理系统中断的例程的地址。这些例程属于系统中的设备的设备驱动程序,每一个设备驱动程序必须负责在驱动程序初始化的时候请求它想要的中断。图7.2显示了irq_action是一个指向irqaction数据结构的指针的向量表。每一个irqaction数据结构都包括了这个中断处理程序的信息,包括中断处理例程的地址。不同体系的中断数目和如何处理是不同的,通常,不同系统之间,Linux中断处理代码是和体系结构相关的。这意味着irq_action向量表的大小依赖于中断源的数目而不同。

     

    当发生中断的时候,Linux必须首先通过读取系统的可编程中断控制器的状态寄存器确定它的来源。然后把这个来源转换成irq_action向量表中的偏移。例如,从软驱控制器来的中断控制器管脚6的中断会转为中断处理程序向量表中的第7个指针。如果发生的中断没有对应的中断处理程序,Linux核心会记录下一个错误,否则,它会调用这个中断源的所有的irqaction数据结构中的中断处理例程。

     

    当Linux核心调用设备驱动程序的中断处理例程的时候,它必须有效地判断为什么被中断,并进行响应。为了找出中断的原因,设备驱动程序会读取中断设备的状态寄存器。设备可能回应:发生了一个错误或者完成了一个请求的操作。例如软驱控制器可能报告它已经把软驱的读磁头定位到了软盘正确的扇区。一旦确定了中断的原因,设备驱动程序可能还需要做更多的工作。如果是这样,Linux核心有机制允许延迟这个操作稍候进行。这可以避免让CPU在中断模式下花费太多时间。详细描述参见设备驱动程序章(第8章)


    发布人:netbull 来自:非常Linux