我的GIS/CS学习笔记:https://github.com/yunwei37/ZJU-CS-GIS-ClassNotes <一个浙江大学本科生的计算机、地理信息科学知识库 >
中断控制是计算机发展中一种重要的技术,最初它是为克服对 I/O 接口控制采用程序查询所带来的处理器低效率而产生的。
本章主要讨论在 Intel i386 保护模式下中断机制在 Linux 中的实现。
实模式和保护模式最本质的差别就是在保护模式引入的中断描述符表。
中断向量:
Intel x86 系列微机共支持 256 种向量中断。
Linux 对 256 个向量的分配如下:
CPU 中增设了一个中断描述符表寄存器 IDTR,用来存放中断描述符表在内存的起始地址
相关汇编指令:
Linux 内核在系统的初始化阶段要进行大量的初始化工作,其与中断相关的工作有:
外部中断向量的设置:
8259A 中断控制器的初始化
void __init init_8259A(int auto_eoi)
{
unsigned long flags;
spin_lock_irqsave(&i8259A_lock, flags);
outb(0xff, 0x21); /* mask all of 8259A-1 */
outb(0xff, 0xA1); /* mask all of 8259A-2 */
/*
* outb_p - this has to work on a wide range of PC hardware.
*/
outb_p(0x11, 0x20); /* ICW1: select 8259A-1 init */
outb_p(0x20 + 0, 0x21); /* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */
outb_p(0x04, 0x21); /* 8259A-1 (the master) has a slave on IR2 */
if (auto_eoi)
outb_p(0x03, 0x21); /* master does Auto EOI */
else
outb_p(0x01, 0x21); /* master expects normal EOI */
outb_p(0x11, 0xA0); /* ICW1: select 8259A-2 init */
outb_p(0x20 + 8, 0xA1); /* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */
outb_p(0x02, 0xA1); /* 8259A-2 is a slave on master's IR2 */
outb_p(0x01, 0xA1); /* (slave's support for AEOI in flat mode
is to be investigated) */
if (auto_eoi)
/*
* in AEOI mode we just have to mask the interrupt
* when acking.
*/
i8259A_irq_type.ack = disable_8259A_irq;
else
i8259A_irq_type.ack = mask_and_ack_8259A;
udelay(100); /* wait for 8259A to initialize */
outb(cached_21, 0x21); /* restore master IRQ mask */
outb(cached_A1, 0xA1); /* restore slave IRQ mask */
spin_unlock_irqrestore(&i8259A_lock, flags);
}中断描述符表 IDT 的预初始化:
中断向量表的最终初始化:
在对中断描述符表进行预初始化后, 内核将在启用分页功能后对 IDT 进行第二遍初始化,也就是说,用实际的陷阱和中断处理程序替换这个空的处理程序。
Linux 利用异常来达到两个截然不同的目的:
内核对异常处理程序的调用有一个标准的结构,它由以下 3 部分组成:
进入异常处理程序的汇编指令在 arch/I386/kernel/entry.S 中.
中断请求队列的初始化:
让每个中断源都必须占用一条中断线是不现实的,在 Linux 设计中,专门为每个中断请求 IRQ 设置了一个队列,这就是我们所说的中断 请求队列。
对于每个 IRQ,Linux 都用一个 irq_desc_t 数据结构来描述,我们把它叫做 IRQ 描述符;
在 IDT 表初始化完成之初,每个中断服务队列还为空。因此,在设备驱动程序的初始化阶段,必须通过 request_irq() 函数将对应的中断服务例程挂入中断请求队列。其中主要语句就是对 setup_irq()函数的调用,该函数才是真正对中断请求队列进行初始化的函数。
本节要关心的主要内容是如何执行中断处理程序。
中断和异常处理的硬件处理:
Linux 对中断的处理:
Linux 把一个中断要执行的操作分为下面的 3 类:
所有的中断处理程序都执行 4 个基本的操作:
与堆栈有关的常量、数据结构及宏:
中断处理程序的执行:
假定外设的驱动程序都已完成了初始化工作,并且已把相应的中断服务例程挂入到特定的中断请求队列。又假定当前进程正在用户空间运行(随时可以接受中断),且外设已产生了一次中断请求。
从中断返回:
中断、异常及系统调用的返回是放在一起实现的:
内核的目标就是尽可能快地处理完中断请求,尽其所能把更多的处理向后推迟:内核把中断处理分为两部分:前半部分(top half)和后半部分(bottom half),前半部分内核立即执行,而后半部分留着稍后处理。