MIT6.828LAB3 (2)

LAB3_Part A User Environments and Exception Handling


前言

记录一下自己的学习过程
实验内容翻译:
https://gitee.com/cherrydance/mit6.828
该翻译仅供参考

练习3

练习3:如果你还没有阅读过,请阅读80386程序员手册中的第9章“异常和中断”(或者IA-32开发者手册中的第5章)。这些章节详细介绍了x86体系结构中的异常和中断机制。

自行阅读

练习4

请编辑trapentry.S和trap.c文件,并实现上述描述的功能。trapentry.S中的宏TRAPHANDLER和TRAPHANDLER_NOEC以及inc/trap.h中的T_*定义将对您有所帮助。您需要为inc/trap.h中定义的每个异常在trapentry.S中添加一个入口点(使用这些宏),并提供_alltraps函数,以供TRAPHANDLER宏引用。您还需要修改trap_init()函数,将IDT初始化为指向在trapentry.S中定义的每个入口点;在这里,SETGATE宏将很有帮助。
您的_alltraps函数应该执行以下操作:
推送值使堆栈看起来像一个struct Trapframe结构体
将GD_KD加载到%ds和%es寄存器
使用pushl %esp将struct Trapframe的指针作为参数传递给trap()
调用trap()函数(trap()函数是否会返回?)
考虑使用pushal指令;它与struct Trapframe的布局非常匹配
使用位于用户目录中的一些测试程序对您的异常处理代码进行测试,这些测试程序在进行任何系统调用之前会导致异常,例如user/divzero。此时,您应该能够成功通过make grade命令对divzero、softint和badsegment测试进行评分

/* TRAPHANDLER defines a globally-visible function for handling a trap.
 * It pushes a trap number onto the stack, then jumps to _alltraps.
 * Use TRAPHANDLER for traps where the CPU automatically pushes an error code.
 *
 * You shouldn't call a TRAPHANDLER function from C, but you may
 * need to _declare_ one in C (for instance, to get a function pointer
 * during IDT setup).  You can declare the function with
 *   void NAME();
 * where NAME is the argument passed to TRAPHANDLER.
 */

意思是它将陷阱编号推入栈中,然后跳转到_alltraps。结合 IA-32 Developer’s Manual中的表:在这里插入图片描述
得到如下代码:

TRAPHANDLER_NOEC(t_divide, T_DIVIDE)
TRAPHANDLER_NOEC(t_debug, T_DEBUG)
TRAPHANDLER_NOEC(t_nmi, T_NMI)
TRAPHANDLER_NOEC(t_brkpt, T_BRKPT)
TRAPHANDLER_NOEC(t_oflow, T_OFLOW)
TRAPHANDLER_NOEC(t_bound, T_BOUND)
TRAPHANDLER_NOEC(t_illop, T_ILLOP)
TRAPHANDLER_NOEC(t_device, T_DEVICE)
TRAPHANDLER(t_dblflt, T_DBLFLT)
TRAPHANDLER(t_tss, T_TSS)
TRAPHANDLER(t_segnp, T_SEGNP)
TRAPHANDLER(t_stack, T_STACK)
TRAPHANDLER(t_gpflt, T_GPFLT)
TRAPHANDLER(t_pgflt, T_PGFLT)
TRAPHANDLER_NOEC(t_fperr, T_FPERR)
TRAPHANDLER(t_align, T_ALIGN)

接下来是_alltraps函数,这个函数我只能说不会写,虽然练习将要做的步骤都写出来了,但是真不会。。。。下面是抄的代码,尽力解释一下。

_alltraps:
	/*首先是顺序,结构体中数据从上到下是低地址到高地址,栈是高地址到低地址的,
	所以要反过来进行压栈。又结构体注释说明了最下面的是内核,中间是硬件,最上面的才是
	我们需要压入栈中的。在调用函数前已经把错误码压入栈了。所以这里是把ds es寄存器压入栈
	而第一个参数是struct PushRegs给出注释是可以使用pusha来入栈。
	*/
    pushl %ds
    pushl %es
    pushal
    /*将GD_KD加载到%ds和%es寄存器*/
    pushl $GD_KD
    popl %ds
    pushl $GD_KD
    popl %es
    /*压入trap()的参数tf,%esp指向Trapframe结构的起始地址*/
    pushl %esp  
    call trap

解下来是完成trap_init函数,在练习中提到了宏SETGATE会很有作用,所以我们看一下这个宏:

#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;		\
}

他的作用是设置一个普通的中断或者陷阱描述符。trap_init就是使用该宏来初始化。
所以需要先得到几个参数的含义

gate:门描述符
istrap: 一个布尔值,表示门描述符的类型。
如果为1,表示门描述符是一个陷阱门(Trap Gate);
如果为0,表示门描述符是一个中断门(Interrupt Gate)。
sel:该选择子指向中断处理程序所在的代码段。
off: 一个32位的偏移量,表示中断处理程序入口地址相对于代码段起始地址的偏移量。
dpl: 一个表示特权级别(Descriptor Privilege Level)的值。它指定了中断处理程序的特权级别。0表示最高特权级别,3表示最低特权级别。

因为第四个参数是指向中断处理程序,因此我们需要先声明这些函数:

void t_divide();
void t_debug();
void t_nmi();
void t_brkpt();
void t_oflow();
void t_bound();
void t_illop();
void t_device();
void t_dblflt();
void t_tss();
void t_segnp();
void t_stack();
void t_gpflt();
void t_pgflt();
void t_fperr();
void t_align();

在trap_init函数中:

	SETGATE(idt[T_DIVIDE], 0, GD_KT, t_divide, 0);
    SETGATE(idt[T_DEBUG], 0, GD_KT, t_debug, 0);
    SETGATE(idt[T_NMI], 0, GD_KT, t_nmi, 0);
    SETGATE(idt[T_BRKPT], 0, GD_KT, t_brkpt, 3);
    SETGATE(idt[T_OFLOW], 0, GD_KT, t_oflow, 0);
    SETGATE(idt[T_BOUND], 0, GD_KT, t_bound, 0);
    SETGATE(idt[T_ILLOP], 0, GD_KT, t_illop, 0);
    SETGATE(idt[T_DEVICE], 0, GD_KT, t_device, 0);
    SETGATE(idt[T_DBLFLT], 0, GD_KT, t_dblflt, 0);
    SETGATE(idt[T_TSS], 0, GD_KT, t_tss, 0);
    SETGATE(idt[T_SEGNP], 0, GD_KT, t_segnp, 0);
    SETGATE(idt[T_STACK], 0, GD_KT, t_stack, 0);
    SETGATE(idt[T_GPFLT], 0, GD_KT, t_gpflt, 0);
    SETGATE(idt[T_PGFLT], 0, GD_KT, t_pgflt, 0);
    SETGATE(idt[T_FPERR], 0, GD_KT, t_fperr, 0);
    SETGATE(idt[T_ALIGN], 0, GD_KT, t_align, 0);

运行结果:
在这里插入图片描述

每个异常/中断都有一个单独的处理程序函数的目的是什么?(即,如果所有异常/中断都传递到同一个处理程序,当前实现中存在的哪个功能无法提供?)

因为不同的中断需要使用不同的处理方式。

您是否需要采取任何措施使user/softint程序正确运行?grade脚本希望它产生一个通用保护错误(trap 13),但softint的代码中是int $14。为什么这会产生中断向量13?如果内核实际上允许softint的int $14指令调用内核的页面故障处理程序(中断向量14),会发生什么?

因为程序是运行在用户态的,当前特权级为3,而int指令是系统指令,需要运行在内核态。内核态的特权级为0。因此会引发trap13 的generation protection(通用保护异常)。用户态代码越权。

总结

完成了lab3 的partA。对于中断有一定的了解,但是还很浅薄。
继续加油!!!!!

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值