思考题

Thinking 2.1

  • 编写c程序时,指针变量存储的地址是虚拟地址;
  • 编写汇编程序时,lwsw指令使用的是虚拟地址。

Thinking 2.2

  1. 链表操作多项操作具有重复性,使用宏定义封装部分代码,并在链表操作时多次使用,可以大大提高可重用性和简便性。

  2. 不同链表比较:

    • 单向链表:可以很好的顺序进行,但是不可以访问当前元素之前的元素;
    • 双向链表:既可以正向进行,也可以逆向进行,可以直接访问当前元素之前的和之后的元素,性能较好;
    • 循环链表:可以很好的顺序进行,但是不可以直接访问当前元素之前的元素,需要循环查找(单向循环列表,双向循环列表性能与普通双向链表相当);

    本实验中设计的双向链表与经典双向链表不同,本实验中链表不能访问上一个元素,只能访问上一个元素的next指针;即本实验中链表无法访问上一个元素,只能改变上一个元素的连接关系。

Thinking 2.3

按照实验设计,选择C选项设计方案最佳:

1
2
3
4
5
6
7
8
9
10
11
12
struct Page_list
{
struct
{
struct
{
struct Page *le_next;
struct Page **le_prev;
} pp_link;
u_short pp_ref;
}* lh_first;
}

其中,pp_link代表链表的连接单元,内含两种指针,分别是*le_next指向链表下一个元素,**le_prev指向链表上一个元素的*le_next指针,可以直接改变连接顺序。pp_ref代表链表页块计数。*lh_first代表链表的初元素的地址。

Thinking 2.4

在一次任务中,可能会出现多个进程,同时还可能发生不同进程调用相同的虚拟内存。此时,asid可以保证不会出现不同进程互相错误调用物理地址的问题。同时,其也可以保证系统不会进错TLB,导致TLB发生更换映射,因此也可以提高TLB效率。

Thinking 2.5

tlb_invalidate函数如以下代码块所示:

1
2
3
4
void tlb_invalidate(u_int asid, u_long va) 
{
tlb_out((va & ~GENMASK(PGSHIFT, 0)) | (asid & (NASID - 1)));
}

tlb_out函数如以下代码块所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
LEAF(tlb_out)
.set noreorder
mfc0 t0, CP0_ENTRYHI //从EntryHi中获得参数,存入t0寄存器中
mtc0 a0, CP0_ENTRYHI //将a0中参数Key放入EntryHi寄存器中
nop
tlbp //根据EntryHi中的Key,查找TLB中与之对应的表项,并将表项的索引存入Index寄存器
nop
mfc0 t1, CP0_INDEX //从Index中获得表项索引,存入t1寄存器中
.set reorder
bltz t1, NO_SUCH_ENTRY //判断表项索引与0的关系,大于等于0时向下进行,小于0时跳转至NO_SUCH_ENTRY
.set noreorder
mtc0 zero, CP0_ENTRYHI //向EntryHi中写入0
mtc0 zero, CP0_ENTRYLO0 //向EntryLo0中写入0
mtc0 zero, CP0_ENTRYLO1 //向EntryLo1中写入0
nop
tlbwi //以Index寄存器中的值为索引,将此时EntryHiEntryLo0EntryLo1的值写到索引指定的TLB表项中
.set reorder

NO_SUCH_ENTRY:
mtc0 t0, CP0_ENTRYHI //向EntryHi中写入进入函数时原始的EntryHi中存放的值
j ra
END(tlb_out)
  • tlb_invalidate函数中调用了tlb_out函数。
  • tlb_invalidate函数主要实现在获得新的页表项进行更新时,对旧有表项进行删除。
  • 对函数tlb_out的解释如前文所示。

Thinking 2.6

X86 和 MIPS 在内存管理上主要有以下几点区别:

  • 地址形式:X86有三种地址形式:逻辑地址、线性地址、物理地址;MIPS有两种地址形式:虚拟地址、物理地址。
  • 内存管理机制:X86主要采用的是段页式管理系统,MIPS主要采用的是页式管理系统(并使用MMU管理内存)。
  • TLB 不命中时的处理:转换失败的虚拟地址,MIPS使用BadVAddr寄存器存放,X86使用CR2存放。
    • X86是以CR3作为当前进程的PGD基址,索引获得PFN后,直接输出PA。同时MMU会填充TLB以加快下次转换的速度。
    • MIPS 触发TLB Refill 异常,内核的 tlb_refill_handler 会以 pgd_current 为当前进程的 PGD 基址,索引获得转换失败的虚拟地址对应的 PTE,并将其填入TLB,然后CPU再用刚刚转换失败的虚拟地址重新访问TLB。

难点分析

本次实验中,我所遇到的难点主要是以下几点:

  • 宏定义的多次出现:本次实验代码中,出现大量宏定义。这样大大简便了代码逻辑性,但是也增加了阅读与编写的难度。尤其在文件众多,指导书中并未明确标注可能使用的宏定义以及其位置的情况下,初次接触内存管理并编写程序的难度极大。
  • 多项函数的互相调用:本次实验中,出现大量函数。这些函数相互联系,在使用中往往需要互相调用,逻辑难度较高。同时也具有分布分散的问题,导致阅读时间较长。
  • 页表及页链表的使用:本次实验中,页结构体作为重点。同时实验中大量使用一阶二阶指针,更改内容。指针指向对象复杂,逻辑难度大。页表内部属性较多,需要逐个调整。
  • 二级页表的映射:实验中主要使用二级页表的结构,一级页表作为页框映射获得二级页表序号,二级页表作为页号映射获得页内容的真实物理地址。映射过程中的数据提取(该部分数据提取哪几位)及数据对应(这些数据又对应下一级数据地址的哪几位)难度较大。

实验体会

Lab2主要让我们学习有关内存管理与映射的问题,了解MIPS系统中虚拟地址与物理地址的对应关系以及在启动过程中获得与输入数据对应的真实数据。整体而言,Lab2的难度应该说是远大于前两个lab的,涉及到的知识点很多很新,同时又有大量未知的需要自己阅读的宏定义和函数(在不知道使用目的与形参函数的含义情况下上手难度极大),对于小白来说还是比较不友好,也花费了我很长一段时间来完成,不得不说,确实让我焦头烂额。因此,十分感谢那些写博客的学长,看了他们的博客后真的是茅塞顿开。操作系统的学习肯定是艰难的,还需坚定内心,勇于攀登。