跳过正文
  1. 博客/
  2. 随笔/
  3. 编程/

内存分页设计

3 分钟· ·
随笔 编程 MIT6.828
作者
Allen
一个强大、轻量级的 Hugo 主题。
目录

引言
#

前面已经通过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组前面去了,也就实现了先使用低地址的物理内存的作用

总结
#

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

引用
#

相关文章

内存分页
2 分钟
随笔 编程 MIT6.828
引导和操作系统的交互
3 分钟
随笔 编程 MIT6.828
什么是操作系统
8 分钟
随笔 编程 MIT6.828
从CS寄存器看段的前世今生
2 分钟
随笔 编程 MIT6.828
mit6.828课程总结
2 分钟
随笔 编程 MIT6.828
AVL树实现原理
2 分钟
随笔 编程