OS-Lab2-实验报告
思考题
Thinking 2.1
1.虚拟地址 2.虚拟地址
Thinking 2.2
一.
用宏实现提高了数据结构的多态性,通常如果我们不用宏,就需要定义一个包含 void *data 的通用链表节点,然后在存取数据时进行繁琐且不安全的强制类型转换;或者为 Student、Process、File 每种不同的数据类型都手写一遍对应的链表增删改查函数。
宏在预处理阶段直接展开为对应类型的指针操作(如 struct type *le_next)。这使得编译器可以在编译阶段严格检查指针的类型匹配。保证了严格的类型安全。
二.
| 链表种类 | 单向链表 | 双向链表 | 循环链表 |
|---|---|---|---|
| 结构特点 | 节点只包含一个 next 尾随指针 |
包含 next 一级指针和精妙的 **le_prev 二级前驱指针 |
包含普通的一级 next 和 prev 指针,首尾相接形成一个闭环 |
| 插入性能 | 在头部插入: 在已知节点后插入: 在已知节点前插入: |
均为 | 均为 |
| 删除性能 |
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 | LEAF(tlb_out) # 定义一个叶子函数名为 tlb_out |
Thinking 2.6
一.纯硬件流程
当用户程序执行一条访存指令(如 lw 或 sw)时,CPU 会产生一个虚拟地址,这个阶段没有任何OS相关的函数被调用
二.软硬件协同流程
当硬件在TLB中找不到对应的映射时,此时它会触发异常,将控制权交给操作系统,开始了内核函数调用
函数调用和CPU访存之间,CPU 访存依赖 TLB 硬件实现极致的性能;而当我们编写的页表管理函数(page_alloc, page_insert 等)构成了 TLB 的坚实后盾。硬件查不到时,由软件函数去完整的两级页表中去兜底。Lab2 中的 passive_alloc 等函数调用 不是在进程创建时就立刻执行的 。它们是以 CPU 访存失败为契机,被被动且延迟触发的。这种设计极大地节省了物理内存。CPU 硬件访存失败 触发异常 执行内核内存管理函数分配物理页 刷新 TLB 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 位虚拟地址空间“一刀切”硬编码划分成了几个段:kuseg、kseg0 、kseg1 、kseg2。在 kseg0 和 kseg1 中的地址, 根本不需要经过 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打下了一个非常牢固的基础!