内存管理-OS课程项目
内存管理——请求调页存储管理方式模拟
[TOC]
开发环境
开发语言:Javascript+html+css
开发框架:Vue.js 3.0+Element-plus
开发工具:Vue-cli、Vue-devtools、VScode、Edge
实现方法
引入Element-plus组件作为UI,采用Vue3框架进行组件化开发
采用 JS 模拟内存管理调页请求逻辑,使用 Vue3 开发单页面应用,便于实时渲染、信息实时显示
使用链表来辅助实现LRU算法
项目浏览
界面UI说明
- 左栏: - 上部分为内存块信息,包含块序号、块内存放页的页号(若没有则为 null)、块起始地址(每次重置时随机给出) 
- 中间为当前已执行指令条数、当前缺页数、当前缺页率 
- 下部分为调页信息表,记录了目前为止每次调度的相关信息,包括调入的逻辑页、调至的块号、被替换出的逻辑页(若无则为 null) 
- 支持滚动条滚动翻阅所有信息 
 
- 右栏: - 记录每次指令执行的相关信息,包括指令id、指令位于的逻辑页号、包含指令的页当前在那一块内存块中(若不在内存中则为 null)、指令在内存中的地址(若不在内存中则为 null ),下一条指令的 id
- 当指令不在内存中时,会先显示指令缺页信息,在缺页请求调度完成后,会再次打印本条指令调度后信息
- 支持滚动条滚动翻阅所有信息
 
- 顶栏: - 单步执行按钮:点击后执行一条指令,若发生缺页则完成缺页请求调度后再次打印本条指令信息
- 自动执行按钮:通过右侧输入框设置合适的指令执行速度,点击自动执行按钮,会根据设置的执行速度自动执行指令,直到单击重置按钮或320条指令全部执行完毕
- 重置按钮:点击后恢复到初始状态
 
源码文件功能介绍
本次的页面较为简单,实现功能的主要文件为Pageshow.vue及linklist.js
文件预览
| 文件名称 | 负责内容 | 子组件 | 
|---|---|---|
| App.vue | 页面根组件 | PageShow.vue | 
| PageShow.vue | 实现页面UI展示、整体逻辑 | null | 
| linklist.js | 实现链表数据结构,便于实现LRU算法 | - | 
主要文件源码变量介绍
PageShow.vue
维护变量
| 1 |  | 
基本介绍
组件内部维护了请求调页存储管理的所有信息,除了基本的内存块数、进程页数、进程指令总数等参数外,还用了对象数组physical_memory, page_table 来存储各个内存块信息、页表信息,以此模拟内存管理中相应的数据结构;并设置了instruction_record 及schedule_record 来记录每次指令执行相关信息、调度相关信息以便于在表格中展示。
主要对象属性介绍
physical_memory 数组中的内存对象包含以下属性:
- id:内存块号
- start_addr: 起始地址
- length:内存地址长度——以一个指令长为单位
- content:内存块中存放的信息的逻辑页号,若无则为 null
初始化函数代码:
| 1 |  | 
page_table 数组中的各个页信息包含以下属性:
- id:逻辑页号
- valid:有效位——标识是否在内存块中
- memory_id:若位于内存块中,则此处记录位于的内存块号,否则为 null
初始化函数代码:
| 1 |  | 
linklist.js
主要变量
| 1 |  | 
主要成员函数(方法)
| 1 |  | 
算法设计及实现
随机指令生成算法
设指令id为0-319,共320条:
- 在0-318条指令之间,随机选取一个起始执行指令,如序号为m - 1 
 2
 3
 4- let randomOrder = Math.floor(Math.random() * (this.instruction_num - 1)); //随机选取起始执行指令
 let i = 0;
 this.exe_order.push(randomOrder);
 i++;
- 顺序执行下一条指令,即序号为m+1的指令 - 1 
 2- this.exe_order.push(randomOrder + 1); //执行序号+1的指令
 i++;
- 通过随机数,跳转到前地址部分0-m-1中的某个指令处,其序号为m - 1- 1 
 2
 3- randomOrder = Math.floor(Math.random() * randomOrder); //跳转至前0 - m-1
 this.exe_order.push(randomOrder);
 i++;
- 顺序执行下一条指令,即序号为m - 1+1的指令- 1 
 2- this.exe_order.push(randomOrder + 1); //执行序号+1的指令
 i++;
- 通过随机数,跳转到后地址部分m - 1+2~318中的某条指令处,其序号为m- 2- 1 
 2
 3
 4
 5- randomOrder = Math.floor(
 Math.random() * (this.instruction_num - randomOrder - 4) + randomOrder + 2
 ); //跳转至后 m+2 - 319
 this.exe_order.push(randomOrder);
 i++;
- 顺序执行下一条指令,即m - 2+1处的指令- 1 
 2- this.exe_order.push(randomOrder + 1); //执行序号+1的指令
 i++;
- 重复跳转到前地址部分、顺序执行、跳转到后地址部分、顺序执行的过程,直到执行完毕 
LRU算法
LRU算法要求替换最近最少使用的块,可以用链表来实现,链表中结点值为存有页项的内存块号,相当于一个内存块链表。
采用以下规则确定根据LRU算法应该被替换出的块:
- 当访问后未发生缺页时,将访问的页所在的内存块移动至链表首结点处; - 1 
 2
 3- //页表处于内存中
 this.LRUlist.movetoHead(this.LRUlist.findIndex(page.memory_id)); //将页表所在块移至链表头
 this.instruct_record_update(insturction_id); //更新记录
- 当访问后发生了缺页: - 若内存块未被占满(即链表长度小于内存总块数),则新建结点,结点值为页刚刚放入的内存块号,并将该结点插至链表顶端; - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17- if (this.LRUlist.length() < this.mem_block_num) {
 //若内存块中有剩余空间则将该页直接放入内存块,将对应的内存块插至链表首位置
 for (let i = 0; i < this.mem_block_num; ++i) {
 if (this.physical_memory[i].content == "null") {
 //将页放入内存
 this.physical_memory[i].content = page_in.id;
 //将内存块号插至链表首位
 this.LRUlist.insertHead(i);
 return {
 page_in: page_in.id,
 dst_block: i,
 page_out: "null",
 };
 }
 }
 }
 ...
- 若内存块已被占满(即链表长度等于内存总块数),则链表末尾的结点对应的内存块应当是要发生替换的块,将其从链表中删除,则转换成了上一种情况; - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14- ...
 else {
 //若内存中无剩余空间
 let dst_block_id = this.LRUlist.deleteLast(); //删除链尾元素,确定替换的块号
 let page_out_id = this.physical_memory[dst_block_id].content; //被调出的页号
 this.physical_memory[dst_block_id].content = page_in.id; //将新页调入
 //将内存块号插至首位
 this.LRUlist.insertHead(dst_block_id);
 return {
 page_in: page_in.id,
 dst_block: dst_block_id,
 page_out: page_out_id,
 };
 }
 
请求调页存储管理算法
- 在执行一条新指令时,根据其id(逻辑地址)获取其相应的逻辑页号,再通过查询页表获取该页对象,其包含相关信息——是否位于内存中,位于哪一个内存块等 - 1 
 2
 3- let insturction_id = this.exe_order[this.exe_num]; //获取对应的指令id
 let page_id = Math.floor(insturction_id / 10); //对应页号
 let page = this.page_table[page_id]; //获取页表对应项
- 判断该页是否位于内存中,即是否发生缺页: - 若发生缺页,则根据LRU算法选择被替换块,进行替换、修改页表对应项,并记录相关信息 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18- if (page.valid == false) {
 this.instruct_record_update(insturction_id); //更新记录
 //若页表不在内存中
 this.page_miss++;
 let cur_schedule = this.LRU(page); //利用LRU算法进行调度,返回调度信息
 this.schedule_record.push(cur_schedule); //记录调度信息
 //修改页表内容
 //修改调入页信息
 this.page_table[page_id].valid = true;
 this.page_table[page_id].memory_id = cur_schedule.dst_block;
 this.instruct_record_update(insturction_id); //更新记录
 //修改调出页信息
 if (cur_schedule.page_out != "null") {
 this.page_table[cur_schedule.page_out].valid = false;
 this.page_table[cur_schedule.page_out].memory_id = "null";
 }
 }
 ...
- 若未发生缺页,则将页表调入空闲内存块,调整LRU辅助链表,记录相关信息 - 1 
 2
 3
 4
 5
 6- ...
 else {
 //页表处于内存中
 this.LRUlist.movetoHead(this.LRUlist.findIndex(page.memory_id)); //将页表所在块移至链表头
 this.instruct_record_update(insturction_id); //更新记录
 }
 
- 执行下一条指令