本次实验主要完成ucore内核对物理内存的管理。

ucore被启动后,需要探测系统的物理内存布局来了解哪些物理内存空间是可用的。ucore是使用e820h中断来获取内存信息,而这个中断必须在实模式下使用,因此必须在bootloader引导进入保护模式前进行,这些收集到的数据将保存在物理地址0x8000处,通过代码中定义的e820map结构体进行映射。

启动分页机制

ucore在80386中的分页机制实现了基本平坦模型的段页式内存管理,这是为后续的虚拟内存做好准备。

CR开头的寄存器为控制寄存器。

CR0寄存器包含系统控制标志,这些标志控制着处理器的运行模式和状态。分页(CR0 的第 31 位)。置 1 启用分页,置 0 不启用分页。当禁用分页 时,所有的线性地址都当作物理地址对待。

CR3寄存器包含页目录表的物理基地址和二个标志(PCD和PWT)。该寄存器也被称为页目录基地址寄存器(PDBR)。ucore中用boot_cr3(mm/pmm.c)来记录这个值。

程序中使用的地址都是逻辑地址,逻辑地址/虚拟地址通过GDT/LDT可以转换为线性地址,线性地址可以通过页表来转化为物理地址。

lab2在运行中分为了4个阶段:

1、bootloader阶段,此时virt addr = linear addr = phy addr

2、第二个阶段从kern_entry开始,但是还没执行enable_paging,尚未开启分页机制之前,线性地址都是等于物理地址。此时虚拟地址到线性地址的映射更新了,新的映射关系为 virt addr - 0xC0000000 = linear addr = phy addr

3、第三个阶段从enable_paging函数开始,到执行gdt_init函数。执行完enable_paging函数中的加载CR0指令,即让CR0寄存器中的PG位置1,启动分页,接下来就是段页式的映射关系了。此时段映射还没有为分页机制的开启而再次更新调整,所以映射关系比较微妙,如下所示

virt addr - 0xC0000000 = linear addr = phy addr + 0xC0000000# 物理地址 在0~4MB之外的三者映射关系

virt addr - 0xC0000000 = linear addr = phy addr# 物理地址在0~4MB之内的三者的映射关系

如上的特殊关系是因为pmm_init中的一条代码:

boot_pgdir[0] = boot_pgdir[PDX(KERNBASE)];因为页目录表中每一项代表一个页表,一个页表可以管理4MB的内存大小,因为pgdir中每一项表示4MB的物理内存地址。即线性地址从0开始的4MB和从KERNBASE开始的4MB通过分页机制映射到同一段内存空间中。

4、从get_init函数开始,此时段映射更新调整,并取消了临时的映射关系,形成了我们期望的映射关系。映射关系为virt addr = linear addr = phy addr + 0xC0000000

ucore中页的大小为4KB,每个页都是使用Page数据结构来表示。

struct Page{
int ref;//页帧的引用次数,若有一个虚拟页映射到此页上,则加一
uint32_t flags;//表示该页帧的状态
unsigned int property;//连续内存空闲块的大小,连续空闲块的首个页帧才会设置此属性
list_entry_t page_link;//通用数据结构,链表查找用的。
}

ucore使用了free_area_t这个数据结构进行管理大量零散的空闲连续内存块。

typedef struct{
list_entry_t free_list;//首个空闲连续内存快的entry
unsigned int nr_free;//此链表中的连续空闲块的个数
}free_area_t;
npage=maxpa/PGSIZE;//得到需要管理的物理页个数
pages=(struct Page *)ROUNDUP((void *)end,PGSIZE);//这个地址是bootloader加载ucore的结束地址后以PGSIZE对齐后的地址。
uintptr_t freemem = PADDR((uintptr_t)pages + sizeof(struct Page) * npage);//由于管理内存的所有页帧还需要npage个Page数据结构所以还需要腾出内存空间给Page,后续的内存地址即为空闲物理内存空间。
/* pmm_init - initialize the physical memory management */
static void
page_init(void) {struct e820map *memmap = (struct e820map *)(0x8000 + KERNBASE);//指针寻址使用的是线性虚拟地址。uint64_t maxpa = 0;
​cprintf("e820map:\n");int i;//遍历memmap中的每一项for (i = 0; i < memmap->nr_map; i ++) {uint64_t begin = memmap->map[i].addr, end = begin + memmap->map[i].size;cprintf(" memory: %08llx, [%08llx, %08llx], type = %d.\n",memmap->map[i].size, begin, end - 1, memmap->map[i].type);if (memmap->map[i].type == E820_ARM) {if (maxpa < end && begin < KMEMSIZE) {maxpa = end;}}}//如果maxpa超过了定义约束的最大可用物理内存空间,则进行调整if (maxpa > KMEMSIZE) {maxpa = KMEMSIZE;}extern char end[];//end是ucore kernel加载后定义的第二个全局变量,该变量所在内存地址后续空间均没有被使用,因此以该地址为起点,存放用于管理物理内存的Page数据结构。
​npage = maxpa / PGSIZE;pages = (struct Page *)ROUNDUP((void *)end, PGSIZE);
​for (i = 0; i < npage; i ++) {SetPageReserved(pages + i);//遍历每一个物理页,默认标记为保留}
​uintptr_t freemem = PADDR((uintptr_t)pages + sizeof(struct Page) * npage);
​for (i = 0; i < memmap->nr_map; i ++) {uint64_t begin = memmap->map[i].addr, end = begin + memmap->map[i].size;if (memmap->map[i].type == E820_ARM) {if (begin < freemem) {//限制空闲地址的最小值begin = freemem;}if (end > KMEMSIZE) {//限制空闲地址的最大值end = KMEMSIZE;}if (begin < end) {//用PGSIZE对齐地址begin = ROUNDUP(begin, PGSIZE);end = ROUNDDOWN(end, PGSIZE);if (begin < end) {//空闲内存块的映射,将其纳入物理内存管理器中,用于后续的物理内存管理init_memmap(pa2page(begin), (end - begin) / PGSIZE);}}}}
}

代码部分

pte_t *
get_pte(pde_t *pgdir, uintptr_t la, bool create) {// 该函数通过线性地址来找到找到对应的页表项(二级页表项),并返回这个页表项的虚拟地址// 获得指定页目录表项的地址pde_t *pdep = &pgdir[PDX(la)];// 判断当前页目录项的Present存在位是否为1(对应的二级页表是否存在)if (!(*pdep & PTE_P)) {// 对应的二级页表不存在// *page指向的是这个新创建的二级页表基地址struct Page *page;if (!create || (page = alloc_page()) == NULL) {// 如果create参数为false或是alloc_page分配物理内存失败return NULL;}// 二级页表所对应的物理页 引用数为1set_page_ref(page, 1);// 获得被page变量管理的页帧的物理地址uintptr_t pa = page2pa(page);// 将上述指定页帧全部填满0,函数的参数要求是虚拟地址,因此需要将物理地址转化为虚拟地址memset(KADDR(pa), 0, PGSIZE);// la对应的一级页目录项进行赋值,使其指向新创建的二级页表(页表中的数据被MMU直接处理,为了映射效率存放的都是物理地址)// 或PTE_U/PTE_W/PET_P 标识当前页目录项是用户级别的、可写的、已存在的*pdep = pa | PTE_U | PTE_W | PTE_P;}// 要想通过C语言中的数组来访问对应数据,需要的是数组基址(虚拟地址),而*pdep中页目录表项中存放了对应二级页表的一个物理地址// 由于内存中的每个页帧都是4KB大小整齐分配,因此每个页帧的物理地址的低12位必然都是0,这低12位有其他作用和二级页表的地址无关//PDE_ADDR将*pdep的低12位抹零对齐(指向二级页表的起始基地址),再通过KADDR转为内核虚拟地址,进行数组访问// PTX(la)获得la线性地址的中间10位部分,即二级页表中对应页表项的索引下标。这样便能得到la对应的二级页表项了return &((pte_t *)KADDR(PDE_ADDR(*pdep)))[PTX(la)];
}

分配物理内存页的功能由default_alloc_pages函数完成

/*** 接受一个合法的正整数参数n,为其分配N个物理页面大小的连续物理内存空间.* 并以Page指针的形式,返回最低位物理页(最前面的)。* * 如果分配时发生错误或者剩余空闲空间不足,则返回NULL代表分配失败* */
static struct Page *
default_alloc_pages(size_t n) {    assert(n > 0);if (n > nr_free) {return NULL;}struct Page *page = NULL;list_entry_t *le = &free_list;// 遍历空闲链表while ((le = list_next(le)) != &free_list) {// 将le节点转换为关联的Page结构struct Page *p = le2page(le, page_link);if (p->property >= n) {// 发现一个满足要求的,空闲页数大于等于N的空闲块page = p;break;}}// 如果page != null代表找到了,分配成功。反之则分配物理内存失败if (page != NULL) {if (page->property > n) {// 如果空闲块的大小不是正合适(page->property != n)// 按照指针偏移,找到按序后面第N个Page结构pstruct Page *p = page + n;// p其空闲块个数 = 当前找到的空闲块数量 - np->property = page->property - n;SetPageProperty(p);// 按对应的物理地址顺序,将p加入到空闲链表中对应的位置list_add_after(&(page->page_link), &(p->page_link));}// 在将当前page从空间链表中移除list_del(&(page->page_link));// 闲链表整体空闲页数量自减nnr_free -= n;// 清楚page的property(因为非空闲块的头Page的property都为0)ClearPageProperty(page);}return page;
}
static inline void
page_remove_pte(pde_t *pgdir, uintptr_t la, pte_t *ptep) {if (*ptep & PTE_P) {// 如果对应的二级页表项存在// 获得*ptep对应的Page结构struct Page *page = pte2page(*ptep);// 关联的page引用数自减1if (page_ref_dec(page) == 0) {// 如果自减1后,引用数为0,需要free释放掉该物理页free_page(page);}// 清空当前二级页表项(整体设置为0)*ptep = 0;// 由于页表项发生了改变,需要TLB快表tlb_invalidate(pgdir, la);}
}

释放物理内存页的功能由default_free_pages函数完成

/*** 释放掉自base起始的连续n个物理页,n必须为正整数* */
static void
default_free_pages(struct Page *base, size_t n) {assert(n > 0);struct Page *p = base;// 遍历这N个连续的Page页,将其相关属性设置为空闲for (; p != base + n; p ++) {assert(!PageReserved(p) && !PageProperty(p));p->flags = 0;set_page_ref(p, 0);}// 由于被释放了N个空闲物理页,base头Page的property设置为nbase->property = n;SetPageProperty(base);// 下面进行空闲链表相关操作list_entry_t *le = list_next(&free_list);// 迭代空闲链表中的每一个节点while (le != &free_list) {// 获得节点对应的Page结构p = le2page(le, page_link);le = list_next(le);if (base + base->property == p) {// 如果当前base释放了N个物理页后,尾部正好能和Page p连上,则进行两个空闲块的合并base->property += p->property;ClearPageProperty(p);list_del(&(p->page_link));}else if (p + p->property == base) {// 如果当前Page p能和base头连上,则进行两个空闲块的合并p->property += base->property;ClearPageProperty(base);base = p;list_del(&(p->page_link));}}// 空闲链表整体空闲页数量自增nnr_free += n;le = list_next(&free_list);// 迭代空闲链表中的每一个节点while (le != &free_list) {// 转为Page结构p = le2page(le, page_link);if (base + base->property <= p) {// 进行空闲链表结构的校验,不能存在交叉覆盖的地方assert(base + base->property != p);break;}le = list_next(le);}// 将base加入到空闲链表之中list_add_before(le, &(base->page_link));
}

ucore lab2学习笔记整理相关推荐

  1. ucore lab3学习笔记整理

    此实验借助页表机制和中断异常处理机制,完成page fault异常处理和fifo页替换算法的实现.ucore建立了mm_struct和vma_struct数据结构,描述了ucore模拟应用程序运行所需 ...

  2. ucore lab1学习笔记整理

    前置知识 ucore是运行在80386这一32位x86架构的CPU,汇编采用的格式是AT&T, lab1 ucore开始执行makefile会生成磁盘映像,这个磁盘映像就是对应着现实计算机中的 ...

  3. ucore lab4学习笔记整理

    在lab4中接触的还是内核线程,内核线程只运行在内核态.在线性空间中,0-3G是用户空间,而3-4G是内核空间,所有的内核线程都是只用同一块内存,因此不需要为每个内核线程维护单独的内存空间,不像用户进 ...

  4. 【mysql学习笔记整理】

    /*mysql学习笔记整理*/ /*常用的数据库操作对象*/ #库的操作 #创建 #数据库的创建 USE mysql; CREATE DATABASE db_x; #删除 #删除数据库 DROP DA ...

  5. Deep Learning(深度学习)学习笔记整理系列之(五)

    Deep Learning(深度学习)学习笔记整理系列 zouxy09@qq.com http://blog.csdn.net/zouxy09 作者:Zouxy version 1.0 2013-04 ...

  6. Deep Learning(深度学习)学习笔记整理系列之(二)

    Deep Learning(深度学习)学习笔记整理系列 zouxy09@qq.com http://blog.csdn.net/zouxy09 作者:Zouxy version 1.0 2013-04 ...

  7. python eval 入门_Python学习笔记整理3之输入输出、python eval函数

    Python学习笔记整理3之输入输出.python eval函数 来源:中文源码网    浏览: 次    日期:2018年9月2日 Python学习笔记整理3之输入输出.python eval函数 ...

  8. Deep Learning(深度学习)学习笔记整理系列之(八)

     Deep Learning(深度学习)学习笔记整理系列之(八) 分类: Deep Learning 机器学习 Linux驱动2013-04-10 11:4257652人阅读评论(25)收藏举报 ...

  9. Deep Learning(深度学习)学习笔记整理系列三

    Deep Learning(深度学习)学习笔记整理系列 声明: 1)该Deep Learning的学习系列是整理自网上很大牛和机器学习专家所无私奉献的资料的.具体引用的资料请看参考文献.具体的版本声明 ...

最新文章

  1. 【Qt】Qt5.9.0: error: GL/gl.h: 没有那个文件或目录
  2. 观《phonegap第三季 angularjs+ionic视频教程 实时发布》学习笔记(一)
  3. CCNA题库关于Frame-relay看图答题的解答
  4. Goldengate的拆分与合并
  5. mybatis中的#{value}和${value}的区别
  6. Tcpdump 详解
  7. 【工作感悟】成功入职阿里月薪45K
  8. MATLAB——PLOT绘图
  9. ApacheCN JavaScript 译文集(二) 20211123 更新
  10. 传统 Ajax 已死,Fetch 永生
  11. TensorFlow相关工具
  12. java jquery提交表单数据_[Java教程]jquery实现ajax提交表单信息
  13. 前端实现红包雨功能_微信隐藏的7个实用功能,你都知道吗?真的白玩这么久微信...
  14. 早期华为发售设备安装Play商店,安装服务框架谷歌Mate20,P30,Mate10,P20
  15. html怎么设置整体右对齐,如何在html中右对齐按钮
  16. 线代:1.7矩阵对角化二次型
  17. 苹果电脑无法自动修复计算机,苹果电脑没办法正常开机的解决方案
  18. coffeescript html5,深入浅出CoffeeScript
  19. 深度学习平台——常用图像标注工具
  20. 小红书榜单,五大行业图文笔记类

热门文章

  1. 令人抓狂的ORA-01722: 无效数字
  2. 让盖泡面的iPad成为你的副屏
  3. 时间控件My97DatePicker相关配置
  4. KL散度及Python实现
  5. 二嗨租车java,1嗨租车,租的放心用的更舒心
  6. 目前最详细的红黑树原理分析(大量图片+过程推导!!!)
  7. 玩转格式转换——.xml->.txt
  8. c语言4字节移位运算,深入懂得C语言中的移位运算
  9. 使用PL/SQL连接Linux下Oracle服务
  10. win10 实现多用户同时远程访问