0%

OS_Lab2_实验报告

OS-Lab2-实验报告

思考题

Thinking 2.1

1.虚拟地址 2.虚拟地址

Thinking 2.2

一.

用宏实现提高了数据结构的多态性,通常如果我们不用宏,就需要定义一个包含 void *data 的通用链表节点,然后在存取数据时进行繁琐且不安全的强制类型转换;或者为 StudentProcessFile 每种不同的数据类型都手写一遍对应的链表增删改查函数。

宏在预处理阶段直接展开为对应类型的指针操作(如 struct type *le_next)。这使得编译器可以在编译阶段严格检查指针的类型匹配。保证了严格的类型安全。

二.

链表种类 单向链表 双向链表 循环链表
结构特点 节点只包含一个 next 尾随指针 包含 next 一级指针和精妙的 **le_prev 二级前驱指针 包含普通的一级 nextprev 指针,首尾相接形成一个闭环
插入性能 在头部插入:O(1)O(1)
在已知节点插入:O(1)O(1)
在已知节点插入:O(n)O(n)
均为O(1)O(1) 均为O(1)O(1)
删除性能 O(n)O(n) O(1)O(1) O(1)O(1)

Thinking 2.3

C

Thinking 2.4

一.

在多进程操作系统中,每个进程都有自己的虚拟地址空间,如果TLB中仅仅缓存VA->PA的映射关系,会导致TLB污染和巨大的性能开销

而ASID引入之后,TLB 的每一个表项不仅记录 VA 到 PA 的映射,还会打上一个“标签”。TLB 的查找逻辑变成了:只有当 CPU 发出的虚拟地址以及当前 CPU 寄存器中的 ASID 与 TLB 表项中的 VA 和 ASID 完全匹配时,才算命中。

二.

根据 MIPS32 4Kc 官方手册的硬件规范,在 MIPS32 架构中,管理 TLB 查找和写入的关键协处理器寄存器是 EntryHi 寄存器(CP0 Register 10, Select 0) 。在 EntryHi 寄存器中,包含一个专门用于存放当前进程地址空间标识符的位域,就是ASID字段,这个字段占据了 EntryHi 寄存器的最低8位,可以组合出256种不同的状态

Thinking 2.5

一.

tlb_invalidate是C语言函数,它在内部调用了由汇编语言编写的tlb_out函数

二.

tlb_invalidate 的作用是:在 TLB 中查找与指定虚拟地址和进程标识符相匹配的表项,如果存在,则将其清空,以防止 CPU 使用过期的地址映射。

三.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
LEAF(tlb_out)                 # 定义一个叶子函数名为 tlb_out
.set noreorder # 告诉汇编器不要重排指令
mfc0 t0, CP0_ENTRYHI # 将协处理器 CP0 的 EntryHi 寄存器当前的值读出,保存在通用寄存器 t0 中。
mtc0 a0, CP0_ENTRYHI # 将函数参数 a0(由 tlb_invalidate 构造好的包含目标 VA 和 ASID 的值)写入 CP0_ENTRYHI 寄存器。
nop # 空指令,用于解决 CP0 寄存器写入的硬件延迟。

/* Step 1: Use 'tlbp' to probe TLB entry */
tlbp # TLB Probe 探测指令。硬件会自动在 TLB 中查找与当前 CP0_ENTRYHI 匹配的项。如果找到,将索引写入 CP0_Index;找不到则将 CP0_Index 的最高位(符号位)置 1。
nop # 延迟槽,等待 tlbp 硬件查找完成。

/* Step 2: Fetch the probe result from CP0.Index */
mfc0 t1, CP0_INDEX # 将探测结果从 CP0_INDEX 寄存器读出,存入通用寄存器 t1。
.set reorder # 恢复汇编器的指令自动重排功能。
bltz t1, NO_SUCH_ENTRY # Branch if Less Than Zero。如果 t1 < 0,则跳转到 NO_SUCH_ENTRY 标签。

.set noreorder # 再次禁用指令重排,准备向 TLB 写入数据。
mtc0 zero, CP0_ENTRYHI # 将 CP0_ENTRYHI 寄存器清零。
mtc0 zero, CP0_ENTRYLO0# 将 CP0_ENTRYLO0 寄存器(偶数物理页项)清零,表示该页无效。
mtc0 zero, CP0_ENTRYLO1# 将 CP0_ENTRYLO1 寄存器(奇数物理页项)清零,表示该页无效。
nop # 延迟槽,等待上述写入完成。

/* Step 3: Use 'tlbwi' to write CP0.EntryHi/Lo into TLB at CP0.Index */
tlbwi # TLB Write Indexed 指令。将当前 CP0_ENTRYHI、ENTRYLO0、ENTRYLO1 的值(现在全是0)写入到 CP0_INDEX 指定的 TLB 槽位中。这实质上就完成了将该 TLB 项“清空/失效”的操作。

.set reorder # 恢复指令重排。

NO_SUCH_ENTRY: # 标签:当 TLB 中本来就没有这个表项时,直接跳到这里。
mtc0 t0, CP0_ENTRYHI # 将最开始保存在 t0 中的原 CP0_ENTRYHI 的值写回 CP0。
j ra # 跳转到返回地址,返回调用者 tlb_invalidate。
END(tlb_out) # 标记函数结束。

Thinking 2.6

一.纯硬件流程

当用户程序执行一条访存指令(如 lwsw)时,CPU 会产生一个虚拟地址,这个阶段没有任何OS相关的函数被调用

二.软硬件协同流程

当硬件在TLB中找不到对应的映射时,此时它会触发异常,将控制权交给操作系统,开始了内核函数调用

函数调用和CPU访存之间,CPU 访存依赖 TLB 硬件实现极致的性能;而当我们编写的页表管理函数(page_alloc, page_insert 等)构成了 TLB 的坚实后盾。硬件查不到时,由软件函数去完整的两级页表中去兜底。Lab2 中的 passive_alloc 等函数调用 不是在进程创建时就立刻执行的 。它们是以 CPU 访存失败为契机,被被动且延迟触发的。这种设计极大地节省了物理内存。CPU 硬件访存失败 \rightarrow 触发异常 \rightarrow 执行内核内存管理函数分配物理页 \rightarrow 刷新 TLB \rightarrow CPU 重新访存成功。在这个闭环中, 函数调用是连接“虚拟内存假象”与“物理内存现实”的桥梁

Thinking 2.7

x86架构的内存管理由硬件承担了大量的工作,虚拟地址首先要经过分段机制转换为线性地址,再经过分页机制转换为最终的物理地址。x86的CPU中还有一个专门的硬件单元叫MMU,当CPU访问一个虚拟地址且TLB Miss的时候,MMU会暂停当前指令,顺着CR3寄存器去物理内存中逐级遍历页表,如果找到了,硬件会自动把表项填入 TLB 并继续执行指令,只有当硬件发现页表项无效时,才会抛出 Page Fault 异常交给 OS 处理。

MIPS的设计硬件比较简单,复杂逻辑交给了软件。x86 TLB Miss发生时,硬件 MMU 自动去查页表,操作系统在这一步完全不知情。OS 只需要在进程创建时把页表建好,把基址扔给 CR3 寄存器就行了。而MIPS在TLB Miss发生时,会抛出一个TLB Refill异常,捕获这个异常必须执行实验里写的软件中断处理函数,由 OS 自己的代码一步步查页目录、查二级页表,最后用汇编指令(如 tlbwr)把查到的结果手动塞进 TLB。

x86因为是硬件去查页表,硬件必须知道页表长什么样。MIPS因为是软件去查页表,所以 MIPS 硬件根本不关心你在内存里的页表长什么样、是几级页表、甚至是哈希表都可以 。

x86没有固定的硬件空间划分。内核态和用户态的隔离完全依赖于页表项中的权限位。MIPS硬件直接将 32 位虚拟地址空间“一刀切”硬编码划分成了几个段:kusegkseg0kseg1kseg2。在 kseg0kseg1 中的地址, 根本不需要经过 TLB 和页表转换,直接抹去最高几位就变成了物理地址

难点分析

lab2中,我觉得一共有以下三个难点:

难点一:queue.h**le_prev 二级指针

难点解析 :BSD 双向链表最反直觉的地方在于,它的 prev 指针不是指向前一个“节点实体”,而是指向前一个节点内部的 next 指针本身的物理地址 。

难点二:两级页表寻址过程的位运算拆

难点解析 :在 32 位架构下,如何将一个虚拟地址通过页目录和页表,最终拼凑成物理地址。尤其是在 pmap.h 中通过大量的宏(如 PDX, PTX, PTE_ADDR)进行位运算掩码提取,极易混淆。

难点三:MIPS 软件 TLB 重填与缺页中断

难点解析 :相较于 x86 由硬件 MMU 自动遍历页表,MIPS 架构采用了软件 TLB机制。这要求操作系统必须亲自接管 TLB 缺失异常,并通过代码(tlb_miss_entry -> _do_tlb_refill -> passive_alloc)进行软硬件协同,这也是实验中最复杂的控制流。

实验体会

这次实验相比于lab1来说是难度飙升,exercise的数量和thinking的数量比以前也多,这次lab难点困扰了我很久,一开始没分清页表项个数和所占物理内存大小的区别,不了解二级页表的结构,感觉到非常困惑,随着实验的进行,我在网上查询资料以及翻阅os指导书以后,终于对内存分配有了更深层次的理解,这也为我以后学习os打下了一个非常牢固的基础!