Speed up system calls

根据hints查看kernel/proc.c中的函数proc_pagetable

// kernel/proc.c
// Create a user page table for a given process,
// with no user memory, but with trampoline pages.
pagetable_t
proc_pagetable(struct proc *p)
{
// map the trampoline code (for system call return)
// at the highest user virtual address.
// only the supervisor uses it, on the way
// to/from user space, so not PTE_U.
if(mappages(pagetable, TRAMPOLINE, PGSIZE,
(uint64)trampoline, PTE_R | PTE_X) < 0){
uvmfree(pagetable, 0);
return 0;
} // map the trapframe just below TRAMPOLINE, for trampoline.S.
if(mappages(pagetable, TRAPFRAME, PGSIZE,
(uint64)(p->trapframe), PTE_R | PTE_W) < 0){
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmfree(pagetable, 0);
return 0;
}
....
}

结合代码以及手册,USYSCALL页面的位置在heap之前,trapfram之后

only read 对应赋予PTE_R | PTE_U

// kernel/proc.c
...
// map the trapframe just below TRAMPOLINE, for trampoline.S.
if(mappages(pagetable, TRAPFRAME, PGSIZE,
(uint64)(p->trapframe), PTE_R | PTE_W) < 0){
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmfree(pagetable, 0);
return 0;
}
// map the usyscall just below TRAPFRAME
if (mappages(pagetable, USYSCALL, PGSIZE,
(uint64) (p->usyscall), PTE_R | PTE_U) < 0) {
uvmunmap(pagetable, USYSCALL, 1, 0);
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmfree(pagetable, 0);
return 0;
}
return pagetable;

分配和初始化页面

  • Don't forget to allocate and initialize the page in allocproc().

allocate函数中参考empty user page的方式依葫芦画瓢为usyscall分配空间

同时要记得将pid返回到用户态,这样才能在用户态直接使用pid

kernel/proc.hstruct proc中添加 struct usyscall* usyscall

  ....
// Allocate a USYSCALL page.
if((p->usyscall = (struct usyscall *)kalloc()) == 0) {
freeproc(p);
release(&p->lock);
return 0;
}
p->usyscall->pid = p->pid; // An empty user page table.
p->pagetable = proc_pagetable(p);
if(p->pagetable == 0){
freeproc(p);
release(&p->lock);
return 0;
}
...

解除映射的部分有两个函数要修改

  • Make sure to free the page in freeproc().

这里一定要释放,不然后面的test过不了

在函数proc_freepagetable添加代码

  // Free a process's page table, and free the
// physical memory it refers to.
void
proc_freepagetable(pagetable_t pagetable, uint64 sz)
{
// free USYSCALL
uvmunmap(pagetable, USYSCALL, 1, 0);
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmunmap(pagetable, TRAPFRAME, 1, 0);
uvmfree(pagetable, sz);
}

在函数freeproc添加代码

  static void
freeproc(struct proc *p)
{
if(p->trapframe)
kfree((void*)p->trapframe);
p->trapframe = 0;
if (p->usyscall)
kfree((void *) p->usyscall);
if(p->pagetable)
proc_freepagetable(p->pagetable, p->sz);
...
}

Print a page table

exec.c文件的return argc语句前插入if(p->pid==1) vmprint(p->pagetable)

参考freewalk

void
freewalk(pagetable_t pagetable)
{
// 一张页表由512个页表项(PTE)组成
for(int i = 0; i < 512; i++){
// 取出当前页表项
pte_t pte = pagetable[i];
// 当该条目有效,但却无法读、写、执行的时候
// 说明这是一条指向子页面的PTE,递归遍历子页面
if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){
// this PTE points to a lower-level page table.
uint64 child = PTE2PA(pte);
freewalk((pagetable_t)child);
pagetable[i] = 0;
} else if(pte & PTE_V){
panic("freewalk: leaf");
}
}
kfree((void*)pagetable);
}

我们的vmprint基本就是copy freewalk只不过不需要free页面

照着格式来

kernel/vm.c编写函数vmprint,别忘了在kernel/defs.h中声明

void
backtrace(pagetable_t pagetable, int level)
{
for(int i = 0; i < 512; i++){
pte_t pte = pagetable[i];
if ((pte & PTE_V) && (pte & (PTE_R | PTE_W | PTE_X)) == 0) {
uint64 child = PTE2PA(pte);
for (int j=0; j <= level; j++) {
printf("..");
if ((j+1) <= level) {
printf(" ");
}
}
printf("%d: pte %p pa %p\n", i, pte, child);
backtrace((pagetable_t)child, level+1);
}
else if (pte & PTE_V) {
uint64 child = PTE2PA(pte);
printf(".. .. ..%d: pte %p pa %p\n", i, pte, child);
}
}
} void vmprint(pagetable_t pagetable)
{
printf("page table %p\n", pagetable);
backtrace(pagetable, 0);
}

Detecting which pages have been accessed

这个实验需要我们判断页面是否被访问过,为此需要添加一个标志位PTE_A来标识页面是否被访问过

  • You'll need to define PTE_A, the access bit, in kernel/riscv.h. Consult the RISC-V manual to determine its value.

根据提示查阅手册

kernel/riscv.h中添加

#define PTE_A (1L << 6) // access bit

  • You'll need to parse arguments using argaddr() and argint()
  • 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)

根据这两个提示我们可以猜测出三个参数的类型应该分别为uint64,int,uint64,分别是第一个需要检查的用户页的虚拟地址,要检查的页表数,以及输出结果的用户态地址(因为内核态跟用户态的空间是不互通的,所以要借助 copyout 将 bitmask 传给用户态程序)

根据参数可以知道我们是要查看n个页的,问题是只给了第一个用户页的虚拟地址,之后的n个页怎么查看。其实只需要每次循环加上页表大小PGSIZE就能到下一个页了

同时我们来看一下walk,它的作用是根据虚拟地址返回页表中对应的PTE

pte_t *
walk(pagetable_t pagetable, uint64 va, int alloc)
{
if(va >= MAXVA)
panic("walk"); for(int level = 2; level > 0; level--) {
// 主要注意这里,请结合下图理解
pte_t *pte = &pagetable[PX(level, va)];
if(*pte & PTE_V) {
pagetable = (pagetable_t)PTE2PA(*pte);
} else {
if(!alloc || (pagetable = (pde_t*)kalloc()) == 0)
return 0;
memset(pagetable, 0, PGSIZE);
*pte = PA2PTE(pagetable) | PTE_V;
}
}
return &pagetable[PX(0, va)];
}

#define PGSHIFT 12  // 对应图中的offset
#define PXSHIFT(level) (PGSHIFT+(9*(level))) //L2 L1 L0 都是9位,+9*level是为了定位到对应的level上
#define PX(level, va) ((((uint64) (va)) >> PXSHIFT(level)) & PXMASK) //获得L2或L1,L0的bits

明白了walk的功能就可以完成这一部分的任务了

  • 通过虚拟地址VA确定PTE(walk来完成)
  • 检查PTE的标志位PTE_A是否为1,1表示被访问过
    • 记得将PTE_A清0,以防影响再次调用sys_pgaccess时影响判断
  • VA+=PGSIZE得到下一个页面的虚拟地址
#ifdef LAB_PGTBL
uint64
sys_pgaccess(void)
{
uint64 va;
int page_num;
uint64 user_bitmask_addr;
if(argaddr(0, &va) < 0)
return -1;
if(argint(1, &page_num) < 0)
return -1;
if(argaddr(2, &user_bitmask_addr) < 0)
return -1;
// 32是看test得出来的
// It's okay to set an upper limit on the number of pages that can be scanned.
if(page_num < 0 || page_num > 32)
return -1; uint32 bitmask = 0;
pte_t *pte;
struct proc *p = myproc(); for(int i = 0; i < page_num; i++){
if(va >= MAXVA)
return -1;
// alloc不为0,walk就会为找不到对应pte地址的va申请一个页
// 显然不存在,就表示没有被访问过
// 我们无需创建页,这不是当前函数的职责
pte = walk(p->pagetable, va, 0); if(pte == 0)
return -1;
if(*pte & PTE_A){
bitmask |= (1 << i);
// Be sure to clear PTE_A after checking if it is set.
*pte &= (~PTE_A);
}
va += PGSIZE;
} if(copyout(p->pagetable, user_bitmask_addr, (char*)&bitmask, sizeof(bitmask)) < 0)
return -1;
return 0;
}
#endif

最新文章

  1. Undefined symbols for architecture arm64:
  2. 怎么用SAX生成xml文件
  3. Bootstrap&lt;基础十一&gt;字体图标(Glyphicons)
  4. mysql存储过程中的异常处理
  5. unity 解析tmx
  6. PHP项目:如何用PHP高并发检索数据库?
  7. UI学习笔记---第十二天UITabBarController
  8. Spark和Hadoop作业之间的区别
  9. javascript简单笔记
  10. Director Scene Layer and Sprite
  11. 《第一行代码》学习笔记1-Android系统架构
  12. Debian/Ubuntu手动编译安装MongoDB C++11驱动及驱动测试
  13. JS错误:Uncaught SyntaxError: Unexpected token ILLEGAL
  14. TCP重传问题解决思路
  15. VirtualBox - RTR3InitEx failed with rc=-1912 (rc=-1912)
  16. 前端的UI设计与交互之字体篇
  17. Guava常用方法
  18. json转化技巧
  19. pycharm无法使用ctrl+c/v复制粘贴的问题
  20. Sql注入基础原理介绍

热门文章

  1. ROS路由器DHCP地址不够使用解决办法!
  2. Objects.equals有坑
  3. JS 邮箱的验证(正则)
  4. my file server1
  5. bzoj4182/luoguP6326 Shopping(点分治,树上背包)
  6. 22.1.23Manacher算法、双端队列、单调栈
  7. MySQL中的约束,添加约束,删除约束,以及其他修饰
  8. 去掉win10的命令行
  9. remote debug 的详细配置
  10. Linux下切换python2和python3