以下以图说明:
图所示的结构对应idt_table数组中的一个表项,也就是一个idt_desc类型的数据。上面说的idt_struct->a对应0到31共32位,idt_struct->b对应32到63共32位。其中16-31共16位是中断处理程序所在的段选择符,0-15位和48-64位组合起来形成32位偏移量,也就是中断处理程序所在段(由16-31位给出)的段内偏移。40-43位共4位表示描述符的类型,45-46两位标识描述符的访问特权级(DPL,Descriptor Privilege Level),47位标识段是否在内存中。如果为1则表示段当前不再内存中。此处对每一个数据项的初始化为:
32到47位就是0x8E00,其中43到40位为1110,表明此次对idt表的初始化是将其全部初始化为中断门描述符,相应的如果是0101则是任务门描述符,1111表示时陷阱门描述符。
47位为1。
要说明的是ignore_int()中断处理程序实际上是一个“空”处理。随后对IDT再次进行初始化的时候,会使其最终初始化为有效的处理程序,也就是在start_kernel()函数中。当系统跳转到start_kernel()函数时,内核才进行真正的初始化。其中调用trap_init()函数对IDT进行最终初始化。调用set_trap_gate()将其初始化为陷阱门,调用set_intr_gate()将其初始化为中断门,调用set_system_gate()将其初始化为访问特权级为3的陷阱门,调用set_task_gate()将其初始化为任务门,调用set_system_intr_gate()将其初始化为访问特权级为3的中断门。
除了set_task_gate(),其它4个函数的接口都一样,行如:
void set_xxx_gate(unsigned int n,void *addr)
{
_set_gate(n,DESCTYPE_XXX,addr,__KERNEL_CS);
}
这样的形式,其中参数n给出要初始化的IDT表项(idt_table数组的下标),addr给出对应的处理程序的地址。在这些函数的内部均是调用_set_gate()。下面就看看_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);
}
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);
}
#define write_idt_entry(dt, entry, a, b) write_dt_entry(dt, entry, a, b)
static inline void write_dt_entry(struct desc_struct *dt,
int entry, u32 entry_low, u32 entry_high)
{
dt[entry].a = entry_low;
dt[entry].b = entry_high;
}
以上函数均摘自include/asm-i386/desc.h。这些代码的功能和上面摘出的汇编的功能是一样的。首先看_set_gate()函数,参数gate和addr当然是set_xxx_gate()传给的中断号和处理程序的地址,type参数指明要将idt_table[gate]初始化为什么类型的描述符,这些类型被定义为:
