6.S081-Lab3
Lab: page tables
课程地址
课程参考书籍
Speed up system calls
一些操作系统(如Linux)通过在用户空间和内核之间共享只读区域中的数据来加快某些系统调用。这就消除了在执行这些系统调用时对内核交叉的需要。为了帮助您了解如何将映射插入到页面表中,您的第一个任务是为xv6中的getpid()系统调用实现此优化。
proc.h 新增一个 syscall. 结构,
// Per-process state
struct proc {struct spinlock lock;// p->lock must be held when using these:enum procstate state; // Process statevoid *chan; // If non-zero, sleeping on chanint killed; // If non-zero, have been killedint xstate; // Exit status to be returned to parent's waitint pid; // Process ID// wait_lock must be held when using this:struct proc *parent; // Parent process// these are private to the process, so p->lock need not be held.uint64 kstack; // Virtual address of kernel stackuint64 sz; // Size of process memory (bytes)pagetable_t pagetable; // User page tablestruct trapframe *trapframe; // data page for trampoline.Sstruct context context; // swtch() here to run processstruct file *ofile[NOFILE]; // Open filesstruct inode *cwd; // Current directorychar name[16]; // Process name (debugging)struct usyscall *usyscall; //pid
};
kernel/proc.c 中新增一个页用于cache syscall
pagetable_t
proc_pagetable(struct proc *p)//create a only read page for syscall getpid cache getpidif(mappages(pagetable, USYSCALL, PGSIZE,(uint64)(p->usyscall), PTE_R|PTE_U) < 0){uvmunmap(pagetable, TRAPFRAME, 1, 0);uvmunmap(pagetable, TRAMPOLINE, 1, 0);uvmfree(pagetable, 0);return 0;}p->usyscall->pid=p->pid;
页销毁时
void
proc_freepagetable(pagetable_t pagetable, uint64 sz)
{uvmunmap(pagetable, TRAMPOLINE, 1, 0);uvmunmap(pagetable, TRAPFRAME, 1, 0);uvmunmap(pagetable, USYSCALL, 1, 0);uvmfree(pagetable, sz);
}
进程初始化,参考 trapframe 新增一个页分配
static struct proc*
allocproc(void)// Allocate a trapframe page.if((p->trapframe = (struct trapframe *)kalloc()) == 0){freeproc(p);release(&p->lock);return 0;}//allocate syscall page if((p->usyscall = (struct usyscall *)kalloc()) == 0){freeproc(p);release(&p->lock);return 0;}
释放进程时将syscall 修改为0,释放分配的内存
static void
freeproc(struct proc *p)
{if(p->trapframe)kfree((void*)p->trapframe);p->trapframe = 0;if(p->pagetable)proc_freepagetable(p->pagetable, p->sz);if(p->usyscall)kfree((void*)p->usyscall); p->usyscall=0; p->pagetable = 0;p->sz = 0;p->pid = 0;p->parent = 0;p->name[0] = 0;p->chan = 0;p->killed = 0;p->xstate = 0;p->state = UNUSED;
}
可以看到在ulib.c 中 用户态直接去访问对应的内核共享页,
int
ugetpid(void)
{struct usyscall *u = (struct usyscall *)USYSCALL;return u->pid;
}
整个思路就是在进程创建的时候给syscall分配指定的内存页。(kalloc的顺序trapfame->usersyscall), 然后页的内存结构就是
TRAMPOLINE->TRAPFRAME->USYSCALL,
关键的点在于理解
mappages 维护了 pte(pagetable中的record,通过它可以找到vitrual address,实现 virtual address到 physical address 的转化),并且控制了读写权限,用户访问权限。(可以在用户态访问TRAMPOLINE或者TRAPFRAME区的数据看看会发生什么?)
kalloc 实现了。max->TRAMPOLINE->TRAPFRAME->USYSCALL 的内存页分配,
Print a page table
在 Sv39 中,定义物理地址(Physical Address)有 56位,而虚拟地址(Virtual Address) 有 39位。实际使用的时候,一个虚拟地址要占用 64位,只有低 39位有效,规定 63−39 位的值必须等于第 38 位的值(类似有符号整数),否则会认为该虚拟地址不合法,在访问时会产生异常。
不论是物理地址还是虚拟地址,我们都可以认为,最后12位表示的是页内偏移,也就是这个地址在它所在页帧的什么位置(同一个位置的物理地址和虚拟地址的页内偏移相同)。除了最后12位,前面的部分表示的是物理页号或者虚拟页号。
这个最核心的是理解它的页表项(PTE, Page Table Entry)的结构是什么样子的
|63-54 | 53-28 | 27-19 | 18-10 | 9-8 | 7
|Reserved |PPN[2] |PPN[1] |PPN[0] |RSW| D|
| | |
Sv39的一个页表项占据8字节,结构是这样的:
63-54 | 53-28 | 27-19 | 18-10 | 9-8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Reserved | PPN[2] | PPN[1] | PPN[0] | RSW | D | A | G | U | X | W | R | V |
一个页表项大小为64位8字节。其中第53-10共44位为一个物理页号,表示这个虚拟页号映射到的物理页号。后面的第9-0位则描述映射的状态信息。
- D=1表示自从上次被清零后,有虚拟地址通过这个页表项进行写入
- A=1表示自从上次被清零后,有虚拟地址通过这个页表项进行读、或者写、或者取指。
- G=1表示这个页表项是”全局"的,也就是所有的地址空间(所有的页表)都包含这一项
- U=1表示用户态 (U Mode)的程序可以通过该页表项进行映射。在用户态运行时也只能够通过 U=1的页表项进行虚实地址映射。
- R,W,X 分别表示是否可读 (Readable),可写 (Writable),可执行 (Executable)。
- V 表示这个页表项是否合法。如果为 表示不合法,此时页表项其他位的值都会被忽略。
以 W这一位为例,如果 W=0表示不可写,那么如果一条 store 的指令,它通过这个页表项完成了虚拟页号到物理页号的映射,找到了物理地址。但是仍然会报出异常,是因为这个页表项规定如果物理地址是通过它映射得到的,那么不准写入! R,X也是同样的道理。
页表基址
翻译的过程中,我们首先需要知道树状页表的根节点的物理地址
这一般保存在一个特殊寄存器里。对于RISCV架构,是一个叫做satp(Supervisor Address Translation and Protection Register)的CSR。实际上,satp里面存的不是最高级页表的起始物理地址,而是它所在的物理页号。除了物理页号,satp还包含其他信息
63-60 | 59-44 | 43-0 |
---|---|---|
MODE(WARL) | ASID(WARL) | PPN(WARL) |
4 | 16 | 44 |
MODE表示当前页表的模式
- 0000表示不使用页表,直接使用物理地址,在简单的嵌入式系统里用着很方便。
- 0100表示Sv39页表,也就是我们使用的,虚拟内存空间高达 512 GiB 512\text{GiB} 512GiB。
- 0101表示Sv48页表,它和Sv39兼容,可以猜猜它有几层。虚拟内存空间高达 512 TiB 512\text{TiB} 512TiB得家里有矿才能用得上
- 其他编码保留备用
快表
物理内存的访问速度要比 CPU 的运行速度慢很多, 去访问一次物理内存可能需要几百个时钟周期(带来所谓的“冯诺依曼瓶颈”)。如果我们按照页表机制一步步走,将一个虚拟地址转化为物理地址需要访问 次物理内存,得到物理地址之后还要再访问一次物理内存,才能读到我们想要的数据。这很大程度上降低了效率。
实践表明虚拟地址的访问具有时间局部性和空间局部性。
- 时间局部性是指,被访问过一次的地址很有可能不远的将来再次被访问;
- 空间局部性是指,如果一个地址被访问,则这个地址附近的地址很有可能在不远的将来被访问。
CPU 内部,我们使用快表 (TLB, Translation Lookaside Buffer) 来记录近期已完成的虚拟页号到物理页号的映射
我们如果修改了 satp 寄存器,比如将上面的 字段进行了修改,说明我们切换到了一个与先前映射方式完全不同的页表。此时快表里面存储的映射结果就跟不上时代了,很可能是错误的。这种情况下我们要使用 sfence.vma 指令刷新整个 TLB 。
同样,我们手动修改一个页表项之后,也修改了映射,但 TLB 并不会自动刷新,我们也需要使用 sfence.vma 指令刷新 TLB 。如果不加参数的, sfence.vma 会刷新整个 TLB 。你可以在后面加上一个虚拟地址,这样 sfence.vma 只会刷新这个虚拟地址的映射。
代码实现:
exec.c
void
vmprint2(pagetable_t pagetable,int level){// there are 2^9 = 512 PTEs in a page table.// so offset >> 25 , if offset >> 9 exit level 1 . offset >> 18 level 2 offset >> 27 level 3 for(int i = 0; i < 512; i++){pte_t pte = pagetable[i];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); if(level==0){printf(".. %d: pte %p pa %p\n", i, pte, child);}else if(level==1){printf(".. .. %d: pte %p pa %p\n", i, pte, child);}else if(level==2){printf(".. .. .. %d: pte %p pa %p\n", i, pte, child);}vmprint2((pagetable_t)child,level+1);} else if(pte & PTE_V){uint64 child = PTE2PA(pte);if(level==0){printf(".. %d: pte %p pa %p\n", i, pte, child);}else if(level==1){printf(".. .. %d: pte %p pa %p\n", i, pte, child);}else if(level==2){printf(".. .. .. %d: pte %p pa %p\n", i, pte, child);}}}
}void
vmprint(pagetable_t pagetable){printf("page table %p\n", pagetable);vmprint2(pagetable,0);}
Detect which pages have been accessed
一些垃圾收集器(自动内存管理的一种形式)可以从有关哪些页面已被访问(读取或写入)的信息中获益。 在实验的这一部分,您将向 xv6 添加一项新功能,通过检查 RISC-V 页表中的访问位来检测此信息并将其报告给用户空间。 RISC-V 硬件页面遍历器在解决 TLB 未命中时在 PTE 中标记这些位
添加访问位A:
kernel/riscv.h.
#define PTE_A (1L << 6)
kernel/sysproc.c.
int
sys_pgaccess(void)
{// lab pgtbl: your code here.int len;uint64 addr;uint64 abits;uint64 result=0;argint(1, &len);argaddr(0, &addr);argaddr(2, &abits);struct proc *p= myproc();//简单来说就是 看下addr 对应的buf中那几片被访问过了 然后构造一个buf对应的bitmask 标记成1//在用户空间下分配的buffer最后会映射到 pagetable上的地址空间//那么要怎么看有哪些page被访问过了呢?就walk一下看下PTE_A 的状态位for(int i=0;i<len;i++){//拿到buf 对应到的 pte pte_t * pte= walk(p->pagetable,addr+i*PGSIZE,0);if(pte && (*pte & PTE_A)){//clear *pte= *pte & ~ PTE_A;result=result|1<<i;}//拿到pte// PTE_A 那什么时候该设置值为访问过呢,在 pages have been accessed (read or write}if(copyout(p->pagetable, abits, (char *)&result, sizeof(result)) < 0)return -1;return 0;
}
Ref:
https://1790865014.gitbook.io/ucore-step-by-step/intro-3
6.S081-Lab3相关推荐
- xv6 6.S081 Lab3: alloc
xv6 6.S081 Lab3: alloc 写在前面 实验介绍 开始! 任务再描述 任务一实现 任务二实现 Buddy Allocator Code Thru 任务二的实现 alloc代码在这里.另 ...
- 6.S081 Lab3 page tables
6.S081 Lab3 page tables 未完成 文章目录 6.S081 Lab3 page tables 未完成 1. Print a page table ([easy](https://p ...
- MIT6.S081 Lab3 Page tables
lab1.2不是太难,lab 3太变态了,github上记一下代码,源代码地址 :https://github.com/CodePpoi/mit-lab 参考博客 : https://blog.csd ...
- 6.s081 lab3
lab3 页表初始化过程: 物理页是一组由run结构体保存的,每个runmain函数调用kinit,初始化物理页.kinit调用了freerange.把内核对应的物理页全部释放掉,加入到freelis ...
- MIT6.S081 Lab3: page tables
Print a page table 接收一个pagetable_t并把它指向的页表打印. 在kernel/def.h中增加函数声明void vmprint(void)并在kernel/vm.c中定义 ...
- 6.S081 Lab3 page tables 页表
Speed up system calls 加速系统调用 有些操作系统会通过在用户空间和内核空间之间共享一些内存来加速系统调用.该实验题目要求在内核中保存一个映射到用户内存空间的结构体,该结构体中保存 ...
- 吴军三部曲态度(四)人际关系
交友不要怕吃亏 如果你现在在大学中,那么很好,大学或者是更早交的朋友,是很单纯的,不会像社会上那么的功利,因为到了社会上,很多东西都跟利益挂钩了,这样目的就不会那么的单纯了. 俗话说,穷在闹市无人问, ...
- XV6 lab3:Trap
Lab3:Traps 参考文章: 6.S081 & 操作系统内核 操作系统MIT6.S081:Lab4->Trap Xv6操作系统的系统调用通过 trampoline 来实现用户空间和内 ...
- OSPF分解试验部分-LAB3:OSPF各种网络类型试验
LAB3:OSPF各种网络类型试验 1.NBMA试验 试验需求: Frame-relay使用物理接口,OSPF建立后,默认应该是NBMA网络类型.我们验证NBMA中我们应该如何配置OSPF. 配置 ...
- CS144 计算机网络实验 lab3 笔记
CS144 计算机网络实验 lab3 笔记 介绍 本实验中,我们将会在之前实验的基础上,实现一个TCP sender ----将字节流转换成数据报并发送. TCP协议是一个在不可靠的协议上提供可靠的, ...
最新文章
- 如何取消JS事件的派发——stopPropagation()
- 万亿市场下,电商代运营还需另求“第二曲线”
- docker挂载的目录无法读写
- 阿里云HBase发布冷存储特性,助你不改代码,1/3成本轻松搞定冷数据处理
- 【高并发】java JUC中的Semaphore(信号量)
- oracle怎么恢复删除数据库数据库文件,Oracle只有数据文件恢复数据库
- Redfish 模型工具:Redfish Mockup Creator 和 Redfish Mockup Server
- 高端游戏计算机配置单,全球最顶级的游戏电脑配置 高端游戏电脑装机推荐
- Go官方依赖包管理工具dep的安装及使用
- 中国地质大学英语语音学习笔记(三):音节与单词变形(ed,es,ing,est,er,派生等)导致的音节数和读音变化
- 荣耀MagicOS 7.0正式发布;快手科技2022年第三季度收入同比增长12.9% | 美通企业日报...
- 【19C】logmnr参考
- 【评测】肠道微生物核酸提取试剂盒
- 取消自动续费服务的步骤
- 对RGB三个通道进行操作示例
- C++ count函数的用法(可以用作统计个数)
- 微信小程序API----授权登录拿到用户头像昵称等信息
- CDR2023软件最新版有哪些新增功能?
- 100之内含有7与7的倍数的数
- Interpretability Beyond Feature Attribution: Quantitative Testing with Concept Activation Vectors