首页 > 技术文章 > MIT 6.828 Lab 03:User Environments ( Part B )

cindycindy 2020-08-18 15:29 原文

PartB :Page Faults, Breakpoints Exceptions, and System Calls





Handling Page Faults

缺页中断中断号是14,发生时引发缺页中断的线性地址将会被存储到CR2寄存器中。

Exercise 05

修改trap_dispatch(),将页错误分配给page_fault_handler()处理。在trap_dispatch()添加如下代码:

static void
trap_dispatch(struct Trapframe *tf)
{
	// Handle processor exceptions.
	// LAB 3: Your code here.
	if(tf->tf_trapno == T_PGFLT)
	{
	    page_fault_handler(tf);
	    return;
	}

	// Unexpected trap: The user process or the kernel has a bug.
	print_trapframe(tf);
	if (tf->tf_cs == GD_KT)
		panic("unhandled trap in kernel");
	else {
		env_destroy(curenv);
		return;
	}
}

Exercise 06

修改trap_dispatch(),使得当断点异常发生时调用内核的monitor。在trap_dispatch()继续添加如下代码:

	if(tf->tf_trapo == T_BRKPT)
	{
	    monitor(tf);
	    return;
	}

Question

  1. The break point test case will either generate a break point exception or a general protection fault depending on how you initialized the break point entry in the IDT (i.e., your call to SETGATE from trap_init). Why? How do you need to set it up in order to get the breakpoint exception to work as specified above and what incorrect setup would cause it to trigger a general protection fault? 【断点测试例子中,产生断点异常还是通用保护错误取决于我们如何初始化断点异常的IDT项。为什么?】

如果设置其DPL为0,则会产生GPF,因为用户程序跳转执行内核态程序。如果我们想要当前执行的程序能够跳转到这个描述符所指向的程序哪里继续执行的话,有个要求,就是要求当前运行程序的CPL,RPL的最大值需要小于等于DPL,否则就会出现优先级低的代码试图去访问优先级高的代码的情况,就会触发general protection exception。此处,如果设置 break pointDPL = 0 则会引发权限错误,由于这里设置的 DPL = 3 ,所以会引发断点。

  1. What do you think is the point of these mechanisms, particularly in light of what the user/softint test program does?【尤其考虑到user/softint测试程序,你认为这些机制的关键点是什么?】

DPL的设置,可以限制用户态对关键指令的使用,有效地防止了一些程序恶意任意调用指令,引发一些危险的错误。

System calls

注意硬件不能产生int 0x30中断,需要程序自行产生此中断,并且没有二义性。

应用程序通过寄存器传递系统调用号和系统调用参数。 这样,内核就不需要在用户环境的堆栈或指令流中获取参数。系统调用号将存放在%eax中,参数(最多五个)将分别位于%edx,%ecx,%ebx,%edi和%esi中。内核通过%eax传递返回值。

inc/syscall.c片段
    
asm volatile("int %1\n"
             : "=a" (ret)
             : "i" (T_SYSCALL),
               "a" (num),
               "d" (a1),
               "c" (a2),
               "b" (a3),
               "D" (a4),
               "S" (a5)
                 : "cc", "memory");

"volatile"表示编译器不要优化代码,后面的指令 保留原样,其中"=a"表示"ret"是输出操作数; "i"=立即数;
最后一个子句告诉汇编器这可能会改变条件代码和任意内存位置。memory强制gcc编译器假设所有内存单元均被汇编指令修改,这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu又将registers,cache中的数据用于去优化指令,而避免去访问内存。

Exercise 07

需要我们做如下几件事:

  1. 为中断号T_SYSCALL添加一个中断处理函数

  2. 在trap_dispatch()中判断中断号如果是T_SYSCALL,调用定义在kern/syscall.c中的syscall()函数,并将syscall()保存的返回值保存到tf->tf_regs.reg_eax等将来恢复到eax寄存器中。

推荐阅读