OS-lab2

思考题
Thinking 2.1
C程序中的指针存储的地址与mips中lw, sw
访存的地址均为虚拟地址
Thinking 2.2
- 使用宏来实现链表能够很好的实现代码复用,减少代码量同时提高程序可读性。
- 单向链表在插入与删除时都需要进行遍历操作,因此时间复杂度均为
。循环链表依然是单向的,但其维护了一个尾项指针,因此插入尾项的复杂度为 ,其余操作均为 。双向链表的链表项中存储了指向前项的指针与后项的指针,因此插入、删除操作的复杂度为 ,但如果项在链表尾部插入链表项,依然需要遍历整个链表。
Thinking 2.3
Page_List
的正确结构应为C
1 | struct Page_list{ |
2.4
- 每个进程都会被分配一块自己的虚拟地址空间,不同虚拟地址在不同地址空间中会映射到不同的物理地址,ASID就是用来区分当前的虚拟地址是被哪个进程所使用,以确定其映射到的物理地址。
- ASID在4Kc处理器中是8位的,因此8位的ASID可以标识2^8=256个不同的地址空间
2.5
tlb_invalid
函数中调用了tlb_out
tlb_invalid
的作用是在页表内容改变后及时更新TLB
1 | LEAF(tlb_out) |
2.6
根据访存流程和用户函数部分的图示,可以得到函数调用与CPU访存流程的对应关系:
- CPU发出访存指令
- 查询目标页面是否在TLB中
- 如果在,直接读取PPN并结合offset完成访存
- 如果不在TLB中,触发TLB Miss
- tlb miss,会导致用户进程暂停,进入一个handler,并调用_do_tlb_refill
- 在_do_tlb_refill函数中调用
page_lookup
,在页目录中查找页表- 如果页表项无效,那么调用
passive_alloc
函数分配新的页表并填写到页目录项 - 如果有效,从页表中查找
- 如果页表项无效,那么调用
- 从页表中查找页表项
- 如果页表项无效,调用
page_alloc
函数分配新页面并填写到页表项 - 如果有效,取出相邻就业,填写到
EntryLo
,写回TLB
,再次访存。
- 如果页表项无效,调用
2.7
在内存管理机制上,X86使用分段和分页的结合方式,分段机制复杂但灵活,MIPS主要依赖分页机制,分段支持较弱,管理更简单直接。在TLB方面,X86:虽然也有TLB,但其内存管理机制更复杂,TLB的实现可能有所不同;MIPS依赖TLB来加速地址转换,对于TLB的使用更直接。总体来说,X86 的内存管理机制更复杂,但有着更高的灵活性,而MIPS的内存管理机制更简单,适合对性能和功耗要求较高的场景。
实验难点
本次实验有两个主要难点:物理内存管理机制与二级页表查询机制
Page结构与管理机制
实验中的操作系统利用Page
结构体来管理物理页,他的结构类似于双向链表,包含一个指向后一项的指针,和一个指向前一项中“指向后一项的指针”的指针。这个指针的指针存在的意义是,当需要删除当前项时,可以方便的修改前一个元素向后的指针。
在实验中,我们将所有空闲的Page结构体链接在Page_list
类型的page_free_list
上。对于Page_list
结构的定义是这个样子的:
1 | // pmap.h |
所以简化之后其实就是一个Page
类型的指针。
1 | struct Page_list { |
在实验中,使用LIST_INIT
宏对其进行初始化,其实就是把lh_first
指向null
。到此,我们就可以将空闲的Page
结构体链接在这个page_free_list
的链表头上了,这一过程是通过page_init()
完成的。
接下来我们需要了解Page
结构体的结构:
1 | // pmap.h |
经过简化,我们可以得到这样的结构:
1 | struct Page { |
也就是说,Page
包含了一个short
类型的pp_ref
,和一个包含两个指针的pp_link
域,根据指导书,这两个指针分别指向下一个Page
结构体,和上一个Page
的le_next
指针。
再接下来,我们来研究一下queue.h
中的宏应该如何使用。例如LIST_NEXT
:
1 |
首先观察到->
,说明elm
需要一个指针,而field
是这个指针指向的一个结构体中的一个元素,并且field
是一个包含了名为le_next
元素的结构体。因此在lab2中,我们大概率是要这样调用这个宏:
1 | struct Page* pp, next_pp; |
还有一种宏包含形参head
,例如:
1 |
在理解了Page_list
的结构后,很容易发现这里的head
应该为一个Page_list
类型的指针。而在pmap.c
中只定义了一个Page_list
类型的全局变量page_free_list
,所以你大概率需要这样调用:
1 | struct Page* pp; |
二级页表管理虚拟内存
利用二级页表访问物理页是一个比较复杂的过程,其大致过程为:
- 将虚拟地址分解为页目录偏移量+页偏移量+页内偏移量
- 根据页目录基地址与页目录偏移量,找到页目录项,得到其中记录的物理页号
- 根据页号与页偏移量,找到页表项,得到其中记录的物理页号
- 根据物理页号和页内偏移量,得到物理地址
pgdir_walk
函数的作用事实上就是以上过程的第二步。其根据给定的Pde* pgdir
(页目录基地址)与va
,获得页表项的地址
体会与感想
Lab2的代码量比Lab1大了不少,而且代码中大量使用了宏和指针,不免让人有些晕头转向,但是当完成之后再进行复盘,能够体会到对于内存管理的理解又加深了一层,整个实验是对理论知识非常充分的实践。
- Title: OS-lab2
- Author: OWPETER
- Created at : 2025-04-12 14:52:26
- Updated at : 2025-04-12 14:53:21
- Link: https://owpeter.github.io/2025/04/12/OS-lab2/
- License: This work is licensed under CC BY-NC-SA 4.0.