5.4.4 Linux内核的中断处理
对于Linux内核来说,中断信号通常分为两类:硬件中断和软件中断(异常)。每个中断由0~255之间的一个数字来标识。对于中断int0~int31(0x00~0x1f),每个中断的功能由Intel公司固定设定或保留用, 属于软件中断,但Intel公司称之为异常。因为这些中断是在CPU执行指令时探测到异常情况而引起的。通常还可分为故障(Fault)和陷阱(traps)两类。中断int32~int255 (0x20~0xff)可以由用户自己设定。所有中断的分类以及执行后CPU的动作方式见表5-1。
表5-1 中断分类以及中断退出后CPU的处理方式
中断 | 名称 | CPU检测方式 | 处理方式 |
硬件 | Maskable可屏蔽中断 | CPU引脚INTR | 清标志寄存器eflags的IF标志可屏蔽中断 |
Nonmaskable不可屏蔽中断 | CPU引脚NMI | 不可屏蔽中断 | |
软件 | Fault错误 | 在错误发生之前检测到 | CPU重新执行引起错误的指令 |
Trap陷阱 | 在错误发生之后检测到 | CPU继续执行后面的指令 | |
Abort放弃(终止) | 在错误发生之后检测到 | 引起这种错误的程序应该被终止 |
在Linux系统中,将int32~int47(0x20~0x2f)对应于8259A中断控制芯片发出的硬件中断请求信号IRQ0~IRQ15(见表5-2),并把程序编程发出的系统调用(system call)中断设置为int128(0x80)。系统调用中断是用户程序使用操作系统资源的唯一界面接口。
表5-2 Linux系统中8259A芯片中断请求发出的中断号列表
中断请求号 | 中断号 | 用途 |
IRQ0 | 0x20 (32) | 8253发出的100Hz时钟中断 |
IRQ1 | 0x21 (33) | 键盘中断 |
IRQ2 | 0x22 (34) | 接连从芯片 |
IRQ3 | 0x23 (35) | 串行口2 |
IRQ4 | 0x24 (36) | 串行口1 |
IRQ5 | 0x25 (37) | 并行口2 |
IRQ6 | 0x26 (38) | 软盘驱动器 |
IRQ7 | 0x27 (39) | 并行口1 |
IRQ8 | 0x28 (40) | 实时钟中断 |
IRQ9 | 0x29 (41) | 保留 |
IRQ10 | 0x | 保留 |
IRQ11 | 0x2b (43) | 保留(网络接口) |
IRQ12 | 0x | PS/2鼠标口中断 |
IRQ13 | 0x2d (45) | 数学协处理器中断 |
IRQ14 | 0x2e (46) | 硬盘中断 |
IRQ15 | 0x | 保留 |
在系统初始化时,内核在head.s程序中首先使用一个哑中断向量(中断描述符)对中断描述符表(Interrupt Descriptor Table,IDT)中所有256个描述符进行了默认设置(boot/head.s,78)。这个哑中断向量指向一个默认的"无中断"处理过程(boot/head.s,150)。当发生了一个中断而又没有重新设置过该中断向量时就会显示信息"未知中断(Unknown interrupt)"。这里对所有256项都进行设置可以有效防止出现一般保护性错误(A gerneal protection fault)(异常13);否则,如果设置的IDT少于256项,那么在一个要求的中断所指定的描述符项大于设置的最大描述符项时,CPU就会产生一个一般保护出错(异常13)。另外,如果硬件出现问题而没有把设备的向量放到数据总线上,此时CPU通常会从数据总线上读入全1(0xff)作为向量,因此会去读取IDT表中的第256项,这样也会造成一般保护出错。对于系统中需要使用的一些中断,内核会在其继续初始化的处理过程中(init/main.c)重新设置这些中断的中断描述符项,让它们指向对应的实际处理过程。通常,异常中断处理过程(int0~int 31)都在traps.c的初始化函数中进行了重新设置(kernl/traps.c,第185行),而系统调用中断int128则在调度程序初始化函数中进行了重新设置(kernel/sched.c,第417行)。
另外,在设置中断描述符表IDT时Linux内核使用了中断门和陷阱门两种描述符。它们之间的区别在于对标志寄存器EFLAGS中的中断允许标志IF的影响。由中断门描述符执行的中断会复位IF标志,因此可以避免其他中断干扰当前中断的处理,随后的中断结束指令iret会从堆栈上恢复IF标志的原值;而通过陷阱门执行的中断则不会影响IF标志。参见第11章中对include/asm/system.h文件的说明
联系客服