mit6.828:lab3心得
lab3中断处理
中断发生时栈的变化
中断发生时,硬件部分的工作:切换栈,压入相关寄存器。
图1 栈结构的变化
Ref:https://pdos.csail.mit.edu/6.828/2018/readings/i386/s09_06.htm
如图1所示,根据中断发生时上下文的不同,栈结构的变化情况也不同。
以用户态到内核态的中断为例,说明本实验中中断发生时对应的情况。
- 用户态栈切换到内核栈。(内核栈怎么确定的呢?由curenv的TSS确定)
- 压入ss、esp、eflags、cs、eip
- 然后根据中断号索引中断描述符表,跳转到在kern/trapentry.S中对应的中断处理函数
- 压入错误码(如果有)以及中断号,
- 压入ds、es、相关寄存器值
- 接着跳转到kern/trap.c:trap(Trapframe * tf)
对应的过程如图2所示:
图2 中断时对栈的处理
kern/trapentry.S中定义了TRAPHANDLER_NOEC、TRAPHANDLER两个宏,根据中断是否由错误码完成对应的压栈操作。总而言之,目的是在内核栈中构造Trapframe结构,接着跳转到kern/trap.c:trap(Struct Trapframe *tf),执行后续的中断处理操作。
中断的返回
如果trap_dispatch(tf)顺利返回,则执行env_run(curenv),最终调用env_pop_tf(tf),最终通过iret指令恢复tf中保存的寄存器值,继续执行。
kern/env.c:env_run()
1 // 2 // Restores the register values in the Trapframe with the 'iret' instruction. 3 // This exits the kernel and starts executing some environment's code. 4 // 5 // This function does not return. 6 // 7 void 8 env_pop_tf(struct Trapframe *tf) 9 { 10 asm volatile( 11 "\tmovl %0,%%esp\n" 12 "\tpopal\n" 13 "\tpopl %%es\n" 14 "\tpopl %%ds\n" 15 "\taddl $0x8,%%esp\n" /* skip tf_trapno and tf_errcode */ 16 "\tiret\n" 17 : : "g" (tf) : "memory"); 18 panic("iret failed"); /* mostly to placate the compiler */ 19 }
中断描述符表的初始化
图3 中断描述符的结构
Ref:https://pdos.csail.mit.edu/6.828/2018/readings/i386/s09_05.htm
inc/mmu.h中的#define SETGATE(gate, istrap, sel, off, dpl)宏完成对中断描述符表的初始化。
inc/mmu.h
#define SETGATE(gate, istrap, sel, off, dpl) \ { \ (gate).gd_off_15_0 = (uint32_t) (off) & 0xffff; \ (gate).gd_sel = (sel); \ (gate).gd_args = 0; \ (gate).gd_rsv1 = 0; \ (gate).gd_type = (istrap) ? STS_TG32 : STS_IG32; \ (gate).gd_s = 0; \ (gate).gd_dpl = (dpl); \ (gate).gd_p = 1; \ (gate).gd_off_31_16 = (uint32_t) (off) >> 16; \ }
1 // Gate descriptors for interrupts and traps 2 struct Gatedesc { 3 unsigned gd_off_15_0 : 16; // low 16 bits of offset in segment 4 unsigned gd_sel : 16; // segment selector 5 unsigned gd_args : 5; // # args, 0 for interrupt/trap gates 6 unsigned gd_rsv1 : 3; // reserved(should be zero I guess) 7 unsigned gd_type : 4; // type(STS_{TG,IG32,TG32}) 8 unsigned gd_s : 1; // must be 0 (system) 9 unsigned gd_dpl : 2; // descriptor(meaning new) privilege level 10 unsigned gd_p : 1; // Present 11 unsigned gd_off_31_16 : 16; // high bits of offset in segment 12 };
在kern/trap.c的开头通过struct Gatedesc idt[256] = { { 0 } }定义了中断描述符数组,kern/trap.c:trap_init()中借助宏SETGATE完成对中断描述符表idt的初始化。
kern/trap.c:trap_init()
1 void t_syscall(); 2 SETGATE(idt[T_SYSCALL],1,GD_KT,t_syscall,3);
t_syscall()为kern/trapentry.S中通过宏定义的汇编形式的函数。
kern/trapentry.S
1 TRAPHANDLER_NOEC(t_syscall,T_SYSCALL)
通过t_syscall引用对应的函数的地址,作为中断描述符中的偏移地址。
关联关系如图4所示。
图4 中断描述符的初始化