BUAA_OS lab2 难点梳理

实验重点

所列出的实验重点为笔者在进行lab2过程中认为需要深刻理解的部分。

  1. 进行内存访问的流程

  2. 熟悉mips内存映射布局,即理解mmu.h内图

  3. 二级页表的理解和实现

以下将参考指导书逻辑,对于重难点进行梳理。

内存访问

首先,简易梳理内存访问流程。

  1. TLB根据虚拟地址查找

  2. 若存在,在cache中查找;若不存在,按照页表查询,再查cache,更新tlb

  3. 若cache命中则ok;若未命中,进行页面替换

内存布局及初始化步骤的理解

lab2主要涉及的内存布局图如下:

  • kuseg:用户态可用地址,需要mmu进行地址转换

  • kseg0:内核地址,转换不需要mmu,只需要将最高位清0

与内存布局密切相关的,就是初始化部分的各个函数,包括创建二级页表的部分。

我们以mips_vm_init()展开理解初始化的各个步骤。

 1  void mips_vm_init()
2 {
3 extern char end[];
4 extern int mCONTEXT;
5 extern struct Env *envs;
6 ​
7 Pde *pgdir;
8 u_int n;
9 ​
10 /* Step 1: Allocate a page for page directory(first level page table). */
11 pgdir = alloc(BY2PG, BY2PG, 1);
12 printf("to memory %x for struct page directory.\n", freemem);
13 mCONTEXT = (int)pgdir;
14 ​
15 boot_pgdir = pgdir;
16 ​
17 /* Step 2: Allocate proper size of physical memory for global array `pages`,
18 * for physical memory management. Then, map virtual address `UPAGES` to
19 * physical address `pages` allocated before. For consideration of alignment,
20 * you should round up the memory size before map. */
21 pages = (struct Page *)alloc(npage * sizeof(struct Page), BY2PG, 1);
22 printf("to memory %x for struct Pages.\n", freemem);
23 n = ROUND(npage * sizeof(struct Page), BY2PG);
24 boot_map_segment(pgdir, UPAGES, n, PADDR(pages), PTE_R);;
25 /* Step 3, Allocate proper size of physical memory for global array `envs`,
26 * for process management. Then map the physical address to `UENVS`. */
27 envs = (struct Env *)alloc(NENV * sizeof(struct Env), BY2PG, 1);
28 n = ROUND(NENV * sizeof(struct Env), BY2PG);
29 boot_map_segment(pgdir, UENVS, n, PADDR(envs), PTE_R);
30 ​
31 printf("pmap.c:\t mips vm init success\n");
32 }

mips_vm_init

  • 首先,调用alloc函数为pgdir开出一块空间。在此需要理解,alloc函数的本质就是将freemem上移,以表示预留空间。在执行完这一条alloc后,freemem的值由end[](0x80400000)增加为0x80401000

  • 然后,调用alloc函数为pages开出一块空间。需要注意的是,pages是用来记录各物理页信息的Page结构体数组,可以根据某Page在pages中的偏移量,间接求出对应的物理页地址。此时,freemem再次增加。与pgdir不同的是,紧接着又调用了boot_map_segment()函数。其作用下文中再叙述。

  • 最后,与第二步相似,为envs先alloc再map。

接下来,我们看一下boot_map_segment()是用来干啥的。

 1  void boot_map_segment(Pde *pgdir, u_long va, u_long size, u_long pa, int perm)
2 {
3 int i, va_temp;
4 Pte *pgtable_entry;
5
6 /* Step 1: Check if `size` is a multiple of BY2PG. */
7
8 if(size%BY2PG!=0){
9 return;
10 }
11
12 /* Step 2: Map virtual address space to physical address. */
13 /* Hint: Use `boot_pgdir_walk` to get the page table entry of virtual address `va`. */
14
15 for(i=0;i<size;i+=BY2PG){
16 pgtable_entry=boot_pgdir_walk(pgdir,va+i,1);
17 *pgtable_entry=(pa+i)|perm|PTE_V;
18 }
19 return;
20 }

boot_map_segment

可以看出,boot_map_segment()的作用就是将[va, va+size)的虚拟地址和[pa, pa+size)的物理地址建立映射关系。通俗来讲,就是将虚拟地址va对应的页表项写入需要对应的pa的值,并设置标志位。

具体实现为,通过boot_pgdir_walk()获取地址为va+i对应的页表项,然后修改它的值。

那么自然而然,我们再来看一下boot_pgdir_walk()是怎么找到va+i对应的页表项地址的。

 1 static Pte *boot_pgdir_walk(Pde *pgdir, u_long va, int create)
2 {
3 ​
4 Pde *pgdir_entryp;
5 Pte *pgtable, *pgtable_entry;
6
7 /* Step 1: Get the corresponding page directory entry and page table. */
8 /* Hint: Use KADDR and PTE_ADDR to get the page table from page directory
9 * entry value. */
10 pgdir_entryp = pgdir+PDX(va); //获取一级页表项的虚拟地址
11 pgtable=KADDR(PTE_ADDR(*pgdir_entryp)); //获取二级页表入口的虚拟地址
12 /* Step 2: If the corresponding page table is not exist and parameter `create`
13 * is set, create one. And set the correct permission bits for this new page
14 * table. */
15 if((*pgdir_entryp & PTE_V)==0 && create){ //如果没有二级页表,且需要创建
16 pgtable = alloc(BY2PG,BY2PG,1); //创建二级页表
17 *pgdir_entryp = PADDR(pgtable)|PTE_V; //将指向该二级页表的一级页表项的值设置为其物理地址
18 }
19 /* Step 3: Get the page table entry for `va`, and return it. */
20 pgtable_entry=pgtable+PTX(va); //返回指向对应二级页表项地址的指针
21 return pgtable_entry;
22 ​
23 }

boot_pgdir_walk

该函数的具体行为已体现在注释中了,不再赘述。

看到这个boot_pgdir_walk()函数在寻找二级页表项的时候,可能会感觉被虚拟和物理地址的转换绕晕了,那么就来捋一下它究竟是根据什么地址找到的页表项吧。

首先需要明确,在想要访问页表的时候,无论是一级还是二级,都用的虚拟地址;而一级页表中存的二级页表地址和二级页表中存的页地址,都是物理地址。

明确这一点之后,以下这句就不难理解了。pgdir中存的是物理地址,但需要转化成虚拟地址访问。其他类似。

 pgtable=KADDR(PTE_ADDR(*pgdir_entryp));

另外,我们会发现,在需要访问的二级页表不存在时,同样调用了alloc,上移freemem,为新页开出空间。这是因为,我们采用的二级页表是动态的,需要哪个就装入哪个,而不是将所有二级页表都放入内存,因为这样太占空间了。

到此位置,初始化部分就完成一大半了,这时候只需要再调用page_init()函数,将此时freemem以下的部分都设置p->pp_ref=1,即该物理页被使用了。因此,根据freemem上移的顺序,物理内存的最底端为pgdir,其次为pages,envs,后来alloc的页等等。

页面置换

在完成初始化之后,进行之后的页面插入、删除、分配、置换就变得容易多了。接下来,就以page_insert()函数来梳理一下后期相关的页面操作。

 1 int
2 page_insert(Pde *pgdir, struct Page *pp, u_long va, u_int perm)
3 {
4 u_int PERM;
5 Pte *pgtable_entry;
6 PERM = perm | PTE_V;
7 /* Step 1: Get corresponding page table entry. */
8 pgdir_walk(pgdir, va, 0, &pgtable_entry);
9 ​
10 if (pgtable_entry!=0 &&(*pgtable_entry&PTE_V)!= 0) {
11 if (pa2page(*pgtable_entry) != pp) {
12 page_remove(pgdir, va);
13 } else {
14 tlb_invalidate(pgdir, va);
15 *pgtable_entry = (page2pa(pp) | PERM);
16 return 0;
17 }
18 }
19 ​
20 /* Step 2: Update TLB. */
21 ​
22 /* hint: use tlb_invalidate function */
23 tlb_invalidate(pgdir,va);
24 ​
25 /* Step 3: Do check, re-get page table entry to validate the insertion. */
26 ​
27 int x = pgdir_walk(pgdir, va, 1, &pgtable_entry);
28 /* Step 3.1 Check if the page can be insert, if can鈥檛 return -E_NO_MEM */
29 if(x==-E_NO_MEM){
30 return -E_NO_MEM;
31 }
32 // printf("0x%x\n",PTE_ADDR(pgdir[0]));
33 /* Step 3.2 Insert page and increment the pp_ref */
34 *pgtable_entry=(page2pa(pp)|PERM);
35 pp->pp_ref+=1;
36 return 0;
37 }

page_insert

第一步是使用pgdir_walk()函数,获取va所对应的二级页表项。由此看来,pgdir_walk()与之前初始化部分提到的boot_pgdir_walk()作用基本相同呢。然不同之处在于,在调用page_insert()时,内存初始化部分已经完成,空闲页表已经使用page_free_list串起来了,因此再分配新页面的时候,直接取出空页即可。

之后的步骤,就是对内存访问的具体步骤的实现了。

1 . . 感言

至此,lab2的部分基本就结束了。回想完成lab2的时候,脑子还是一团浆糊,对于许多操作都很不理解。如今回头写总结,才发现主要就是对于初始化部分的具体行为理解不清。

另外,lab2对queue.h部分的操作不涉及理解难度,就是指针复(chong)习(xue),注意指针别飞了,就没有大问题。

(代码仓库位于右上角Github)

BUAA_OS lab2 难点梳理的更多相关文章

  1. BUAA_OS lab3 难点梳理

    BUAA_OS lab3 难点梳理 实验难点 进程创建 对于初始化部分,首先需要在pmap.c中修改mips_vm_init()函数,为envs开空间,并map到UENVS空间. 其次,模仿page_ ...

  2. BUAA_OS lab4 难点梳理

    BUAA_OS lab4 难点梳理 lab4体会到了OS难度的飞升.实验需要掌握的重点有以下: 系统调用流程 进程通信机制 fork 本lab理解难度较高,接下来将以以上三部分分别梳理. 系统调用 概 ...

  3. Collection集合重难点梳理,增强for注意事项和三种遍历的应用场景,栈和队列特点,数组和链表特点,ArrayList源码解析, LinkedList-源码解析

    重难点梳理 使用到的新单词: 1.collection[kəˈlekʃn] 聚集 2.empty[ˈempti] 空的 3.clear[klɪə(r)] 清除 4.iterator 迭代器 学习目标: ...

  4. HRMS(人力资源管理系统)-从单机应用到SaaS应用-系统介绍

    上周发布的<2018,全新出发(全力推动实现住有所居)>文章,其中记录了个人在这5年过程中的成长和收获,有幸认识了不少博客园的朋友,大家一起学习交流,在这个过程当中好多朋友提出SaaS系统 ...

  5. 《BAT前端进阶[师徒班]》学习总结

    这是一个培训课 是的,这是一个面向中级前端的培训班,但明显跟传统的填鸭式培训班不太一样.这边的老师都是大牛这是毫无疑问的,而且都是一线开发人员.而且课程一开始就说明了面向了是有1-3年有工作经验的前端 ...

  6. 什么是泛型?,Set集合,TreeSet集合自然排序和比较器排序,数据结构-二叉树,数据结构-平衡二叉树

    ==知识点== 1.泛型 2.Set集合 3.TreeSet 4.数据结构-二叉树 5.数据结构-平衡二叉树 ==用到的单词== 1.element[ˈelɪmənt] 要素 元素(软) 2.key[ ...

  7. 什么是可变参数?如何创建不可变集合?Steam三类方法是什么?获取流方法特点?流中间方法特点?终结流方法特点?

    ==知识梳理== ==重难点梳理== ==今日目标== 1.能够了解什么是可变参数 2.能够了解如何去创建不可变集合 3.能够掌握Stream流的使用 ==知识点== 1.可变参数 2.Stream流 ...

  8. 多线程,线程类三种方式,线程调度,线程同步,死锁,线程间的通信,阻塞队列,wait和sleep区别?

    重难点梳理 知识点梳理 学习目标 1.能够知道什么是进程什么是线程(进程和线程的概述,多进程和多线程的意义) 2.能够掌握线程常见API的使用 3.能够理解什么是线程安全问题 4.能够知道什么是锁 5 ...

  9. JDBC基础:JDBC快速入门,JDBC工具类,SQL注入攻击,JDBC管理事务

    JDBC基础 重难点梳理 一.JDBC快速入门 1.jdbc的概念 JDBC(Java DataBase Connectivity:java数据库连接)是一种用于执行SQL语句的Java API,可以 ...

随机推荐

  1. css 使用paint创建自定义css

    See also: https://houdini.how/ https://github.com/una/extra.css#readme

  2. nasm astrstr函数 x86

    xxx.asm: %define p1 ebp+8 %define p2 ebp+12 %define p3 ebp+16 section .text global dllmain export as ...

  3. 新年狂欢高倍币,1万枚VAST拿到手软

    新一年NGK推出了新币VAST,成为了当前最瞩目的一颗星.有人认为VAST有价无市,有人认为VAST价值巨大,潜力无限.总之,众说风云! 选择数字货币,当然是挑选有价值的货币,从内外价值入手. 一.币 ...

  4. NGK的发行量是多少?NGK销毁机制是怎么样的?

    代币销毁(Coin Burning),是指将代币从流通中永久性去除.换句话说,被销毁的代币相当于被永久性冻结,再也无法流入市场.那为什么要进行代币销毁呢? 销毁加密货币,可以使剩余加密货币的价值升高, ...

  5. Vue学习笔记-django-cors-headers安装解决跨域问题

    一  使用环境: windows 7 64位操作系统 二  jango-cors-headers安装解决跨域问题(后端解决方案) 跨域,指的是浏览器不能执行其他网站的脚本.它是由浏览器的同源策略造成的 ...

  6. 微信小程序:列表渲染

    wx:for,(wx:for-item,wx:for-index),wx:key. 列表循环包括数组循环和对象循环 一.数组循环 此时控制台报错如下:属性"wx:key"可以提高性 ...

  7. MYSQL索引优化法则

    目录 一首诗送给各位: 全值匹配我最爱,最左前缀要遵守: 带头大哥不能死,中间兄弟不能断: 索引列上少计算,范围之后全失效: Like百分写最右,覆盖索引不写星: 不等空值还有or,索引失效要少用: ...

  8. 看完我的笔记不懂也会懂----MarkDown使用指南

    目录 语法 [TOC] 自动生成目录 1. 标题 2. 文本强调 3. 列表 4. 图片 5. 超链接 6. 文本引用 7. 分割线 8. 代码 9. 任务列表 (MPE专属) 10. 表格 11. ...

  9. JSP, EL, JSTL的使用

    JSP基础指令和语法 回顾 在Jsp页面: 只要是Java代码就会原封不动的输出, 如果是html代码,就会转义为 out.write("<html>\r\n") 这样 ...

  10. ZooKeeper 的选举机制,你了解多少?

    本文作者:HelloGitHub-老荀 Hi,这里是 HelloGitHub 推出的 HelloZooKeeper 系列,免费开源.有趣.入门级的 ZooKeeper 教程,面向有编程基础的新手. 项 ...