Linux内核中使用的门是在i386平台的门的基础上做了更进一步的细化后形成的,如下所示:
1、中断门:处理器中DPL被设置为0的中断门,不能用户态下对其进行访问。Linux下所有的(硬)中断处理程序都是在核心态下通过中断门来激活的。
2、系统门:处理器中DPL被设置为3的陷阱门,可以在用户态下对其进行访问。Linux的三种异常处理函数(中断号为4、5、128)都是通过系统门来调用的,即指令into、bound和int $0x80可以在用户态下被执行。
3、系统中断门:处理器中DPL被设置为3的中断门,可以在用户态下访问。Linux中中断向量号为3的中断(int3)是通过系统中断门来调用的,可在用户态下被执行。
4、陷阱门:处理器中DPL被设置成0的陷阱门,不能在用户态下进行访问。Linux中的大多数异常处理函数都是通过陷阱门被激活的。
5、任务门:处理器中DPL被设置成0的任务门,不能在用户态下进行访问。Linux中仅有“Double Fault”异常的处理函数是通过任务门进行调用的。
在系统引导的过程中,内核调用/arch/i386/kernel/traps.c文件中的trap_init函数来对中断进行初始化。该函数通过调用同一文件下的set_trap_gate等多个函数来对中断描述符进行初始化。
set_trap_gate等函数的本质是调用include/asm-i386/desc.h文件中的_set_gate函数来完成对中断描述符的初始化工作,该函数的代码如下:
static inline void _set_gate(int gate, unsigned int type, void *addr, unsigned short seg) { __u32 a, b; pack_gate(&a, &b, (unsigned long)addr, seg, type, 0); write_idt_entry(idt_table, gate, a, b); } |
其中,pack_gate函数的代码如下:
static inline void pack_gate(__u32 *a, __u32 *b, unsigned long base, unsigned short seg, unsigned char type, unsigned char flags) { *a = (seg << 16) | (base & 0xffff); *b = (base & 0xffff0000) | ((type & 0xff) << 8) | (flags & 0xff); } |
write_idt_entry宏展开后代码如下:
#define write_idt_entry(dt, entry, a, b) write_dt_entry(dt, entry, a, b)
static inline void write_dt_entry(void *dt, int entry, __u32 entry_a, __u32 entry_b) { __u32 *lp = (__u32 *)((char *)dt + entry*8); *lp = entry_a; *(lp+1) = entry_b; } |
其中,pack_gate函数的主要工作是设置相应的中断描述符,a为低32位,b为高32位。write_idt_entry的工作则是将在pack_gate函数中设置好的中断描述符填入到中断描述符表的相应位置。