深入学习操作系统!详细剖析 MIT 6.S081 课程 Lab 3 : page tables - 2 Print a page table

本文细致的剖析了 2021 FALL MIT 6.S081 课程的一项实验, Lab 链接 Lab: page tables (mit.edu)

新人博主,大家的每一次阅读都会激励我继续创作,大家的点赞将会是我继续更新的巨大动力,对文中内容或实验过程中有任何疑问欢迎留言!

Lab 3 主要探索页表并对其进行修改,以简化将数据从用户空间复制到内核空间的函数。本实验涉及到一些虚拟地址和物理地址结构,以及两者之间的转换,需要对虚拟内存的知识有基本认识。

Print a page table (easy)

为了帮助您了解 RISC-V 页表,也为将来的调试提供帮助,您的任务是编写一个打印页表内容的函数。

定义一个名为 vmprint() 的函数。它应当接收一个 pagetable_t 作为参数,并以下面描述的格式打印该页表。在 exec.c 中的 return argc 之前插入 if(p->pid==1) vmprint(p->pagetable) ,以打印第一个进程的页表。如果你通过了 pte printout 测试的 make grade ,你将获得此作业的满分。

提示

  • 你可以将 vmprint() 放在 kernel/vm.c
  • 使用定义在 kernel/riscv.h 末尾处的宏
  • 函数 freewalk 可能会对你有所启发
  • vmprint 的原型定义在 kernel/defs.h 中,这样你就可以在 exec.c 中调用它了
  • printf 中使用 %p 来打印像上面示例中的完成的 64 比特的十六进制 PTE 和地址

实验过程

上一实验中我们已经对内存管理中涉及到的主要函数与设计思想进行了剖析,因此完成本实验较为容易。

首先,按照题目要求,在 kernel/exec.c 中插入 if(p->pid==1) vmprint(p->pagetable)

rust 复制代码
 // kernel/exec.c
 // ...
 int
 exec(char *path, char **argv)
 {
   // ...
     
   // Commit to the user image.
   oldpagetable = p->pagetable;
   p->pagetable = pagetable;
   p->sz = sz;
   p->trapframe->epc = elf.entry;  // initial program counter = main
   p->trapframe->sp = sp; // initial stack pointer
   proc_freepagetable(oldpagetable, oldsz);
 ​
   if(p->pid==1) vmprint(p->pagetable);
 ​
   return argc; // this ends up in a0, the first argument to main(argc, argv)
 ​
   // ...
 }

vmprint 接收一个 pagetable_t 类型的参数,并打印如下列格式,第一行为 vmprint 的输入参数值,接着以深度优先的方式,打印出页表的所有 PTE," .." 表示 page directory 的层级:

css 复制代码
 page table 0x0000000087f6e000
 ..0: pte 0x0000000021fda801 pa 0x0000000087f6a000
 .. ..0: pte 0x0000000021fda401 pa 0x0000000087f69000
 .. .. ..0: pte 0x0000000021fdac1f pa 0x0000000087f6b000
 .. .. ..1: pte 0x0000000021fda00f pa 0x0000000087f68000
 .. .. ..2: pte 0x0000000021fd9c1f pa 0x0000000087f67000
 ..255: pte 0x0000000021fdb401 pa 0x0000000087f6d000
 .. ..511: pte 0x0000000021fdb001 pa 0x0000000087f6c000
 .. .. ..510: pte 0x0000000021fdd807 pa 0x0000000087f76000
 .. .. ..511: pte 0x0000000020001c0b pa 0x0000000080007000

xv6 采用了三级页表机制,与上一个题目中分析的 freewalk() 函数类似,(请参考深入学习操作系统!详细剖析 MIT 6.S081 课程 Lab 3 : page tables - 1 Speed up system calls),我们要依据不同的页表层级来做不同的操作,首先若 PTE 存在且 Valid bit 被置位,则为有效页表,应该打印页表信息。

只要 WRX 其中任何一位被置位,就是最后一级的页表,不需要递归。

若 PTE 不可读 & 不可写 & 不可执行,代表为第一/二级的页表,应该继续递归处理。

按照这一思路,参考 freewalk() 函数的代码,我们在 kernel/vm.c 中添加 vmprint() 函数的代码,由于vmprint() 仅接收一个 pagetable_t 类型的参数,而我们在递归过程中还需要一个参数来标记递归深度,因此我们设置一个辅助函数vmprint_recursive() ,参数类型为 pagetable_tint

scss 复制代码
 // kernel/vm.c
 // ...
 void
 vmprint_recursive(pagetable_t pagetable, int level)
 {
   for (int i = 0; i < 512; i++)
   {
     pte_t pte = pagetable[i];
     if (pte & PTE_V) {
       uint64 pa = PTE2PA(pte);
       printf("..");
       for (int j = 0; j < level; j++) {
         printf(" ..");
       }
       printf("%d: pte %p pa %p\n", i, pte, pa);
       // PTE without any WRX bit set points to low-level page table
       if ((pte & (PTE_W|PTE_R|PTE_X)) == 0)
         vmprint_recursive((pagetable_t)pa, level + 1);
     }
   }
 }
 ​
 void 
 vmprint(pagetable_t pagetable)
 {
   printf("page table %p\n", pagetable);
   vmprint_recursive(pagetable, 0);
 }

注意在 kernel/exec.c 中并未引用 kernel/vm.c ,那么 kernel/exec.c 中执行 vmprint() 函数应该会报错,我们应在 kernel/def.h 文件中增加一个函数定义(你问怎们知道在这个文件里增加?问就是对着 kernel/exec.c 包含的头文件一个一个找的):

arduino 复制代码
 // kernel/def.h
 // ...
 // vm.c
 void            kvminit(void);
 void            kvminithart(void);
 void            kvmmap(pagetable_t, uint64, uint64, uint64, int);
 int             mappages(pagetable_t, uint64, uint64, uint64, int);
 pagetable_t     uvmcreate(void);
 void            uvminit(pagetable_t, uchar *, uint);
 uint64          uvmalloc(pagetable_t, uint64, uint64);
 uint64          uvmdealloc(pagetable_t, uint64, uint64);
 int             uvmcopy(pagetable_t, pagetable_t, uint64);
 void            uvmfree(pagetable_t, uint64);
 void            uvmunmap(pagetable_t, uint64, uint64, int);
 void            uvmclear(pagetable_t, uint64);
 uint64          walkaddr(pagetable_t, uint64);
 int             copyout(pagetable_t, uint64, char *, uint64);
 int             copyin(pagetable_t, char *, uint64, uint64);
 int             copyinstr(pagetable_t, char *, uint64, uint64);
 void            vmprint(pagetable_t pagetable);
 ​
 // plic.c
 // ...

此时,启动 xv6 ,即可看到打印出来的页表。

相关推荐
尼尔森系3 小时前
排序与算法:希尔排序
c语言·算法·排序算法
GEEK.攻城狮4 小时前
使用VSCODE开发C语言程序
c语言·ide·vscode
Victoria.a5 小时前
数组和指针常见笔试题(深度剖析)
c语言·开发语言
别说我什么都不会6 小时前
鸿蒙轻内核M核源码分析系列九 互斥锁Mutex
操作系统·harmonyos
xiaohai@Linux6 小时前
ESP32 在IDF_V5.3.1版本下实现AP无线热点模式!(带WIFI事件处理)
c语言·嵌入式硬件·tcp/ip·wifi·esp32
L_09077 小时前
【C】初阶数据结构6 -- 队列
c语言·开发语言·数据结构
charlie1145141917 小时前
(萌新入门)如何从起步阶段开始学习STM32 —— 0.碎碎念
c语言·stm32·单片机·嵌入式硬件·学习·教程
极客代码8 小时前
C语言中的链表封装
c语言·开发语言·数据结构·链表
L_09078 小时前
【C】栈的应用
c语言·数据结构·算法
不灭锦鲤9 小时前
c语言(函数)
c语言·c++·算法