IDT本质是在kernel中的一个数组,是中断向量id到中断描述符(也即中断服务程序起始地址)的映射。
我画了一个图,图解IDT处理流程。
其中trap_dispatch()分配以后就是中断的下半部。
TRAPHANDLER_NOEC(__idt_divide,T_DIVIDE) TRAPHANDLER_NOEC(__idt_debug,T_DEBUG) TRAPHANDLER_NOEC(__idt_nmi,T_NMI) TRAPHANDLER_NOEC(__idt_breakpoint,T_BRKPT) TRAPHANDLER_NOEC(__idt_overflow,T_OFLOW) TRAPHANDLER_NOEC(__idt_bound,T_BOUND) TRAPHANDLER_NOEC(__idt_illop,T_ILLOP) TRAPHANDLER_NOEC(__idt_device,T_DEVICE) TRAPHANDLER(__idt_dblflt,T_DBLFLT) TRAPHANDLER(__idt_tss,T_TSS) TRAPHANDLER(__idt_segnp,T_SEGNP) TRAPHANDLER(__idt_stack,T_STACK) TRAPHANDLER(__idt_gpflt,T_GPFLT) TRAPHANDLER(__idt_pgflt,T_PGFLT) TRAPHANDLER_NOEC(__idt_fperr,T_FPERR) TRAPHANDLER(__idt_align,T_ALIGN) TRAPHANDLER_NOEC(__idt_mchk,T_MCHK)//MCE TRAPHANDLER_NOEC(__idt_simd,T_SIMDERR) TRAPHANDLER_NOEC(__idt_syscall,T_SYSCALL) TRAPHANDLER(__idt_default,T_DEFAULT) TRAPHANDLER_NOEC(__idt_irq0,IRQ_OFFSET+ IRQ_TIMER) TRAPHANDLER_NOEC(__idt_irq1,IRQ_OFFSET+ IRQ_KBD) TRAPHANDLER_NOEC(__idt_irq2,IRQ_OFFSET+ 2) TRAPHANDLER_NOEC(__idt_irq3,IRQ_OFFSET+ 3) TRAPHANDLER_NOEC(__idt_irq4,IRQ_OFFSET+ IRQ_SERIAL) TRAPHANDLER_NOEC(__idt_irq5,IRQ_OFFSET+ 5) TRAPHANDLER_NOEC(__idt_irq6,IRQ_OFFSET+ 6) TRAPHANDLER_NOEC(__idt_irq7,IRQ_OFFSET+ IRQ_SPURIOUS) TRAPHANDLER_NOEC(__idt_irq8,IRQ_OFFSET+ 8) TRAPHANDLER_NOEC(__idt_irq9,IRQ_OFFSET+ 9) TRAPHANDLER_NOEC(__idt_irq10,IRQ_OFFSET+ 10) TRAPHANDLER_NOEC(__idt_irq11,IRQ_OFFSET+ 11) TRAPHANDLER_NOEC(__idt_irq12,IRQ_OFFSET+ 12) TRAPHANDLER_NOEC(__idt_irq13,IRQ_OFFSET+ 13) TRAPHANDLER_NOEC(__idt_irq14,IRQ_OFFSET+ IRQ_IDE) TRAPHANDLER_NOEC(__idt_irq19,IRQ_OFFSET+ IRQ_ERROR) /* * Lab 3: Your code here for _alltraps */ _alltraps: pushl %ds pushl %es pushal movw $GD_KD, %ax movw %ax,%ds movw %ax,%es pushl %esp call trap void trap_init(void) { extern struct Segdesc gdt[]; // LAB 3: Your code here. extern void __idt_divide(); extern void __idt_debug(); extern void __idt_nmi(); extern void __idt_breakpoint(); extern void __idt_overflow(); extern void __idt_bound(); extern void __idt_illop(); extern void __idt_device(); extern void __idt_dblflt();//double fault extern void __idt_tss(); extern void __idt_segnp(); extern void __idt_stack(); extern void __idt_gpflt(); extern void __idt_pgflt(); extern void __idt_fperr(); extern void __idt_align(); extern void __idt_mchk(); extern void __idt_simd(); extern void __idt_syscall(); extern void __idt_default(); extern void __idt_irq0(); extern void __idt_irq1(); extern void __idt_irq2(); extern void __idt_irq3(); extern void __idt_irq4(); extern void __idt_irq5(); extern void __idt_irq6(); extern void __idt_irq7(); extern void __idt_irq8(); extern void __idt_irq9(); extern void __idt_irq10(); extern void __idt_irq11(); extern void __idt_irq12(); extern void __idt_irq13(); extern void __idt_irq14(); int i = 0; SETGATE(idt[0],0,GD_KT,__idt_default,0); for(i=1;i<255;i++) { idt[i] = idt[0]; } SETGATE(idt[T_DIVIDE],1,GD_KT,__idt_divide,0); SETGATE(idt[T_DEBUG],0,GD_KT,__idt_debug,0); SETGATE(idt[T_NMI],0,GD_KT,__idt_nmi,0); SETGATE(idt[T_BRKPT],1,GD_KT,__idt_breakpoint,3); SETGATE(idt[T_OFLOW],1,GD_KT,__idt_overflow,0); SETGATE(idt[T_BOUND],1,GD_KT,__idt_bound,0); SETGATE(idt[T_ILLOP],1,GD_KT,__idt_illop,0); SETGATE(idt[T_DEVICE],1,GD_KT,__idt_device,0); SETGATE(idt[T_DBLFLT],1,GD_KT,__idt_dblflt,0); SETGATE(idt[T_TSS],1,GD_KT,__idt_tss,0); SETGATE(idt[T_SEGNP],1,GD_KT,__idt_segnp,0); SETGATE(idt[T_STACK],1,GD_KT,__idt_stack,0); SETGATE(idt[T_GPFLT],1,GD_KT,__idt_gpflt,0); SETGATE(idt[T_PGFLT],1,GD_KT,__idt_pgflt,0); SETGATE(idt[T_FPERR],1,GD_KT,__idt_fperr,0); SETGATE(idt[T_ALIGN],1,GD_KT,__idt_align,0); //SETGATE(idt[T_DIVIDE],1,GD_KT,__idt_divide,0); SETGATE(idt[T_MCHK],1,GD_KT,__idt_mchk,0); SETGATE(idt[T_SIMDERR],1,GD_KT,__idt_simd,0); SETGATE(idt[T_SYSCALL],0,GD_KT,__idt_syscall,3); SETGATE(idt[IRQ_OFFSET],0,GD_KT,__idt_irq0,0); SETGATE(idt[IRQ_OFFSET+1],0,GD_KT,__idt_irq1,0); SETGATE(idt[IRQ_OFFSET+2],0,GD_KT,__idt_irq2,0); SETGATE(idt[IRQ_OFFSET+3],0,GD_KT,__idt_irq3,0); SETGATE(idt[IRQ_OFFSET+4],0,GD_KT,__idt_irq4,0); SETGATE(idt[IRQ_OFFSET+5],0,GD_KT,__idt_irq5,0); SETGATE(idt[IRQ_OFFSET+6],0,GD_KT,__idt_irq6,0); SETGATE(idt[IRQ_OFFSET+7],0,GD_KT,__idt_irq7,0); SETGATE(idt[IRQ_OFFSET+8],0,GD_KT,__idt_irq8,0); SETGATE(idt[IRQ_OFFSET+9],0,GD_KT,__idt_irq9,0); SETGATE(idt[IRQ_OFFSET+10],0,GD_KT,__idt_irq10,0); SETGATE(idt[IRQ_OFFSET+11],0,GD_KT,__idt_irq11,0); SETGATE(idt[IRQ_OFFSET+12],0,GD_KT,__idt_irq12,0); SETGATE(idt[IRQ_OFFSET+13],0,GD_KT,__idt_irq13,0); SETGATE(idt[IRQ_OFFSET+14],0,GD_KT,__idt_irq14,0); // Per-CPU setup trap_init_percpu(); }
首先,我们利用TRAPHANDLER和TRAPHANDLER_NOEC宏来实现handler。
这两个宏所生成的代码的作用是,根据传入参数定义handler的“标签”,然后进行一些压栈操作。压什么东西?
中断发生后,CPU自动切换到一个新的栈,然后自动地将当前运行程序的EFLAGS寄存器压入栈中,然后压入CS和IP,然后对于一些特殊的异常CPU还会压入error code,然后才会转到中断服务程序的第一条指令——这第一条指令就是在handler中。
然后,handler就开始压栈了。为了保证Trapframe结构的一致性和完整性,对于CPU没有压入error code的情况,TRAPHANDLER_NOEC定义的handler首先压入一个0用于占位。 接着,TRAPHANDLER和TRAPHANDLER_NOEC都压入了trap number。
最后,两者都跳转到_alltraps,这是每个handler都一样的代码,它的作用依然是继续补充Trapframe结构,注意压栈顺序要和Trapframe结构中的顺序一致。我们稍后就会发现,经过上述一系列操作之后,一个完整的Trapframe结构诞生了,也就意味着保存现场的工作完成