6.s081 学习实验记录(四)page tables

文章目录

    • 一、Speed up system calls
      • 简介
      • 注意
      • 实验代码
      • 结果
    • 二、Print a page table
      • 简介
      • 注意
      • 实验代码
      • 实验结果
    • 三、Detect which pages have been accessed
      • 简介
      • 注意
      • 实验代码
      • 实验结果

一、Speed up system calls

简介

许多操作系统通过共享一系列信息到用户态只读页面来加速某些系统调用的执行时间,因此我们需要实现该功能来加速系统调用 getpid()

每当进程创建的时候.,在 USYSCALL(该VA定义在 memlayout.h) 处映射一个只读的物理页,这个物理页的起始位置存储一个 struct usyscall(同样定义在 memlayout.h),初始该结构体存储当前进程的 PID。当前实验,用户态已经准备好了一个应用pgtbltest,该应用使用ugetpid() 函数获取进程pidugetpid()函数实现非常简单,就是从用户态的 USYSCALL 虚拟地址处读取 usyscall中记录的 pid

所以,我们要做的事情也很简单,在创建进程的时候,为其用户态页表分配一个物理页,该物理页映射的虚拟地址为 USYSCALL,然后在该地址处存储一个 struct usyscall 即可。

用户态的代码已经准备完毕,我们只需要改动内核部分即可

注意

  • 使用 allocproc() 申请并初始化物理页
  • 使用 freeproc() 释放物理页
  • 可以在 kernel/proc.c 中的 proc_pagetable() 实现物理地址和虚拟地址的映射,该函数用于创建一个用户态的页表,可以使用 mappages() 完成物理地址和虚拟地址的映射
  • 物理页的权限要设置为用户态只读
  • 因此实现顺序:首先 allocproc() 创建进程时申请一个物理页,设置物理页用户态只读, proc_pagetable() 创建用户态页表时,通过 mappages() 建立虚拟地址和物理地址映射

实验代码

  • kernel/proc.h
// Per-process state
struct proc {// ... pagetable_t pagetable;       // User page tablestruct trapframe *trapframe; // data page for trampoline.Sstruct usyscall *usyscall; // 存储我们映射到用户态的物理页的物理地址// ... 其他代码
};
  • kernel/proc.c:alloc_proc()
static struct proc*
allocproc(void)
{struct proc *p;//...其他代码
found:// ...其他代码// Allocate a USYSCALL page (新增代码)if((p->usyscall = kalloc()) == 0){freeproc(p);release(&p->lock);return 0;}p->usyscall->pid = p->pid;// An empty user page table.// ... 其他代码return p;
}
  • kernel/proc.c:proc_pagetable()
pagetable_t
proc_pagetable(struct proc *p)
{pagetable_t pagetable;// An empty page table.pagetable = uvmcreate();if(pagetable == 0)return 0;// ... 其他代码// map the trapframe page just below the trampoline page, 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 page to USYSCALL (新增代码)if(mappages(pagetable, USYSCALL, PGSIZE,(uint64)(p->usyscall), PTE_R | PTE_U) < 0){uvmunmap(pagetable, TRAMPOLINE, 1, 0);uvmfree(pagetable, 0);return 0;}return pagetable;
}
  • kernel/proc.c:proc_freepagetable()
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);
}
  • kernel/proc.c:free_proc()
static void
freeproc(struct proc *p)
{if(p->trapframe)kfree((void*)p->trapframe);p->trapframe = 0;//新增代码if(p->usyscall)kfree((void*)p->usyscall);p->usyscall = 0;// 新增结束if(p->pagetable)proc_freepagetable(p->pagetable, p->sz);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;
}

结果

在这里插入图片描述

二、Print a page table

简介

实现一个函数,用于打印页表。定义一个函数 vmprint(),参数为pagetable_t ,功能为输出这个页表的格式化信息。 同时在 exec.c 的 exec()函数 return argc之前插入 Insert if(p->pid==1) vmprint(p->pagetable) ,打印第一个进程的页表。
做完上述这些之后,启动 xv6 的时候,应该会输出如下打印(在通过 exec() 加载第一个进程 init 时):

page table 0x0000000087f6b000..0: pte 0x0000000021fd9c01 pa 0x0000000087f67000.. ..0: pte 0x0000000021fd9801 pa 0x0000000087f66000.. .. ..0: pte 0x0000000021fda01b pa 0x0000000087f68000.. .. ..1: pte 0x0000000021fd9417 pa 0x0000000087f65000.. .. ..2: pte 0x0000000021fd9007 pa 0x0000000087f64000.. .. ..3: pte 0x0000000021fd8c17 pa 0x0000000087f63000..255: pte 0x0000000021fda801 pa 0x0000000087f6a000.. ..511: pte 0x0000000021fda401 pa 0x0000000087f69000.. .. ..509: pte 0x0000000021fdcc13 pa 0x0000000087f73000.. .. ..510: pte 0x0000000021fdd007 pa 0x0000000087f74000.. .. ..511: pte 0x0000000020001c0b pa 0x0000000080007000
init: starting sh

页表输出的时候按照一级、二级、三级、物理页的层次来打印,每一级缩进一个 ..,同时只打印有效的页表,因此,上述输出中,一级页表只有 0255 的页表项是有效的,0 号页表项索引的二级页表同样只有 0 号页表项有效。二级页表的 0 号页表项索引的三级页表有 0,1,2,3 四个表项有效,指向四个物理页。

注意

  • 可以在 kernel/vm.c 中实现 vmprint() 函数
  • kernel/defs.h 中定义 vmprint() 函数的原型,这样就可以在 exec.c 中调用
  • 可以使用 kernel/riscv.h 中的宏
  • 可以参考 freewalk() 的实现方式
  • 打印的时候使用 %p 来进行打印

实验代码

我们要做的事情也比较简单,实现一个新的函数 vmprint() 即可。同时为了验证我们的函数功能,在exec中稍加修改,只打印第一个进程的页表,后续该函数作为调试功能存在。

  • kernel/defs.h
// vm.c
// 其他代码
void            vmprint(pagetable_t pagetable); //新增
  • kernel/vm.c
// 如果是多核OS,会存在哪些问题呢?此时应该怎么实现?
static void __vmprint(pagetable_t pagetable, int deep){// 页表最大深度为3if(deep > 3){return;}// 打印第一行输出if(deep == 0){printf("page table %p\n", (uint64)pagetable);}// there are 2^9 = 512 PTEs in a page table.for(int i = 0; i < 512; i++){pte_t pte = pagetable[i];// 打印页表项if(pte & PTE_V){//打印页表项前缀for(int j = 0; j <= deep; j++){printf("..");}printf("%d: pte %p pa %p\n", i, (uint64)pte, (uint64)PTE2PA(pte));}if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){// this PTE points to a lower-level page table.deep++;uint64 child = PTE2PA(pte);__vmprint((pagetable_t)child, deep);deep--;}}
}void
vmprint(pagetable_t pagetable)
{__vmprint(pagetable, 0);
}
  • kernel/exec.c
int
exec(char *path, char **argv)proc_freepagetable(oldpagetable, oldsz);//新增代码if(p->pid == 1){vmprint(p->pagetable);}return argc;
}

实验结果

  • 执行 make qemu,结果如下:
    在这里插入图片描述
  • 执行 ./grade-lab-pgtbl pte printout
    在这里插入图片描述

三、Detect which pages have been accessed

简介

一些垃圾回收器需要获取当前哪些页面已经被访问过(读或者写),因此该实验需要我们判断页表的状态位来检查当前页是否被访问过,并报告给用户态。(RISC-V硬件页遍历在解决TLB miss的时候会设置这些bit位)

实现系统调用 pgaccess(),该调用需要三个参数,分别是要被检查的用户态页的起始虚拟地址,第二个为页的数目,第三个为用户态接收结果的内存地址(结果使用一个 bitmask 保存,每一位表示一个页的访问情况)

注意

  • user/pgtbltest.cpgaccess_test() 显示了如何调用 pgaccess()
  • kernel/sysproc.c 中实现 sys_pgaccess() 系统调用
  • 使用 argaddr() and argint() 获取参数
  • 结果的返回可以在内核中使用临时buffer存放,然后使用 copyout() 拷贝到用户态
  • 可以限制第二个参数的最大值
  • kernel/vm.c 中的 walk() 可以用于寻找 PTEs.
  • kernel/riscv.h 中定义 PTE_A
  • 确保在 pgaccess() 访问完这些页表之后,设置这些页表的 PTE_A 状态为 0,因此硬件只会在页表被访问时,给对应的PTE置位,但并不会将其清0,因此如果我们不清0,那么只要该PTE被访问过一次,此后将一直为1
  • 可以使用 vmprint 协助debug
  • PTE表项内容:

在这里插入图片描述

实验代码

因此,我们的目标也很简单,实现一个系统调用,系统调用的大多数步骤实验代码已经写好,我们只需要在 sys_proc.c 中实现 sys_pgaccess() 即可。该系统调用使用 walk() 获取 VA 对应的pte,判断标志位,汇总并通过 copyout 返回给用户态即可。

  • 函数原型:
    • int pgaccess(void *base, int len, void *mask);
    • uint64 sys_pgaccess(void);
  • kernel/riscv.h
//由前面的图可知,PTE_A 在第7个bit上
#define PTE_V (1L << 0) // valid
#define PTE_R (1L << 1)
#define PTE_W (1L << 2)
#define PTE_X (1L << 3)
#define PTE_U (1L << 4) // user can access
#define PTE_A (1L << 6) // 新增PTE_A
  • kernel/sysproc.c
#ifdef LAB_PGTBL
int
sys_pgaccess(void)
{// lab pgtbl: your code here.uint64 addr;uint64 a;int num;uint64 result_addr;uint64 result = 0;pte_t *pte;argaddr(0, &addr);argint(1, &num);argaddr(2, &result_addr);struct proc* cur = myproc();for(a = 0; a < num; a++){if((pte = walk(cur->pagetable, addr + a * PGSIZE, 0)) == 0){printf("pgacess: page is not exist");return -1;}if(PTE_FLAGS(*pte) & PTE_A){result = result | (1L << a);}*pte &= (~(PTE_A)); //将PTE_A置0}if(copyout(cur->pagetable, result_addr, (char *)&result, sizeof(result)) < 0){printf("pgacess: copyout result to user space failed");return -1;}return 0;
}
#endif

实验结果

  • make qemu
    在这里插入图片描述
  • ./grade-lab-pgtbl pgtbltest
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/449968.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言第十七弹---指针(一)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 指针 1、内存和地址 1.1、内存 2、指针变量和地址 2.1、取地址操作符&#xff08;&&#xff09; 2.2、指针变量和解引用操作符&#xff08;*&#xff09;…

VS打包.exe文件步骤

1.借助vs自带扩展工具 2.1打开扩展栏 2.2搜索栏填入 " installer " 2.3下载安装 下载完成后&#xff0c;推出vs自动弹出安装。 2.生成安装包 2.1新建一个项目 2.2输入"setup" 直接下一步 2.3输入项目名称和存储位置、点击创建 出现该栏 2.4选择 主文件…

了解UDP发送过快导致的问题和对应解决方案

在当今这个以数据为核心的时代&#xff0c;企业对于数据传输的速度和稳定性有着日益增长的需求。UDP凭借其低延迟和高效率的特性&#xff0c;在实时通信和大规模数据传输领域扮演着关键角色。然而&#xff0c;UDP的无连接特性和缺乏可靠性也给数据传输带来了挑战&#xff0c;尤…

java反射详解

动态代理 什么是动态代理&#xff1f; 特点&#xff1a;无侵入式的给代码增加额外的功能 对象有什么方法想要被代理&#xff0c;代理就一定要有对应的方法 对象和代理需要实现同一个接口&#xff0c;接口中就是被代理的方法 调用者通过代理&#xff0c;调用代理中的方法&#x…

149基于matlab的A星算法和PSO算法实现路径规划动画演示

基于matlab的A星算法和PSO算法实现路径规划动画演示&#xff0c;具有GUI界面&#xff0c;可自主生成障碍物。移动靶路径规划。程序已调通&#xff0c;可直接运行。 149 matlab A星算法 PSO算法 路径规划 (xiaohongshu.com)

向刻苦耐劳乐观向上的青年致敬

今晨互联网上的国际时事新闻报道&#xff0c;显得越来越真假难辨&#xff1b;特别是对俄乌战争、以巴战争、中美俄日朝印越和欧盟各国关系的新闻报道&#xff0c;可谓朝三暮四&#xff0c;一日多变&#xff0c;令人不知谁家的报道可信&#xff0c;便绕道行&#xff0c;不议为妙…

C++(11)——内存管理

C内存分布 我们先看一段代码以及相关问题。 这道题的答案是多少呢&#xff1f; 答案在这里哦&#xff0c;看一下有没有问题呀。如果这么简单的题做错了&#xff0c;怕不是要被电击一下。 C内存管理方式 我们知道C语言中动态内存管理的方式是 malloc realloc calloc free 这几…

嵌入式学习第三篇——51单片机

目录 1&#xff0c;嵌入式系统 1&#xff0c;嵌入式系统的定义 2&#xff0c;单片机的定义 2&#xff0c;51单片机 1&#xff0c;开发环境 2&#xff0c;开发板使用的基本思路 1&#xff0c;查看原理图&#xff0c;查看芯片手册 2&#xff0c;获得调用硬件的管…

【js逆向】scrapy基础

目录 一, 爬虫工程化 二, scrapy简介 三, Scrapy工作流程(重点) 四, scrapy安装 4.1 pip 安装 4.2 wheel安装 五, Scrapy实例 六, 自定义数据传输结构item 七, scrapy使用小总结 一, 爬虫工程化 在之前的学习中我们已经掌握了爬虫这门技术需要的大多数的技术点, 但是我…

OpenCV学习记录——形态学处理

文章目录 前言一、腐蚀和膨胀二、高级形态学运算三、具体应用代码 前言 形态学是图像处理中最常用的技术之一&#xff0c;它主要用于从图像中提取有意义的形状信息&#xff0c;例如边界和连通区域&#xff0c;以便后续的识别工作能够捕捉到目标对象最重要的形状特征。此外&…

车载测试Vector工具CANoe——常见问题汇总(下)

车载测试Vector工具CANoe——常见问题汇总(下) 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师(Wechat:gongkenan2013)。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一…

elementUI中el-tree组件单选没有复选框时,选中、current-node-key高亮、刷新后保留展开状态功能的实现

目录 一、代码实现1. 属性了解 &#xff08;[更多](https://element.eleme.cn/#/zh-CN/component/tree)&#xff09;2. 实现步骤3.代码示例 二、 效果图 一、代码实现 1. 属性了解 &#xff08;更多&#xff09; node-key 每个树节点用来作为唯一标识的属性&#xff0c;整棵树…