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 中断描述符的初始化



猜你喜欢

转载自www.cnblogs.com/ecjtusbs/p/12765041.html
今日推荐