shell 执行新程序的逻辑:fork -> exec
系统调用需要切换地址空间,这会耗费大量的时间,一些只读的数据可以放到共享空间然后让用户直接读取,从而避免系统调用
在用户地址空间加一个 USYSCALL 段:
我的思路:创建用户进程的方式为:exec,所以我们需要在exec中映射 USYSCALL 页。
进一步查看 exec 的代码后发现并不是改 exec 的代码,应该在 proc 结构体中添加 usyscall 结构体的指针,改 proc.c 的 proc_pagetable 以添加映射
别看 proc 结构体中的 trapframe 指针用 kalloc 分配,这也是虚拟地址,但可以认为是物理地址
内核地址空间和用户地址空间的页表不同
fork时不能复制原来的 usys_data
,这个点卡我了半天
从指定文件地址打开文件,读取ELF头,根据 magic 检查ELF,初始化新页表
根据程序段头 proghdr 加载 text 和 data 到内存中
分配两个页放 stack 和 guard 并 清空 guard 为0。
入栈字符串参数,将其地址存储在 ustack 中。末尾为0。然后入栈 ustack。argc 是返回值放到 a0,argv 放到 a1
设置更新进程的一些属性:pagetable、size、epc、sp,然后释放原进程的物理内存,返回 argc
另外,xv6是小端序,可以从这段入栈字符串的代码中看出:
——分级访问即可
riscv.h
需要注意一下类型转换
根据 va 获取 Lk 页表的页号:
常用权限位:
PTE_A 由硬件读写该PTE指向的数据时设置,由软件清除
这个实验感觉没啥意思,难度是 hard,但其实做起来半个小时都不到就可以做完
Your job is to implement pgaccess(), a system call that reports which pages have been accessed. The system call takes three arguments. First, it takes the starting virtual address of the first user page to check. Second, it takes the number of pages to check. Finally, it takes a user address to a buffer to store the results into a bitmask (a datastructure that uses one bit per page and where the first page corresponds to the least significant bit). You will receive full credit for this part of the lab if the pgaccess test case passes when running pgtbltest.
搞一个 PTE_A 宏,在系统调用中用argaddr等函数获取寄存器参数,根据提供的页指针,也就是vm获用 walk 模拟 MMU 获取 PTE,检查PTE_A,若设置则记录并清空PTE_A…循环结束后将结果复制到用户地址空间即可