MMU 模拟


在一个基础的只能执行普通指令的模拟器中添加访问内存的功能。

项目文件如下:

dataStructure.h
  定义了PCB和文件跟踪的数据结构
fileIO.h
fileIO.c
读取输入文件以及写入最终结果
simulator.c
  init()
    初始化各种数据结构
  simulate()
    主体程序,模拟每一个进程的执行。
  diskToMemory()
    从硬盘中读取一个页面到磁盘中。
  readPage()
    执行一条访存或者计算指令
  mmu.h
  mmu.c
    页表,页面地址翻译,OS内存管理以及TLB模拟

Base simulator

初始化

这里指的是需要我们在这基础上进行修改的代码。基础模拟器会读取输入的指令,并读取进程的信息,包括指令序列和指令类型。

进程调度

该模拟器是一个多进程的模拟器,所以需要进程调度的一些内容。根据三种状态模型简要的把进程分为就绪(ready), 阻塞(blocked), 以及运行(running)。在程序开始时,将所有进程装入就绪队列,随后,当没有正在运行的程序时,将就绪队列取出第一个进程加入运行队列,然后开始执行。

那么执行到什么时候呢,当以下事件发生,需要进行调度。

  1. 时间片耗尽
  2. 发生了中断(在这里指硬盘中断)
  3. Page fault导致无法继续执行。
  4. 程序运行结束。

当发生了以上事件时,需要进行调度,调度方法就是把运行队列的第一个元素加入就绪队列的队尾(除非它已经完成了), 然后将就绪队列的第一个元素设置为运行状态。

需要增加的内容

我们在这基础上需要增加内存访问的实现。包括虚拟地址到物理地址的翻译,Page fault的触发和处理,磁盘模拟,以及MMU的模拟

MMU实现

在输入的参数中有一级到三级页表不定的输入,而且每一级页表的长度不是固定的,因此需要给出一个兼容性的实现。那么地址翻译的过程就是取出N1_bits,然后作为数组下标访问第二级页表,再取出N2_bits去访问第三级页表,取出N3_bits去访问物理页,最后通过页偏移确定在物理页中的物理地址。

Page fault的触发和处理。

当最后一级页表的页表项的最低位(present位)为0时,说明页面不存在,页面访问无法进行下去,那就会触发page fault。page fault需要向磁盘发出请求,等待磁盘进行处理。随后进程进入阻塞队列。

磁盘模拟

一旦磁盘处理完磁盘请求,将触发磁盘中断。在中断处理中,操作系统将申请一个新的物理页,并把数据读入物理页,然后填上对应的最后一级页表项。然后该阻塞的进程就会被移入就绪队列的队尾了。

内存管理

内存管理包括物理页的申请和释放。这里包括两种策略。这里空闲物理页和正在使用的物理页都由双向链表来维护。

LRU

LRU保证释放的页面是最久没有使用的页面了。LRU为了效率,还需要一个哈希表来快速定位双向链表。哈希表的key是物理页地址,value是双向链表的链表项。每当进行一次内存访问,LRU需要将该物理页移动到正在使用的页面的队尾,然后进行页面替换时替换掉第一个物理页。

Clock

Clock算法是LRU算法的一种简单实现。在链表项中增加一个使用标记。访问一个页面时将标记置为true。需要进行页面置换时从链表头开始遍历,遇到true的标记就全部设置为false,如果遇到false的页面就将其置换。随后把链表的首尾相连,将链表尾设置为当前节点的前一个节点,链表头设置为下一个节点。

页面分配

如果有空闲的物理页,也就是free_pages不为空的话,从空闲物理页取下一页用来分配。如果没有空闲的物理页,就调用LRU或者Clock算法进行页面置换。

页面释放

将一个物理页从正在使用的链表移动到空闲链表即可。同时将哈希表项置为NULL

TLB实现

假设有三级页表,那么一次内存指令需要访问四次内存。那么如果有TLB的话使用TLB记录最近使用的页表项,就能快速定位到物理页。
TLB表项为虚拟页地址以及物理页地址。然后还需要一个有效位标记。

这里的TLB也用双向链表实现,每次访存时遍历一次TLB,如果有匹配的返回对应的物理页地址;否则TLB Miss,需要在访存以后填入TLB。将TLB表项移动到队列末尾,使用LRU算法进行置换。

在进行进程切换的时候,需要将TLB刷掉,因为不同进程的地址空间不一样。

其它遇到的小问题

Clion中找不到函数定义的问题

Clion中通过CMakeList.txt进行项目文件的定位,要么创建一个CMakeList.txt,要么将Makefile转化成compile_command.json,然后在打开项目的时候打开CMakeList.txt或者compile_command.json,就可以找到符号了。

readline内存泄漏的问题

readline会分配内存到你给定的指针里,但是这个指针指向的内存是需要你手动释放的,如果没有释放会造成内存泄漏。


Author: 蒋璋
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source 蒋璋 !