LAB3概述:
本次操作系统实验,我们对计算机的操作系统进行了初步的探究,通过完成作业和问题,对lab3部分有了较好的理解。Lab3主要实现能运行被保护的用户模式环境(protected user-mode environment,即process)的内核服务。我们将增加数据结构来记录进程、创建进程、为其装载一个程序镜像。我们还要让JOS 内核能够处理进程产生的系统调用和异常。具体来说:
第一部分主要完成用户环境和异常处理,我们主要先为我们的环境申请地址并进行映射,之后改写env.c中的相应函数来使得我们可以让系统进入用户空间执行我们想要的系统调用。接下来我们对处理中断和异常进行了探索,建立了我们自己的中断描述符表IDT,在trapentry.s和trap.c对产生中断的一开始过程深入了解。
第二部分处理缺页错误,断点异常以及系统调用。我们逐个对缺页中断、断点异常、系统调用等等的中断进行处理,通过对应参数调用不同的函数对相应的中断进行对应处理。我们在最后完成了用户进程的启动,以及对页错误和内存的保护。
此外,在完成这两大块内容的同时,对于问题的完成也使我们对于一些基础概念的理解,以及代码部分知识的掌握有了进一步的提升。
作业一
一.准备知识
Env数据结构代表一个进程描述符,定义在env.h中,包括进程的id,父进程的id,执行状态,该进程的寄存器状态,执行的次数等,并使用env_link指向下一个空闲的Env。
所有Env对象存储在envs数组中,该数组定义在env.c的开头。
除此之外curenv代表当前正在执行的进程,env_free_list指向空闲的进程描述符,组成链表,链表的添加与删除均在表头执行。
二.代码展示
修改mem_init() 使之能分配envs数组。这个数组是由NENV个Env结构体组成的。envs数组所在的这部分内存空间应该是用户模式只读的。
如题,类似lab2中分配pages数组,分配空间给数组envs同时初始化。
在lab2中分配pages数组时操作。
类似的分配env数组空间,并初始化
然后可以将虚拟内存的UENVS段映射到envs的物理地址
用到函数boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_tpa, int perm);
作业二
一.函数介绍
env_init(): 初始化所有的在envs数组中的 Env结构体,并把它们加入到env_free_list中。还要调用 env_init_percpu,这个函数要配置段式内存管理系统,让它所管理的段,可能具有两种访问优先级其中的一种,一个是内核运行时的0优先级,以及用户运行时的3优先级。
env_setup_vm(): 为一个新的用户环境分配一个页目录表,并且初始化这个用户环境的地址空间中的和内核相关的部分。
region_alloc(): 为用户环境分配物理地址空间。
load_icode(): 分析一个ELF文件,类似于bootloader做的那样,我们可以把它的内容加载到用户环境下。
env_create(): 利用env_alloc函数和load_icode函数,加载一个ELF文件到用户环境中。
env_run(): 在用户模式下,开始运行一个用户环境。
二.代码实现
1)env_init():进程描述符数组初始化
按照注释,遍历数组envs,要把它的id设为0,并且令envs中所有环境都是free状态,然后按序插入链表中。此时注意第一个被使用的是envs[0]。
2)env_setup_vm():初始化进程的虚拟空间。不同的进程有不同的虚拟地址空间,进而就必须有自己的页目录和页表,该函数的任务就是初始化页目录。
本题注释讲的很清楚,The VA space of all envs is identical (same)above UTOP。进程的页目录和内核的页目录(utop之上)可以共用,所以先分配一个空闲页,然后直接copy就可以,我们不需要做任何其他事情。接着增加分配的物理页的引用,将此页的虚拟地址赋值给进程的pgdir,然后使进程有权限操作自己的pgdir。
3)region_alloc():分配物理地址。把起始地址和终止地址对齐,然后以页为单位,一页一页分配内容,并且修改页目录表和页表。
为进程分配物理内存,映射到虚拟内存。round函数对起始地址和终止地址页对齐。
然后就可以以页单位给进程进行内寸分配了。然后修改页目录和页表
4)通过load_icode给相应的进程加载可执行代码。可执行代码的格式是elf格式。
5)env_creat():简单的函数,新建进程,调用env-alloc。然后利用load-icode读取elf。