1、完整的中断处理过程的教学探讨摘要:计算机系统对意外事件的处理是通过中断技术来实现。对一具体中断事件的处理划分为中断响应、中断服务及中断返回三个过程,在专业教学中学生对此很容易理解,但对中断系统在操作系统中的组织、具体处理过程及过程间的衔接常常感到不解,本文以 Linux 系统为例作了较了详细的介绍。 关键词:中断中断向量中断描述符 中图分类号:G424.21 文献标识码:A 文章编号: 在 i386 中根据 CPU 和中断源间的时序关系,将中断分为同步中断和异步中断两大类。同步中断称为异常(Exception) ,是指 CPU 执行指令时所产生的中断;异步中断称为中断(Interrupt) ,
2、是指由 I/O 设备随机产生的中断。异常和中断在操作系统中没有本质上的差异,二者间的差异主要在于中断向量的形成方式不同。 中断系统的软硬件组织: 1、中断系统的硬件组织 现代计算机中中断硬件系统是由 I/O 设备、中断控制器及 CPU 组成的三级层次结构。逻辑结构如下所示: 中断控制器的主要功能是将 I/O 设备的 IRQ 中断请求信号转换为 CPU的中断请求信号;向 CPU 提供 I/O 设备的中断向量以找到中断服务程序。在现代微机中一般采用 8259A 芯片。 2、Linux 系统中断的软件组织 (1)中断向量 每一个异常和中断都有一个唯一的编号,称为中断向量,在 Linux中使用 025
3、5 之间的无符号整数来表示。在 Linux 中具体分配如下: 031 用于异常和不可屏蔽中断;3247 用于可屏蔽中断;48255用于软中断。 (2)中断描述符 中断描述符主要用来反映中断向量与中断服务程序间的对应关系。中断描述符共占用 8 个字节,其格式如下: 150 位偏移量 150 位选择器 0 0 0 字计数 P DPL 0 类型 3116 位偏移量 其中:字计数表示参数数量,5 位;P 是存在位;DPL 是描述符特权级,2 位;类型占用 4 位,共有任务门、调用门、中断门、陷阱门四种。 16 位选择器和 32 位偏移量用于形成中断服务程序的入口地址,该地址由硬件自动转换为内存的物理地
4、址。 (3)中断描述符表 中断描述符表是由中断描述符的集合,也称为中断向量表。由于Linux 系统中提供了 256 个异常和中断,因此,该表中共有 256 个表项,其中中断向量就是在中断描述表中的索引。 在 Linux 中通过数组 irq_desc256来表示中断描述符表。 (4)与中断相关的数据结构及相互间的关系 在 struct irq_desc 中: status:IRQ 的状态,如是否被禁止、有关 IRQ 的设备当前是否正被自动检测等; handler:中断控制器的类型; action:一条 IRQ 请求线可连接多个设备,该条线上所有设备的中断服务程序以单链表方式连接起来,action
5、 即指向该单链表; 在 struct irqaction 中: handler:指向中断服务程序; flags:该标志位可以是 0 也可以是SA_INTERRUPT、SA_SAMPLE_RANDOM 或 SA_SHIRQ; Static struct hw_interrupt_type i8259A_irq_type:8259A 的属性描述。 (5)中断系统的初始化 中断系统的初始化主要完成对中断控制器和中断向量表的初始化。具体化过程如下: 初始化 8259A:就是向 8259A 的二个端口写入一组初始化命令字; 关闭中断,防止系统因中断信号进入不可预知的状态; 创建中断描述符表:当前所有设备
6、的驱动程序没有加载,因此,此时的中断描述符表仍然是一张空表; 调用函数 trap_init()完成异常、软中断及不可屏蔽中断的初始化,即 031 和 128 共 33 个中断描述符的初始化; 调用函数 init_IRQ()完成可屏蔽中断的初始化,即剩余的 223 个中断描述符的初始化; 调用 sched_init()函数设置时钟 TIMER_BH、TTY 设备 TQUEUE_BH和设备驱动程序 IMMEDIATE_BH 三类事件的中断处理函数。 调用 kernel_thread()函数通过 init 进程完成对设备的中断服务程序的登记,即把 I/O 设备的中断信息添加 irq_action 数
7、组中。 三、内核的中断处理机制 在 Linux 系统中,中断的处理是在内核中完成。为了提高处理的效率,Linux 将一个中断处理过程分为上半部(top half)和下半部(bottom half)。上半部处理由 I/O 设备发出的中断请求,把该中断请求设备的中断服务程序挂到该设备的下半部执行队列中,上半部必须及时完成,即中断处理过程不允许中断嵌套;下半部主要获取中断设备的中断信息,并执行中断服务程序,下半部允许中断嵌套。内核具体的实现机制如下: 1、Linux 的内核是通过查询数组 bh_base 来确定当前需要执行的任务,bh_base 的数组的每一项指向一个固定的内核例程处理程序,内核例程
8、处理程序通常要处理一个任务队列,中断服务程序即挂在任务队列上。 2、内存例程处理程序在执行前必须要先安装,安装的状态信息是由一个 32 位的整数 bh_mask 来表示,bh_mask 的每一位对应 bh_base 数组的一项,若 i 位为 1 则表示 bh_basei已安装。 3、内存例程处理程序在执行前还需激活,当前激活的状态是由一个32 位的整数 bh_activ 表示,bh_activ 每一位对应 bh_base 的一项,若 i 位为 1 则表示 bh_basei指向的内核例程处理程序需要激活。 四、Linux 系统的中断处理过程: 中断处理过程划分为中断响应、中断服务及中断返回三个过
9、程。其中,中断响应由硬件自动完成,而对中断服务程序的管理及具体执行过程与操作系统有关。Linux 中中断处理的基本思路如下: 中断具体处理过程如下: 1、CPU 响应中断 I/O 设备根据自已的状态产生中断请求信号 IRQ,若该信号未被8259A 的屏蔽掉,则中断控制器将把该信号将转换为向 CPU 发出的中断请求信号 INT;同时,当 CPU 处于开中断,则 CPU 在接收到 INT 信号后,将在指令的最后一个时钟周期发出 INTA 响应信号,然后关中断。8259A 接收到 INTA 信号后,会自动将中断请求设备的中断向量 n 送到总线。 CPU 根据中断描述表寄存器和中断向量 n 获得该中断
10、设备的中断描述符, CPU 从描述符中获得中断服务程序的入口地址然后自动转入函数IRQn_interrupt。 2、IRQn_interrupt 该函数将保存中断向量,并转入公共中断服务的程序common_interrupt。 3、common_interrupt 该程序是一个汇编语言程序: SAVE_ALL / 保存内核寄存器,转入系统态 call do_IRQ jmp ret_from_intr/ 返回内核 4、do_IRQ (1)do_IRQ void do_IRQ(struct pt_regs regs) int irq = regs.orig_eax irq_desc desc =
11、irq_desc + irq;/获取中断描述符 if (!(status /开中断,如果不是快速中断,就允许中断 else _cli(); /关中断 action-handler(irq, action-dev_id, regs);/可能执行mark_bh()注上激活标记 action = action-next; while (action); _cli(); 函数的主要任务是:依次执行 IRQn 线上所有的中断服务程序;通过mark_bh()函数给被中断的内核队列注上激活标记,以便在下半部完成对该队列的处理。 (4)do_bottom_half() 进入下半部,所有的中断服务程序都是在下半
12、部被执行。 _sti();/ 开中断,下半部允许中断嵌套 run_bottom_halves(); _cli(); (5)run_bottom_halves() active = get_active_bhs(); / 取得激活的下半部 clear_active_bhs(active); / 清除激活的下半部 bh = bh_base; do if (active /依次执行队列中的例程,包括所有的中断服务程序 bh+;active = 1; while (active); 当所有的队列被执行,就意味着当前所有的中断请求已被处理。 参考文献 1、陈莉君, Linux 操作系统内核分析 ,机械工业出版社 2、李善平, Linux 内核 2.4 版源代码分析大全 ,机械工业出版社 3、周明德, 微机原理及其应用 ,清华大学出版社