文章目錄
  1. 1. 引言
  2. 2. 物理载体
  3. 3. 二级指针的妙用
  4. 4. 总结
  5. 5. 引用

引言

前面已经通过lab1的这篇博文了解了内存分页的实现细节,接下来就谈谈如何具体实现内存分页

物理载体

通过了解KERNBASE对操作系统的影响这篇博文我们知道,其实内存分页就是完成对物理存在的一种分割和隔离,所以我们在完成内存分页系统设计之前必须要构建一个载体,完成对物理存在的一种表示。

在xv6中声明一个动态数组来代表物理内存,每一个值代表一块4k的内存页。我们主要通过offset - base得到偏移倍数,每个偏移量为4K,也就是通过(offset - base ) << 12得到物理地址,我们看一下这个数组成员:PageInfo结构体

struct PageInfo {
    struct PageInfo *pp_link;
    uint16_t pp_ref;
};

主要存在两个值:一个为下一个可用的地址指针,一个为引用次数。

引用次数比较好理解,但是这个pp_link有什么用呢。其实你可用把这个结构体看做成一个由链表组成的堆栈,我们只需要保留栈顶值(page_free_list),由于它保存下一个值地址,这样通过不断的push、pop,就能维持一个可用物理内存栈。

二级指针的妙用

由于前面的博客原理已经介绍的很详细了,我就不再累赘了,在这里我提一下源码中二级指针的妙用,虽然它只有短短几行,但是运行的结果却是让人大开眼界,体会到指针的神奇威力。

这段代码出现在kern/pmap.ccheck_page_free_list函数中

if (only_low_memory) {
    // Move pages with lower addresses first in the free
    // list, since entry_pgdir does not map all pages.
    struct PageInfo *pp1, *pp2;
    struct PageInfo **tp[2] = { &pp1, &pp2 };
    for (pp = page_free_list; pp; pp = pp->pp_link) {
        int pagetype = PDX(page2pa(pp)) >= pdx_limit;
        *tp[pagetype] = pp;
        tp[pagetype] = &pp->pp_link;
    }
    *tp[1] = 0;
    *tp[0] = pp2;
    page_free_list = pp1;
}

主要的作用是将“栈底”的元素移到“栈顶”,首先它使用了两个一级指针(pp1、pp2),还有两个二级指针分别指向(pp1、pp2)

首先int pagetype = PDX(page2pa(pp)) >= pdx_limit;判断物理地址是否为大于4M还是小于4M,我们把物理内存页分成两组

  • 小于4M A组
  • 大于4M B组

对于小于4M的组,分两种情况

  1. 第一个小于4M的内存页(page1)
  1. *tp[0] (也就是pp1) 存贮了pp的值,也就是pp1 = page1
  2. tp[0] 存贮了pp -> link 的地址(这个没有什么用)
  1. A组第二个以及以后的内存页(page2)
  1. 上一个地址的值等于pp(没什么用)
  2. tp[0] 存贮了下一个空闲地址的值

对于B组来说也是一样的,最重要的是for循环结束后实现的交换

*tp[1] = 0; 

B组最后一个的pp_link地址地址设置为NULL,也就相当于把他放到栈底

*tp[0] = pp2;

A组最后一个变成pp_link地址设置pp2,也就是B组的第一个接到了A组的最后面去了

page_free_list = pp1;

栈顶变成A组第一个,通过这样的“乾坤大挪移”术就将A组部分移到B组前面去了,也就实现了先使用低地址的物理内存的作用

总结

理解内存分页必须要了解背后的原理,了解了原理看具体实现的时候才能事半功倍。

引用

文章目錄
  1. 1. 引言
  2. 2. 物理载体
  3. 3. 二级指针的妙用
  4. 4. 总结
  5. 5. 引用