实验内容
模拟请求分页虚拟存储管理技术中的硬件地址变换、缺页中断及页式淘汰算法处理缺页中断
实验目的
为了使大的作业(其地址空间超过内存可用空间)或多个作业的地址空间之和超过实际主存空间时,仍能运行,引入了”虚拟存储器”的概念.使作业的一部分地址空间在主存,另一部分在辅存,由操作系统实现多级存储的自动管理,实现主存间的自动覆盖.要求通过本实验,对请求分页管理有一个清楚的概念.
实验题目
第一部分:模拟请求分页虚拟存储管理技术中的硬件地址变换和缺页中断的过程
提示:
1、 请求分页虚拟存储管理技术是把作业地址空间的全部信息存放在磁盘上,当作业被选中运行时,先把作业的开始几页装入主存并启动运行.为此,在为作业建立页表时,应说明哪些页已在内存,哪些页不在内存.
页表的格式如下:
2、 作业在执行时,指令中的逻辑地址指出参加运算操作数(或指令)地址中的页号和页内偏移量.硬件地址转换机构按页号查页表.
若该页的标志为”1”,则表示该页已在主存,从而找到该页对应的内存块号,根据关系式:
绝对地址=块号*块的长度+页内偏移量
计算出欲访问的内存地址.由于页长为2的整次幂,所以只要将块号与页内偏移量相拼接,放入地址寄存器即可按照该地址取指令或取操作数,完成指定的操作.
若对应的页不在内存(即标志为0),则硬件产生缺页中断,转操作系统处理系统.根据页表中的”外存地址”,找到该页.再查内存分块表,找一个空闲块装入该页,修改页表和内存分块表,继续执行被中断的指令.
3、 设计一个”地址变换”程序,模拟硬件地址变换过程:
当访问的页在内存时,则形成绝对地址后,不去模拟指令的执行,而是输出被转换的地址;当访问的页不在内存时,则输出”*该页(页号)不在内存”,以表示产生了一次缺页中断;若地址非法,显示”地址非法”,并终止程序的运行
假定内存的每块长度为128字节,现有一个只有七页的作业,其中第0页至第3页已经装入内存.该作业的页表如下:
作业执行的指令序列如下表:
运行你设计的地址变换程序,显示或打印运行结果.因为只是模拟地址变换,并不模拟指令的执行,故不考虑上述指令的操作结果.
第二部分:采用先进先出(或LRU)算法,实现分页管理的缺页调度.
提示:
1、 在分页虚拟存储系统中,当硬件发出缺页中断时,若内存中已无空闲块,当采用FIFO算法时,则淘汰最先进入内存的页,若该页修改过,还要存入磁盘,然后,再把当前要访问的页装入该块,并修改表中的对应标志.
2、 当采用LRU算法时,则淘汰最近很少访问的页.
两算法均可采用一个数组或链表记录内存中页号的排序,每次将链首页淘汰.数组或链表中只包含页的虚页号─项信息,其它信息通过查页表得到.
实验报告:
(1)、程序中使用数据结构和符号说明.
(2)、给出程序流程.
(3)、打印初始页表、调入/出的页、每执行完一条指令页表的状态及得到的内存地址
(4)、编程语言不限。
采用LRU算法解决
- 首先创建page类模拟页,生成geeter setter 有参 无参方法 我引入的lombox插件自动生成,手动生成也可以
package com.xu.demo.ytu.lru;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Page {
private int pageNum;
private int flag;
private int cacheNum;
private String outAddress;
private int changeNum;
@Override
public String toString() {
return
"\t"+pageNum +
" \t" + flag +
" \t" + cacheNum +
" \t" + outAddress +
" \t" + changeNum ;
}
}
- 创建work类模拟工作表,生成geeter setter 有参 无参方法 我引入的lombox插件自动生成,手动生成也可以
package com.xu.demo.ytu.lru;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Work {
private String operation;
private int pageNum;
private String unitNum;
}
- LRU算法核心
package com.xu.demo.ytu.lru;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class LRU {
Node head, tail;
int size;
//容量
int capacity;
Map<Integer, Node> cache;
public LRU(int capacity) {
this.capacity = capacity;
//初始化链表
initDoubleLinkList();
size = 0;
cache = new HashMap<>(capacity + 2);
}
public static class Node {
public Node pre;
public Node next;
public int key;
public Page page;
public Node(int key, Page page) {
this.key = key;
this.page = page;
}
//空参
public Node() {
}
}
private void initDoubleLinkList() {
//头结点
head = new Node();
//尾结点
tail = new Node();
//头指向尾
head.next = tail;
//尾指向头
tail.pre = head;
}
//删除结点
private void deleteNode(Node node) {
node.pre.next = node.next;
node.next.pre = node.pre;
}
//添加结点
private void addNode(Node node) {
head.next.pre = node;
node.next = head.next;
node.pre = head;
head.next = node;
}
private void moveToHead(Node node) {
//删除原来的结点关系
deleteNode(node);
//添加结点到头部
addNode(node);
}
public Page get(int key) {
Node node = cache.get(key);
if (node == null) {
return null;
}
moveToHead(node);
return node.page;
}
public void put(int key, Page page) {
//hashmap中直接查出时间复杂度为O(1)
Node node = cache.get(key);
//如果链表中存在继续添加就是把它添加到首位
if (node != null) {
node.page = page;
moveToHead(node);
return;
}
//如果不存在,先加进去,再移除尾结点
if (size == capacity) {
Node lastNode = tail.pre;
deleteNode(lastNode);
cache.remove(lastNode.key);
size--;
}
//加入头结点
Node newNode = new Node();
newNode.key = key;
newNode.page = page;
addNode(newNode);
cache.put(key, newNode);
size++;
}
public static void main(String[] args) {
//缓存容量为4
LRU cache = new LRU(4);
//已经存在与缓存的数据
cache.put(3, new Page(3, 1, 1, "021", 0));
cache.put(2, new Page(2, 1, 9, "013", 0));
cache.put(1, new Page(1, 1, 8, "012", 1));
cache.put(0, new Page(0, 1, 5, "011", 1));
//初始化页
List<Page> pages = new ArrayList<>();
pages.add(new Page(0, 1, 5, "011", 1));
pages.add(new Page(1, 1, 8, "012", 1));
pages.add(new Page(2, 1, 9, "013", 0));
pages.add(new Page(3, 1, 1, "021", 0));
pages.add(new Page(4, 0, 0, "022", 0));
pages.add(new Page(5, 0, 0, "023", 0));
pages.add(new Page(6, 0, 0, "123", 0));
System.out.println("————————————————————执行 初始化 操作————————————————————");
System.out.println(" 页号 \t标志 \t内存块号 \t外存地址 \t修改值");
for (Page page : pages) {
System.out.println(page + "\t");
}
System.out.println("LRU队列顺序:");
System.out.print(cache.head.next.page.getPageNum()+"<--");
System.out.print(cache.head.next.next.page.getPageNum()+"<--");
System.out.print(cache.tail.pre.pre.page.getPageNum()+"<--");
System.out.print(cache.tail.pre.page.getPageNum());
System.out.println();
//初始化工作表
List<Work> works = new ArrayList<>();
works.add(new Work("+", 0, "070"));
works.add(new Work("+", 1, "050"));
works.add(new Work("*", 2, "015"));
works.add(new Work("存", 3, "021"));
works.add(new Work("取", 0, "057"));
works.add(new Work("-", 6, "040"));
works.add(new Work("移位", 4, "053"));
works.add(new Work("+", 5, "023"));
works.add(new Work("存", 1, "037"));
works.add(new Work("取", 2, "076"));
works.add(new Work("+", 4, "001"));
works.add(new Work("取", 6, "074"));
//执行操作
for (Work work : works) {
Page page = cache.get(work.getPageNum());
if (page == null) {
System.out.println("————————————————————执行 " + work.getOperation() + " 操作————————————————————");
System.out.println("操作的页:"+work.getPageNum());
System.out.println("缓存不存在该页,发出缺页请求");
System.out.println("该页面在于内存中");
System.out.println("淘汰的页:"+cache.tail.pre.page.getPageNum());
int i = Integer.parseInt(work.getUnitNum(), 8);
pages.get(work.getPageNum()).setCacheNum(cache.tail.pre.page.getCacheNum());
pages.get(cache.tail.pre.page.getPageNum()).setFlag(0);
pages.get(cache.tail.pre.page.getPageNum()).setChangeNum(0);
pages.get(cache.tail.pre.page.getPageNum()).setCacheNum(0);
cache.put(work.getPageNum(), pages.get(work.getPageNum()));
int n = (pages.get(work.getPageNum()).getCacheNum()) * 128 + i;
System.out.println("绝对地址:" + Integer.toHexString(n));
pages.get(work.getPageNum()).setFlag(1);
pages.get(work.getPageNum()).setChangeNum(1);
System.out.println(" 页号 \t标志 \t内存块号 \t外存地址 \t修改值");
for (Page p : pages) {
System.out.println(p + "\t");
}
System.out.println("LRU队列顺序:");
System.out.print(cache.head.next.page.getPageNum()+"<--");
System.out.print(cache.head.next.next.page.getPageNum()+"<--");
System.out.print(cache.tail.pre.pre.page.getPageNum()+"<--");
System.out.print(cache.tail.pre.page.getPageNum());
System.out.println();
} else {
System.out.println("————————————————————执行 " + work.getOperation() + " 操作————————————————————");
System.out.println("操作的页:"+work.getPageNum());
System.out.println("该页存在于缓存中");
//将8进制转10进制再转16进制
String reallyAddress = Integer.toHexString(page.getCacheNum() * 128 + Integer.parseInt(work.getUnitNum(), 8));
System.out.println("绝对地址:" + reallyAddress);
pages.get(work.getPageNum()).setFlag(1);
pages.get(work.getPageNum()).setChangeNum(1);
System.out.println(" 页号 \t标志 \t内存块号 \t外存地址 \t修改值");
for (Page p : pages) {
System.out.println(p + "\t");
}
System.out.println("LRU队列顺序:");
System.out.print(cache.head.next.page.getPageNum()+"<--");
System.out.print(cache.head.next.next.page.getPageNum()+"<--");
System.out.print(cache.tail.pre.pre.page.getPageNum()+"<--");
System.out.print(cache.tail.pre.page.getPageNum());
System.out.println();
}
}
}
}
- 算法设计思路
运行结果