欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

MIT6.828_Lab 3_User Environments_Part A

程序员文章站 2022-06-18 10:58:56
Lab 3: User Environments简介:本实验中将实现运行受保护的用户态环境(即“进程”)所需的基本内核功能。 将增强JOS内核,以设置数据结构来跟踪用户环境(进程),创建单个用户环境(进程),将程序映像加载到其中并开始运行。还将使JOS内核能够处理用户环境(进程)发出的任何系统调用并处理它引起的任何其他异常。注意:在本实验中,术语“环境”和“过程”是可互换的-两者均指允许您运行程序的抽象。 为了强调JOS环境和UNIX进程提供不同的接口,并且不提供相同的语义,我们引入术语“环境”...

Lab 3: User Environments

简介:
本实验中将实现运行受保护的用户态环境(即“进程”)所需的基本内核功能。 将增强JOS内核,以设
置数据结构来跟踪用户环境(进程),创建单个用户环境(进程),将程序映像加载到其中并开始运行。还
将使JOS内核能够处理用户环境(进程)发出的任何系统调用并处理它引起的任何其他异常。
注意:在本实验中,术语“环境”和“过程”是可互换的-两者均指允许您运行程序的抽象。 为了强调
JOS环境和UNIX进程提供不同的接口,并且不提供相同的语义,我们引入术语“环境”而不是传统的术
语“进程”。
预备工作:按课程给出的步骤切换到lab3分支,再将lab2合并
实验要求:与实验2一样,完成实验3中描述的所有常规练习以及至少一个挑战性问题。在lab目录顶层的一个名为Answers-lab3.txt的文件中,写下对实验室中提出的问题的简短答案,写下一两个段落的描述来说明是如何解决挑战性问题的。

Part A: User Environments and Exception Handling

课程中描述了一些管理用户环境(进程)的关键结构体,以及结构体中各元素的含义,值得一提的是在JOS
中,各个环境(进程)不像xv6中的进程那样具有自己的内核堆栈。一次在内核中只能有一个活动的JOS环境(进程),因此JOS只需要一个内核堆栈。

练习1:
修改kern / pmap.c中的mem_init()来分配和映射envs数组,与页面数组一样,应在合适的地方:UENVS(inc / memlayout.h)上以只读方式映射为用户,以便用户进程可以从该数组读取。
要求:运行你的代码,并保证check_kern_pgdir通过。
代码修改很简单,参考Lab2里写过的分配和映射Pages的代码就行了:
补充的代码:
MIT6.828_Lab 3_User Environments_Part AMIT6.828_Lab 3_User Environments_Part A
测试居然报错了:
MIT6.828_Lab 3_User Environments_Part A
那就调试一下:
调试代码:
MIT6.828_Lab 3_User Environments_Part A
调试结果:
MIT6.828_Lab 3_User Environments_Part A
可以看到memset之后,kern_pgdir变成0了,搞不清楚,上网查波资料,最后参考了
Salvete 的博客,原来链接器提供的
end并不准确,并非指向.bss段的末尾,按文章修改链接器脚本后,总算成功输出了:
MIT6.828_Lab 3_User Environments_Part A
练习2:补充env.c中未完成的函数
env_init():初始化Env结构体数组,并将它们加入env_free_list链表
MIT6.828_Lab 3_User Environments_Part A
env_setup_vm:为用户环境(进程)分配一张页目录,并初始化用户环境的地址空间
MIT6.828_Lab 3_User Environments_Part A
region_alloc():为一个环境(进程)分配大小为len的物理内存,并建立映射。
MIT6.828_Lab 3_User Environments_Part A
load_icode():解析ELF二进制文件镜像,将内容加载到新环境的地址空间中
MIT6.828_Lab 3_User Environments_Part A
env_create():创建一个用户环境(进程)
MIT6.828_Lab 3_User Environments_Part A
env_run():让给定的用户环境(进程)上处理机运行
MIT6.828_Lab 3_User Environments_Part A

完成代码补充后,make qemu,系统将进入用户态并执行hello 二进制文件,由于JOS尚未设置硬件以允许
从用户空间到内核的转换,所以二进制文件hello执行到使用int 指令进行系统调用时,会看到tripple fault错误信息,后面会解决这个问题,调试一下走一遍过程,首先使用make qemu-gdb并在env_pop_tf处设置GDB断点,然后si单步调试,在iret指令之后处理器应进入用户态:
iret之前-内核态:
MIT6.828_Lab 3_User Environments_Part A
iret之后-用户态:
MIT6.828_Lab 3_User Environments_Part A
按照课程提示在hello的sys_cputs的int $30命令处打上断点,该命令是一条系统调用,用于向控制台输
出字符
int $30命令
MIT6.828_Lab 3_User Environments_Part A
执行完这步后就发生tripple fault,无法执行下一条指令了,这是因为处理器变为用户态后,没办法转回
内核态,因此无法执行系统调用,我们需要实现对基本的异常及系统调用的处理,才能让内核从用户模式
下夺回处理器的控制权,首先得熟悉下x86中断和异常的机制。

练习3:
阅读Chapter 9 Exceptions and Interrupts
其他参考资料:详解struct Env 与 struct Trapframe
重点:9.5 IDT Descriptors, 9.6 Interrupt Tasks and Interrupt Procedures

练习4:
编辑trapentry.S和trap.c并实现上述功能。 trapentry.S中的TRAPHANDLER和TRAPHANDLER_NOEC宏以及inc / trap.h中的T_ *定义都将提供帮助。 需要为inc / trap.h中定义的每个陷阱在trapentry.S中添加一个入口点(使用这些宏),并且必须提供TRAPHANDLER宏所引用的_alltraps。 另外还需要修改trap_init()来初始化idt,以指向trapentry.S中定义的每个入口。 SETGATE宏(mmu.h)在这里会有所帮助。

_alltraps要点:
(1)入栈以使堆栈在布局上看起来像Trapframe
(2)将GD_KD加载到%ds和%es中
(3)入栈%esp来传递一个指向trapframe的指针作为trap函数的参数
(4)调用trap(trap可以返回吗?)
(5)考虑使用Pushal指令,该指令很适合Trapframe的布局。
(6)使用用户目录中的某些测试程序测试trap handling代码。

第一步:查看trapentry.S,mmu.h,trap.h,在trap.h中先根据trapentry.S中的注释声明函数,再参考9.10 Error Code Summary根据是否存在errcode加入入口指针。
声明:
MIT6.828_Lab 3_User Environments_Part A
添加入口:
MIT6.828_Lab 3_User Environments_Part A
第二步:根据上面的_alltraps要点,参考inc/trap.h中trapframe的布局及kern/env.c中env_pop_tf的代码。
trapframe:
MIT6.828_Lab 3_User Environments_Part Aenv_pop_tf:
MIT6.828_Lab 3_User Environments_Part A
可以发现入栈顺序和出栈顺序是相反的,栈顶到栈底(从低地址到高地址)应该与trapframe的布局完全一
致,入栈跟出栈反着来就行了,其余部分按上面的要点来,代码如下:
_alltraps:
MIT6.828_Lab 3_User Environments_Part A
第二步:补充trap.c中的trap_init函数,先看一下mmu.h中SETGATE宏函数:
MIT6.828_Lab 3_User Environments_Part A
函数功能:设置IDT中的中断描述符
函数参数:
gate:要设置的中断描述符项
istrap:该位为1表示该描述符为陷入门,为0表示为中断门,两者区别在于中断门会将IF位置0来阻止嵌套
中断的发生,而陷入门不会。
sel:中断/陷入处理程序的代码段选择子。
off:中断/陷入处理程序的代码段偏移量。
dpl:特权级别描述符,软件调用所需的特权等级

trap_init函数的主体部分就是调用SETGATE来初始化IDT表中各描述符,istrap参数参考9.9 Exception Summary,sel段参数是GD_KT,off为对应处理函数地址,dpl为特权级别描述符.
trap_init()代码如下:MIT6.828_Lab 3_User Environments_Part A
make grade测试通过:
MIT6.828_Lab 3_User Environments_Part A
问题1:为每个异常/中断设置单独的处理函数的目的是什么?
答:中断,异常的类型多种多样,有些在处理完后要返回用户程序继续执行(如缺页中断),有些在处理完后直接杀死用户程序(如除0异常),如果把所有中断/异常集中到一起处理,首先函数代码量会比较大,且要对不同情况做不同处理(如中断要关IF位而陷入不用,各个中断/异常要求的DPL不同),提高了编写,调试,维护代码的难度,其次也不方便中断函数的拓展。

问题2:怎么使用户/ softint程序正常运行? 评分脚本期望它会产生一般性的保护错误(trap 13),但softint的代码是int $ 14。 为什么会产生中断向量13? 如果内核允许softint的int $ 14指令调用内核的页面错误处理程序(即中断向量14),会发生什么?
答:softint是用户程序,特权级别为3,而页面错误处理程序要求的特权级别为0,低级别程序想要调用高级别中断处理程序的结果就是产生一个general protection fault,该问题的其他几问还没解决。

本文地址:https://blog.csdn.net/userXKk/article/details/107942673

相关标签: 操作系统