万卷书   首页 > 电脑书籍 > Linux >Linux内核分析方法谈(下)
 
 II、Linux的中断处理

  硬件中断机制提供了256个入口,即idt中包含的256个中断描述符(对应256个中断向量)。

  而0-31号中断向量被intel公司保留用来处理异常事件,不能另作它用。对这0-31号中断向量,操作系统只需提供异常的处理程序,当产生一个异常时,处理机就会自动把控制转移到相应的处理程序的入口,运行相应的处理程序;而事实上,对于这32个处理异常的中断向量,此版本(2.2.5)的Linux只提供了0-17号中断向量的处理程序,其对应处理程序参见表一、中断向量和异常事件对应表;也就是说,17-31号中断向量是空着未用的。

  既然0-31号中断向量已被保留,那么,就是剩下32-255共224个中断向量可用。这224个中断向量又是怎么分配的呢?在此版本(2.2.5)的Linux中,除了0x80 (SYSCALL_VECTOR)用作系统调用总入口之外,其他都用在外部硬件中断源上,其中包括可编程中断控制器8259A的15个irq;事实上,当没有定义CONFIG_X86_IO_APIC时,其他223(除0x80外)个中断向量,只利用了从32号开始的15个,其它208个空着未用。

  这些中断服务程序入口的设置将在下面有详细说明。

一.相关数据结构
中断描述符表idt: 也就是中断向量表,相当如一个数组,保存着各中断服务例程的入口。(详细描述参见图一、中断描述符格式)
与硬中断相关数据结构:
与硬中断相关数据结构主要有三个:

一:定义在/arch/i386/kernel/irq.h中的

struct hw_interrupt_type {

const char * typename;

void (*startup)(unsigned int irq);

void (*shutdown)(unsigned int irq);

void (*handle)(unsigned int irq, struct pt_regs * regs);

void (*enable)(unsigned int irq);

void (*disable)(unsigned int irq);

};

二:定义在/arch/i386/kernel/irq.h中的

typedef struct {

unsigned int status; /* IRQ status - IRQ_INPROGRESS, IRQ_DISABLED */

struct hw_interrupt_type *handler; /* handle/enable/disable functions */

struct irqaction *action; /* IRQ action list */

unsigned int depth; /* Disable depth for nested irq disables */

} irq_desc_t;

三:定义在include/linux/ interrupt.h中的

struct irqaction {

void (*handler)(int, void *, struct pt_regs *);

unsigned long flags;

unsigned long mask;

const char *name;

void *dev_id;

struct irqaction *next;

};

三者关系如下:

图四、与硬中断相关的几个数据结构各关系

各结构成员详述如下:

struct irqaction结构,它包含了内核接收到特定IRQ之后应该采取的操作,其成员如下:
handler:是一指向某个函数的指针。该函数就是所在结构对相应中断的处理函数。

flags:取值只有SA_INTERRUPT(中断可嵌套),SA_SAMPLE_RANDOM(这个中断是源于物理随机性的),和SA_SHIRQ(这个IRQ和其它struct irqaction共享)。

mask:在x86或者体系结构无关的代码中不会使用(除非将其设置为0);只有在SPARC64的移植版本中要跟踪有关软盘的信息时才会使用它。

name:产生中断的硬件设备的名字。因为不止一个硬件可以共享一个IRQ。

dev_id:标识硬件类型的一个唯一的ID。Linux支持的所有硬件设备的每一种类型,都有一个由制造厂商定义的在此成员中记录的设备ID。

next:如果IRQ是共享的,那么这就是指向队列中下一个struct irqaction结构的指针。通常情况下,IRQ不是共享的,因此这个成员就为空。
struct hw_interrupt_type结构,它是一个抽象的中断控制器。这包含一系列的指向函数的指针,这些函数处理控制器特有的操作:
typename:控制器的名字。

startup:允许从给定的控制器的IRQ所产生的事件。

shutdown:禁止从给定的控制器的IRQ所产生的事件。

handle:根据提供给该函数的IRQ,处理唯一的中断。

enable和disable:这两个函数基本上和startup和shutdown相同;


另外一个数据结构是irq_desc_t,它具有如下成员:
status:一个整数。代表IRQ的状态:IRQ是否被禁止了,有关IRQ的设备当前是否正被自动检测,等等。

handler:指向hw_interrupt_type的指针。

action:指向irqaction结构组成的队列的头。正常情况下每个IRQ只有一个操作,因此链接列表的正常长度是1(或者0)。但是,如果IRQ被两个或者多个设备所共享,那么这个队列中就有多个操作。

depth:irq_desc_t的当前用户的个数。主要是用来保证在中断处理过程中IRQ不会被禁止。

irq_desc是irq_desc_t 类型的数组。对于每一个IRQ都有一个数组入口,即数组把每一个IRQ映射到和它相关的处理程序和irq_desc_t中的其它信息。
与Bottom_half相关的数据结构:
图五、底半处理数据结构示意图


bh_mask_count:计数器。对每个enable/disable请求嵌套对进行计数。这些请求通过调用enable_bh和disable_bh实现。每个禁止请求都增加计数器;每个使能请求都减小计数器。当计数器达到0时,所有未完成的禁止语句都已经被使能语句所匹配了,因此下半部分最终被重新使能。(定义在kernel/softirq.c中)

bh_mask和bh_active:它们共同决定下半部分是否运行。它们两个都有32位,而每一个下半部分都占用一位。当一个上半部分(或者一些其它代码)决定其下半部分需要运行时,就通过设置bh_active中的一位来标记下半部分。不管是否做这样的标记,下半部分都可以通过清空bh_mask中的相关位来使之失效。因此,对bh_mask和bh_active进行位AND运算就能够表明应该运行哪一个下半部分。特别是如果位与运算的结果是0,就没有下半部分需要运行。

bh_base:是一组简单的指向下半部分处理函数的指针。
  bh_base代表的指针数组中可包含 32 个不同的底半处理程序。bh_mask 和 bh_active 的数据位分别代表对应的底半处理过程是否安装和激活。如果 bh_mask 的第 N 位为 1,则说明 bh_base 数组的第 N 个元素包含某个底半处理过程的地址;如果 bh_active 的第 N 位为 1,则说明必须由调度程序在适当的时候调用第 N 个底半处理过程。 

上一页 下一页