lab3.1--vmprint【零基础向】

MIT 6.s081 lab3.1–printpgtbl

No.1 写在前面的话

从这个实验开始,难度上升,一开始就得知大名鼎鼎的lab3最难,果不其然,我踩了很多坑,并且因为我的能力有限,只做了前两个实验(第三个完全看不懂orz),话虽如此,不要畏难,开始前进!

No.2 初识页表

提示中的添加函数和语句不再赘述,我们在这里直接先分析freewalk,首先参看注释,上面写道——

// Recursively free page-table pages.

//递归释放页表页面

// All leaf mappings must already have been removed.

//所有叶映射必须已被删除

接下来查看具体代码:

void
freewalk(pagetable_t 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) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){
      // this PTE points to a lower-level page table.
      //最后一层页表中页表项rwx起码有一位置一
      //注意运算符优先级!【==后才&&】
      //若两者为真,说明不在最后一层
      uint64 child = PTE2PA(pte);
      //child为其页表条目提取的物理地址
      freewalk((pagetable_t)child);
      pagetable[i] = 0;
      //递归释放
    } else if(pte & PTE_V){
      //如果满足页表条目有效,为最后页表
      //叶映射没有被删除
      panic("freewalk: leaf");
    }
  }
  kfree((void*)pagetable);
}

大致意思明白了,判断有效位和rwx位,查看是否是最后一层,不是则递归,如果是则释放。但是因为并不清楚页表条目的物理地址是怎样的,好奇的添加printf打印一下:

if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){
      uint64 child = PTE2PA(pte);
      printf("child is %p\n", child);

结果如图:

Imgur

通过用gdb打断点和观测,观察页表的分层发现:67000是二级页表,66000是三级,再往后继续执行,似乎页表存放页不是连续的?两页有效的pte之中会存在很多的空条目,真是神奇。

No.3 实现vmprint

那么现在我们已经可以利用for循环递归遍历页表了,初版如下:

void
vmprint(pagetable_t pagetable)
{
  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);
      vmprint((pagetable_t)child);
      pagetable[i] = 0;
    }
  }
  kfree((void*)pagetable);
}

只差另一个问题:打印[…]

不得不说,这个问题困扰了我很长时间。一开始,我认为在递归前打印[…]就好,于是在pte_t pte = pagetable[i];后写了一段printf,结果事与愿违,无限的[…]填满了qemu,让我不知如何是好;于是又返回题目要求,仔细阅读这里:【每个PTE行都有一些“..”的缩进表明它在树中的深度。】深度……也就是说,每进入一层多打一次[…],然后深度加一继续,自然而然想到for循环,设置变量记录深度;可是题目所给的vmprint只提供页表不负责记录深度,递归时怎么办呢?

那么,就需要让vmprint成为一个过渡函数,根据题目要求,代码如下:

void
vmprint(pagetable_t pagetable){
  printf("page table %p\n", pagetable);
  Pritbl(pagetable, 1);//设置深度为1,0也可以
}

一切准备就绪,因为题目要求不要打印无效的PTE,因此,分为两个部分,有效和无效,有效则里面进行递归,无效跳过,最终代码如下:

void
Pritbl(pagetable_t pagetable, uint64 level){
  for(int i = 0; i < 512; ++i){
    pte_t pte = pagetable[i];

    //拼接[..]
    if(pte & PTE_V)
    {
      for (int j = 0; j < level; ++j)
      {
        if(j == 0) printf("..");
        else printf(" ..");
      }
      
      uint64 child = PTE2PA(pte);
      printf("%d: pte %p pa %p\n", i, pte, child);

      //如果页表还有深度,递归
      if((pte & (PTE_R|PTE_W|PTE_X)) == 0){
        Pritbl((pagetable_t)child, level+1);
        //为什么用++level,level++结果完全不同,只有level+1可以?
      }
      //上述步骤都是在页表【存在】的条件下进行
    }

    if((pte & PTE_V) == 0) continue;
    //题目提示不要打印无效pte,无效的pte会无限打印
  }

}

void
vmprint(pagetable_t pagetable){
  printf("page table %p\n", pagetable);
  Pritbl(pagetable, 1);
}

记得将两个函数添加到defs.h。

完美解决。【用时多半天】

No.4 补充知识

因为lecture7是页表实验的解答,在这里我将与vmprint相关的内容放入,以便了解更加深刻,首先是进程页表结构和打印结果:

Imgur

Q1:trampoline和trapframe位于顶部,为何在在这里位于第一个根页面?且索引为255?

A:不需要更多内存,在此处已经足够。

Q2:数据与文本为什么合并在一页?

A:为了xv6尽可能简单,实际上现在的操作系统会将它们隔离开来。

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值