6.S081-2021-Lab3 Pgtbl学习笔记
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.h
的struct 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, inkernel/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()
andargint()
- 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
6.S081-2021-Lab3 Pgtbl学习笔记的更多相关文章
- ucore操作系统学习笔记(二) ucore lab2物理内存管理分析
一.lab2物理内存管理介绍 操作系统的一个主要职责是管理硬件资源,并向应用程序提供具有良好抽象的接口来使用这些资源. 而内存作为重要的计算机硬件资源,也必然需要被操作系统统一的管理.最初没有操作系统 ...
- 热更新解决方案--xlua学习笔记
一.热更新方案简介 在Unity游戏工程中,C#代码(编译型语言)资源和Resources文件夹下的资源打包后都不可以更改,因此这部分内容不能进行热更新,而lua代码(解释型语言)逻辑不需要进行预编译 ...
- 热更新解决方案--tolua学习笔记
一.tolua使用准备工作:从GitHub上下载tolua(说明:这篇笔记使用的Unity版本是2019.4.18f1c1,使用的tolua是2021年4月9日从GitHub上Clone的tolua工 ...
- (目录)Fortran学习笔记:开坑!!!
前言:因为某些原因,需要使用Fortran编写程序,记录下Fortran语法学习过程中的部分笔记.在此开坑记录,立下Flag,"希望年末能够更新完" Fortran 学习笔记 陈橙 ...
- Qt Creator 源码学习笔记02,认识框架结构
阅读本文大概需要 6 分钟 在上一篇大概了解了关于Qt Creator 基础知识后[1],本篇先学习下框架基本结构,这样能够清晰的知道这个框架当中包含哪些文件.文件夹.工程文件,这些文件分别代表什么意 ...
- FFT/NTT复习笔记&多项式&生成函数学习笔记Ⅲ
第三波,走起~~ FFT/NTT复习笔记&多项式&生成函数学习笔记Ⅰ FFT/NTT复习笔记&多项式&生成函数学习笔记Ⅱ 单位根反演 今天打多校时 1002 被卡科技了 ...
- Linux学习笔记-韩顺平
这是我学习哔哩哔哩UP主韩顺平的2021韩顺平图解Linux课程的学习笔记. 课程地址:2021韩顺平图解Linux课程 Linux基础篇-Linux目录结构 基本介绍 linux 的文件系统是采用级 ...
- R-CNN学习笔记
R-CNN学习笔记 step1:总览 步骤: 输入图片 先挑选大约2000个感兴趣区域(ROI)使用select search方法:[在输入的图像中寻找blobby regions(可能相同纹理,颜色 ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
随机推荐
- LGP4456题解
我就是不用矩阵快速幂! 题意:一个 \(\rm 01\) 序列为合法的当且仅当没有两个相邻的 \(1\),若 \(1\) 的个数为 \(x\),\(0\) 的个数为 \(y\),这个 \(\rm 01 ...
- 自定义函数实现atoi功能
思路: 列如char a[ ] ="123" "1" "2" "3' "\0" 首先遍历这个字符串 知道这个字 ...
- JS 邮箱的验证(正则)
/^([a-zA-Z\d])(\w|\-)+@[a-zA-Z\d]+\.[a-zA-Z]{2,4}$/
- Python安装wxPython和ubuntu使用apt提示不能更新
[空两格]昨天憨批室友搁我面前装b,说他会用pip安装Python包了,说是安装wxPython的时候通过换源解决了之前安装出错的问题.我一听,这事不对劲啊,是这个b直接看不懂输出了吧.果然,我让他在 ...
- 一文了解MySQL的Buffer Pool
摘要:Innodb 存储引擎设计了一个缓冲池(Buffer Pool),来提高数据库的读写性能. 本文分享自华为云社区<MySQL 的 Buffer Pool,终于被我搞懂了>,作者:小林 ...
- callbale 和runnable 区别
Callable接口: 1 2 3 public interface Callable<V> { V call() throws Exception; } Runnable接口: ...
- 一条SQL语句执行得很慢的原因有哪些
说实话,这个问题可以涉及到 MySQL 的很多核心知识,可以扯出一大堆,就像要考你计算机网络的知识时,问你"输入URL回车之后,究竟发生了什么"一样,看看你能说出多少了. 之前腾讯 ...
- github新手使用指南
常用命令: Git 速查表(摘自 AI有道) 一.常见命令 git init : 初始化 git 仓库,即将一个文件夹初始化为一个 git 仓库.具体的操作是创建一个 .git 隐藏文件夹 git ...
- java中的正则表达式And Pattern And Macher
在哪里?? java.util.regex包下有两个用于正则表达式的类, 一个是Matcher类, 另一个Pattern 简单例子 public class RegexLeaning { public ...
- 如何通过HibernateDaoSupport将Spring和Hibernate 结合起来?
用 Spring 的 SessionFactory 调用 LocalSessionFactory.集成过程分三步: 配置 the Hibernate SessionFactory. 继承 Hibern ...