JOS Lab3 User Environment Part B

Part B: Page Faults, Breakpoints Exceptions, and System Calls

现在kernel已经有了基本的异常处理能力,我们将继续完善它的功能.

处理Page Fault

Exercise 5 要求修改trap_dispatch()函数,把page fault错误派发给page_fault_handler()处理,这个处理函数现在还没有完成,只是一个入口。
这个Exercise比较简单:

static void
trap_dispatch(struct Trapframe *tf)
{
   
	// Handle processor exceptions.
	// LAB 3: Your code here.
	switch (tf->tf_trapno)
	{
   
	case T_PGFLT:
		page_fault_handler(tf);
		break;
	
	default:
		// 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);
		}
		break;
	}
	return;
}

后面会完善page_fault_handler,在实现系统调用的时候。

断点异常

断点异常作为中断向量3 (T_BRKPT)通常用来让调试器向程序代码中插入断点。一般会把相关的程序指令用一个特殊的1个byte长的int3软件中断指令代替。JOS中略微泛用了这个概念,让它变成了一个任意用户环境都可以用来唤醒JOS kernel监视器的伪系统调用。如果我们把JOS kernel监视器看作是调试器的话,这种泛用也是恰当的。
Exercise 6 要求修改trap_dispatch让断点异常唤醒kernel监视器。
这要改两个地方,一个是在switch中添加一个入口:

	case T_BRKPT:
		monitor(tf);
		break;

另外一个是把IDT中对应的描述符的权限设成3,即用户可以引起断点异常中断。

    SETGATE(idt[T_BRKPT], 0, GD_KT, &th_brkpt, 3);

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?
    Answer: Depending on how you have set DPL parameter in the IDT gates. Setting the DPL as user level privilege will get it work and as kernel level won’t. Also accessing a gate contains a null segment selector or the segment selector does not point to a code segment will also cause General Protection Fault.

  2. What do you think is the point of these mechanisms, particularly in light of what the user/softint test program does?
    Answer: To restrict what user program could do and protect the kernel from being corrupted by user program.

系统调用

用户进程通过系统调用让kernel替它做一些它做不了的事情。当用户进程发起了一个系统调用,处理器和kernel一起保存用户进程状态,kernel执行代码实现系统调用,然后恢复用户进程现场。
JOS内核中,我们使用int指令引起一个处理器中断。特别地,我们使用int $0x30作为系统调用中断。注意中断0x30不会由硬件产生,所以让用户代码可以产生这个中断没有歧义。
用户进程通过寄存器传递系统调用需要的调用号和调用参数。这样kernel不用去翻找用户栈或者指令流。系统调用的调用号会保存在%eax寄存器中,最多五个调用参数则分别以此保存在%edx, %ecx, %ebx, %edi, %esi寄存器之中。kernel把系统调用的返回值保存在%eax寄存器中。唤起系统调用的代码已经在lib/syscall.c中的syscall()函数中为你写好了,仔细阅读搞明白它在做些什么。
Exercise 7 为系统调用的中断向量T_SYSCALL添加一个中断处理程序。需要编辑kern/trapentry.Skern/trap.c文件,还需要修改trap_dispatch()通过唤起syscall()函数处理系统调用中断,并且安排返回值从%eax寄存器中传回给用户进程。最后需要实现kern/syscall.c中的syscall()函数。
补充syscall如下:

// Dispatches to the correct kernel function, passing the arguments.
int32_t
syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
{
   
	// Call the function corresponding to the 'syscallno' parameter.
	// Return any appropriate return value.
	// LAB 3: Your code here.


	switch (syscallno) {
   
	case SYS_cgetc:
		return sys_cgetc();
	case SYS_cputs:
		sys_cputs((const char*)a1, a2);
		return 0;
	case SYS_env_destroy:
		return sys_env_destroy(a1);
	case SYS_getenvid:
		return sys_getenvid();
	default:
		return -E_INVAL;
	}
	
}

trapentry.S中添加:

	TRAPHANDLER_NOEC(th_syscall, T_SYSCALL)

trap.c中添加:

	void th_syscall();
	SETGATE(idt[T_SYSCALL], 0, GD_KT, &th_syscall, 3);

以及

	case T_SYSCALL:
		tf->tf_regs.reg_eax = syscall(tf->tf_regs.reg_eax,tf->tf_regs.reg_edx,
		tf->tf_regs.reg_ecx,tf
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值