BUAA-OS-Lab2
Lab2
:内存管理
[toc]
访存流程概览
内核态
内核态是操作系统运行的一种状态,与用户态相对,在这种状态下,计算机系统赋予了操作系统内核最高的权限。包括但不限于:
- 拥有最高权限:CPU可执行指令集中的所有指令,包括特权指令,进而直接访问和控制关键资源和硬件。
- 访问所有内存空间。
- 处理中断和异常。
- 系统资源管理:如CPU时间片分配,进程的创建与销毁等。
内存布局
在我们设计的MOS
操作系统中,内存的布局以及虚实映射关系如下:
- 虚拟地址:
0x00000000
~0x7fffffff
(kuseg
),这2GB
空间用于存放用户程序代码与数据。需要通过TLB
转化为物理地址,再通过cache访存。 - 虚拟地址:
0x80000000
~0x9fffffff
(kseg0
),这512MB
空间用于存放内核代码与数据。将虚拟地址的最高位置零得到物理地址,通过cache访存。 - 虚拟地址:
0xa0000000
~0xbfffffff
(kseg1
),这512MB
空间可以用于访问外存,将虚拟地址的最高三位置零得到物理地址,不通过cache访存 - 虚拟地址:
0xc0000000
~0xffffffff
(kseg2
),这1GB
空间只能在内核态下使用,并且需要通过TLB
转化为物理地址,通过cache访存
在4Kc
(实验所用CPU)中,通过硬件MMU
来完成上述地址映射,MMU
中集成了TLB
。对所有第2GB
空间的访存都需要经过TLB
。
内核程序启动
mips_init
函数是在基于MIPS架构的操作系统内核代码里出现的一个初始化函数。它的主要作用是对 MIPS 架构相关的硬件和软件环境进行初始化操作。它需要调用调用如下函数
mips_detect_memory(u_int _memsize)
该函数作用是探测硬件可用内存,并对一些和内存管理相关的变量进行初始化。
mips_vm_init()
该函数作用是建立一些用于管理的数据结构。
为了建立起内存管理机制,还需要用到alloc
函数来分配内存空间。
1 | staticvoid *alloc(u_intn,u_intalign, intclear) { |
extern char end[]
:外部变量 end,它表示内核代码和全局变量所占用内存的结束地址。在MOS
系统中,end对应虚拟地址0x80400000
,根据对应规则,转化为物理地址就是0x00400000
。我们从该地址开始进行内存分配。ROUND(freemem, align)
:作用是将freemem
按照align
向上对齐,例如freemem
为101,align
为4,则对齐后为104。if (clear) {...}
:当clear
为真,该函数会将这一部分内存清零。
实现完分配函数后,我们要完成对内存管理数据结构的空间分配。
1 | void mips_vm_init() { |
这里使用alloc
函数为物理内存管理所用到的Page
结构体按页分配物理内存。
物理内存管理
MOS中的内存管理使用页式内存管理,采用链表法管理空闲物理页框。
页控制块
MOS中维护了npage
个页控制块,也就是Page
结构体。每一个页控制块对应一页的物理内存,MOS用这个结构体来按页管理物理内存的分配。
将空闲物理页对应的Page
结构体全部插入一个链表中,该链表被称为空闲链表,即page_free_list
。 当一个进程需要分配内存时,就需要将空闲链表头部的页控制块对应的那一页物理内存分配出去,同时将该页控制块从空闲链表中删去。 当一页物理内存被使用完毕(准确来说,引用次数为0)时,将其对应的页控制块重新插入到空闲链表的头部。
虚拟内存管理
前面已经提到,MOS中用PADDR(x)
宏可以将位于kseg0
的虚拟地址x转化为对应的物理地址。KADDR(x)
宏可以返回物理地址x位于kseg0
的虚拟地址。
两级页表结构
第一级页表称为页目录(Page Directory
),第二级页表称为页表(Page Table
)。
对于一个32位的虚存地址,其31-22位,共10位表示的是一级页表项的偏移量。21-12位,共10位表示的是二级页表项的偏移量。11-0位,共12位表示的是页内偏移量。
include/mmu.h
中提供了两个宏以快速获取偏移量,PDX(va)
可以获取虚拟地址va
的31-22 位,PTX(va)
可以获取虚拟地址va
的21-12 位。
一级页表与二级页表的结构相同:1024个页表项 + 每个页表项32位(20位物理页号 + 高6位硬件标志位 + 低6位软件标志位)。每个页表4KB
,刚好是一个物理页面的大小。
访问内存与TLB
重填
TLB组成
TLB
是页表的高速缓存,是一种硬件,用于缓存最近使用过的虚拟页号(VPN
)和对应的物理页框号(PPN
)的映射关系,以加快虚拟地址到物理地址的转化速度。
每个TLB
表项都有两个组成部分,包括一组Key和两组Data。EntryLo0
存储Key
对应的偶页而EntryLo1
存储 Key
对应的奇页。
当TLB
缺失时, EntryHi
中的VPN
自动(由硬件)填充为对应虚拟地址的虚页号。
4Kc
处理器架构
4Kc
中与内存管理相关的CP0
寄存器如下:
4Kc
中的TLB
采用奇偶页的设计,即使用VPN
中的高19位与ASID
作为Key
,一次查找到两个Data
(一对相邻页面的两个页表项),并用VPN
中的最低1位在两个Data
中选择命中的结果。因此在对TLB
进行维护(无效化、重填)时,除了维护目标页面,同时还需要维护目标页面的邻居页面。
TLB
事实上构建了一个映射TLB
→ <PFN
,C,D,V,G >。(Physical Frame Number)
TLB
相关指令
tlbr
:以Index
寄存器中的值为索引,读出TLB
中对应的表项到EntryHi
与EntryLo0
、EntryLo1
。tlbwi
:以Index
寄存器中的值为索引,将此时EntryHi
与EntryLo0
、EntryLo1
的值写到索引指定的TLB
表项中。tlbwr
:将EntryHi
与EntryLo0
、EntryLo1
的数据随机写到一个TLB
表项中(此处使用Random
寄存器来“随机”指定表项,Random
寄存器本质上是一个不停运行的循环计数器)tlbp
:根据EntryHi
中的Key
(包含VPN
与ASID
),查找TLB
中与之对应的表项,并将表项的索引存入Index
寄存器(若未找到匹配项,则Index
最高位被置1)
tlbp
中,p代表probe,搜索与指定虚拟地址(此时虚拟地址体现于EntryHi
)相匹配的条目。
软件必须经过CP0
与TLB
交互,因此软件操作TLB
的流程总是分为两步:
- 填写
CP0
寄存器。 - 使用
TLB
相关指令。
TLB
的维护
更新页表中虚拟地址
va
对应的页表项时,将TLB
中对应的旧表项无效化。在下次访问该虚拟地址时,硬件会触发
TLB
重填异常,此时操作系统对TLB
进行重填。tlb_invalidate
函数实现删除特定虚拟地址在TLB
中的旧表项(无效化)。
_do_tlb_refill(Pte *pte, u_intva, u_intasid)
重填。