Lab2:内存管理

[toc]

访存流程概览

内核态

内核态是操作系统运行的一种状态,与用户态相对,在这种状态下,计算机系统赋予了操作系统内核最高的权限。包括但不限于:

  • 拥有最高权限:CPU可执行指令集中的所有指令,包括特权指令,进而直接访问和控制关键资源和硬件。
  • 访问所有内存空间。
  • 处理中断和异常。
  • 系统资源管理:如CPU时间片分配,进程的创建与销毁等。

内存布局

在我们设计的MOS操作系统中,内存的布局以及虚实映射关系如下:

  • 虚拟地址:0x00000000 ~ 0x7fffffffkuseg),这2GB空间用于存放用户程序代码与数据。需要通过TLB转化为物理地址,再通过cache访存
  • 虚拟地址:0x80000000 ~ 0x9fffffffkseg0),这512MB空间用于存放内核代码与数据。将虚拟地址的最高位置零得到物理地址,通过cache访存
  • 虚拟地址:0xa0000000 ~ 0xbfffffffkseg1),这512MB空间可以用于访问外存,将虚拟地址的最高三位置零得到物理地址,不通过cache访存
  • 虚拟地址:0xc0000000 ~ 0xffffffffkseg2),这1GB空间只能在内核态下使用,并且需要通过TLB转化为物理地址,通过cache访存

4Kc(实验所用CPU)中,通过硬件MMU来完成上述地址映射,MMU中集成了TLB。对所有第2GB空间的访存都需要经过TLB

image-20250329132755543

OS_L2_G1

内核程序启动

mips_init函数是在基于MIPS架构的操作系统内核代码里出现的一个初始化函数。它的主要作用是对 MIPS 架构相关的硬件和软件环境进行初始化操作。它需要调用调用如下函数

mips_detect_memory(u_int _memsize)

该函数作用是探测硬件可用内存,并对一些和内存管理相关的变量进行初始化。

mips_vm_init()

该函数作用是建立一些用于管理的数据结构。

为了建立起内存管理机制,还需要用到alloc函数来分配内存空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 staticvoid *alloc(u_intn,u_intalign, intclear) {
extern charend[];
u_long alloced_mem;

if(freemem== 0) {
freemem = (u_long)end;//end
}
freemem = ROUND(freemem,align);
alloced_mem= freemem;
freemem = freemem +n;
if(PADDR(freemem) >=memsize){
panic("outofmemory");
}
if(clear) {
memset((void *)alloced_mem,0, n);
}
return (void *)alloced_mem;
}
  • extern char end[]:外部变量 end,它表示内核代码和全局变量所占用内存的结束地址。在MOS系统中,end对应虚拟地址0x80400000,根据对应规则,转化为物理地址就是0x00400000。我们从该地址开始进行内存分配。
  • ROUND(freemem, align):作用是将freemem按照align向上对齐,例如freemem为101,align为4,则对齐后为104。
  • if (clear) {...}:当clear为真,该函数会将这一部分内存清零。

实现完分配函数后,我们要完成对内存管理数据结构的空间分配。

1
2
3
void mips_vm_init() {
pages = (struct Page *)alloc(npage * sizeof(struct Page), PAGE_SIZE, 1);
}

这里使用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寄存器如下:

image-20250331100544100

OS_L2_G2

4Kc中的TLB采用奇偶页的设计,即使用VPN中的高19位与ASID作为Key,一次查找到两个Data(一对相邻页面的两个页表项),并用VPN中的最低1位在两个Data中选择命中的结果。因此在对TLB进行维护(无效化、重填)时,除了维护目标页面,同时还需要维护目标页面的邻居页面。

image-20250331100733565

OS_L2_G3

TLB事实上构建了一个映射TLB → <PFN,C,D,V,G >。(Physical Frame Number)

TLB相关指令

  • tlbr:以Index寄存器中的值为索引,读出TLB中对应的表项到EntryHiEntryLo0EntryLo1
  • tlbwi:以Index寄存器中的值为索引,将此时EntryHiEntryLo0EntryLo1的值写到索引指定的TLB表项中。
  • tlbwr:将EntryHiEntryLo0EntryLo1的数据随机写到一个TLB表项中(此处使用Random寄存器来“随机”指定表项,Random寄存器本质上是一个不停运行的循环计数器)
  • tlbp:根据EntryHi中的Key(包含VPNASID),查找TLB中与之对应的表项,并将表项的索引存入Index寄存器(若未找到匹配项,则Index最高位被置1)

tlbp中,p代表probe,搜索与指定虚拟地址(此时虚拟地址体现于EntryHi)相匹配的条目。

软件必须经过CP0TLB交互,因此软件操作TLB的流程总是分为两步:

  • 填写CP0寄存器。
  • 使用 TLB相关指令。

TLB的维护

  1. 更新页表中虚拟地址va对应的页表项时,将TLB中对应的旧表项无效化。

  2. 在下次访问该虚拟地址时,硬件会触发TLB重填异常,此时操作系统对TLB进行重填。

    tlb_invalidate函数实现删除特定虚拟地址在TLB中的旧表项(无效化)。

_do_tlb_refill(Pte *pte, u_intva, u_intasid)重填。

image-20250331112335536

OS_L2_G4