一、中断描述符表的初始化
从操作系统角度描述中断描述符表的初始化。
Linux内核在系统的初始化阶段需要进行大量的初始化工作,与中断相关的工作有:初始化可编程控制器(中断控制器)8259A;将中断描述符表的起始地址载入IDTR寄存器,并初始化表中的每一项。
用户进程通过INT指令发出一个中断请求,其中断请求向量在0~255之间。为防止用户使用INT指令模拟非法的中断和异常,必须对中断描述符表进行谨慎的初始化。其措施之一是将中断门或陷阱门中的请求特权级DPL域置为0。如果用户进程发出这样一个中断请求,CPU会检查出其当前特权级CPL(3)与所请求的特权级DPL(0)冲突,产生一个通用保护异常。
但有时必须让用户进程能够使用内核提供的功能(系统调用),从用户态进入内核态,通过把中断门或陷阱门的DPL域置为3。
当计算机运行在实模式时,中断描述符表被初始化,由BIOS使用。一旦真正进入Linux内核,中断描述符表被移到内存的另一个区域,并为进入保护模式进行预初始化:用LIDT指令对中断描述符表寄存器IDTR初始化,即把IDTR置为0。把中断描述符表IDT的起始地址载入IDTR。
用setup_idt()填充中断描述符表中的256个表项。填充这个表时,使用一个空的中断处理程序。理由因为处于初始化阶段,没有任何中断处理程序,因此,用空的中断处理程序填充每个表项。
对中断描述符表预初始化后,内核将在启用分页功能后对IDT进行第二遍初始化,即用实际的陷阱和中断处理程序替换这个空的处理程序。一旦这个过程完成,对于每个异常,IDT都有一个专门的陷阱门或系统门,对每个外部中断,IDT都包含专门的中断门。
1、IDT(中断描述符表)表项的设置(linux-2.4.x.x)
IDT表项的设置通过_set_gate()函数实现,如何调用_set_gate()函数在IDT表中插入一个门。
代码路径:arch/i386/kernel/traps.c(x86体系架构)
#define _set_gate(gate_addr,type,dpl,addr) \
do { \
int __d0, __d1; \
__asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
"movw %4,%%dx\n\t" \
"movl %%eax,%0\n\t" \
"movl %%edx,%1" \
:"=m" (*((long *) (gate_addr))), \
"=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \
:"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
"3" ((char *) (addr)),"2" (__KERNEL_CS << 16)); \
} while (0)