一、先进先出置换算法
1.主要内容
基本思想是淘汰最先进入内存的页面,即选择在内存驻留时间最长的页面予以淘汰。实现简单。按页面调入内存的先后链结为队列,设置一个替换指针,总是指向最先进入内存的页面。缺点在与进程实际运行规律不符,性能不好。
2.算法原理
先进先出算法的核心思想是总是淘汰最先进入内存的页面,即选择在内存中驻留最久的页面予以淘汰,FIFO算法基于“先来先服务”的原则。在这种算法中,页面被存储在队列中,当一个新页面需要加载到内存而内存已满时,最早进入队列(即在内存中停留时间最长)的页面将被移除,为新页面腾出空间,FIFO算法假设最早加载的页面最不可能在近期被再次访问。
3.算法实现
#include <stdio.h>
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
const int MAXSIZE = 1000; // 定义页访问流的最大长度
const int MAXQUEUE = 3; // 定义可用页面数
typedef struct node {
int loaded;
int hit;
} page;
page pages[MAXQUEUE]; // 定义页面表
int queue[MAXSIZE];
int quantity;
vector<int> page_out_flow; // 用来记录页面调出流
// 初始化结构函数
void initial() {
for (int i = 0; i < MAXQUEUE; i++) {
pages[i].loaded = -1;
pages[i].hit = 0;
}
for (int i = 0; i < MAXSIZE; i++) {
queue[i] = -1;
}
quantity = 0;
page_out_flow.clear();
}
// 初始化页面函数
void init() {
for (int i = 0; i < MAXQUEUE; i++) {
pages[i].loaded = -1;
pages[i].hit = 0;
}
}
void readData() {
cout << "请输入页访问流数据:" << endl;
int page;
while (cin >> page) { // 读取页面号
if (quantity < MAXSIZE) {
queue[quantity++] = page;
} else {
cout << "页访问流长度超过最大限制,输入结束。" << endl;
break;
}
// 检查是否到达行尾,即换行符
if (cin.peek() == '\n') {
break; // 如果是换行符,则结束输入
}
}
}
// FIFO调度算法
void FIFO() {
int i, j, p = 0, flag;
int absence = 0;
cout << '\n' << "先进先出调度算法(FIFO)主存页面放置情况:\n";
for (i = 0; i < quantity; i++) {
flag = 0;
for (j = 0; j < MAXQUEUE; j++) {
if (pages[j].loaded == queue[i]) {
flag = 1;
break;
}
}
if (flag == 0) { // 页面不在内存中,发生缺页
absence++;
if (p >= MAXQUEUE) p = 0;
// 记录被调出的页面
if (pages[p].loaded != -1) {
page_out_flow.push_back(pages[p].loaded);
}
pages[p].loaded = queue[i];
p++;
}
// 输出当前主存中的页面放置情况
cout << "访问页面 " << queue[i] << " -> ";
for (j = 0; j < MAXQUEUE; j++) {
if (pages[j].loaded != -1) {
cout << pages[j].loaded << " ";
}
}
cout << endl;
}
cout << endl << "总缺页数:" << absence << endl;
cout << "页面调出流:";
for (int page : page_out_flow) {
cout << page << " ";
}
cout << endl;
}
// 显示
void version() {
cout << " /*******************先进先出调度算法(FIFO)****************/" << endl;
cout << endl;
}
int main() {
version();
initial();
readData();
FIFO();
init();
return 0;
}
4.运行结果
5.结果分析
从键盘输入页访问流数据为1 4 3 1 2 5 1 4 2 1 4 5,主存块数为3,采用先进先出调度算法,初始访问时主存中无数据
访问1,缺页,将1加载到第一个主存块中[1,-1,-1],-1代表为空
访问4,缺页,将4加载到第二个主存块中[1,4,-1]
访问3,缺页,将3加载到第三个主存块中[1,4,3]
访问1,命中,已在主存中即[1,4,3]
访问2,缺页,将最先进到主存中的页面替换掉即替换1,即[2,4,3]
访问5,缺页,将最先进到主存中的页面替换掉即替换4,即[2,5,3]
访问1,缺页,将最先进到主存中的页面替换掉即替换3,即[2,5,1]
访问4,缺页,将最先进到主存中的页面替换掉即替换2,即[4,5,1]
访问2,缺页,将最先进到主存中的页面替换掉即替换5,即[4,2,1]
访问1,命中,已在主存中即[4,2,1]
访问4,命中,已在主存中即[4,2,1]
访问5,缺页,将最先进到主存中的页面替换掉即替换1,即[4,2,5]
故利用该算法进行页面调度最终缺页次数为9,页面调出流为1 4 3 2 5 1,这与上述运行结果一致。
二、最佳置换算法
1.主要内容
基本思想是所选择的被淘汰页面,将是以后永不使用的,或是在最长(未来)时间内不再被访问的页面。采用最佳置换算法,可保证获得最低的缺页率。
2.算法原理
最佳置换算法的原理是在未来信息已知的情况下,总是选择将来不会被使用,或者在最长时间内不会被使用的页面进行置换。换句话说,当需要置换页面时,最佳算法会查看未来的页面访问序列,并选择在未来最远才会被访问的页面进行置换。这种算法可以保证最少的缺页次数,但由于需要预知未来的页面访问请求,这在实际中是不可能的,因此它主要用于理论上的性能比较。
3.算法实现
#include <stdio.h>
#include <stdlib.h>
#define FRAME_SIZE 3 // 物理内存的页框数,题目要求为3
// 函数:查找页框中是否有该页面
int page_in_frame(int frames[], int size, int page) {
for (int i = 0; i < size; i++) {
if (frames[i] == page) {
return 1; // 页框中有该页面
}
}
return 0; // 页框中没有该页面
}
// 函数:找出最佳替换页面的索引
int get_optimal_page_to_replace(int frames[], int frame_size, int pages[], int current_index, int total_pages) {
int furthest_index = -1; // 用来记录最远使用的页面的索引
int page_to_replace = -1; // 要替换的页面
// 对每个在内存中的页面,找出它们下次出现的位置
for (int i = 0; i < frame_size; i++) {
int j;
for (j = current_index; j < total_pages; j++) {
if (frames[i] == pages[j]) {
break; // 找到该页面的下次访问位置
}
}
// 如果页面之后再也不会被访问
if (j == total_pages) {
return i;
}
// 找到页面下次访问的位置,如果当前页面的下次访问位置更远,则替换它
if (j > furthest_index) {
furthest_index = j;
page_to_replace = i;
}
}
return page_to_replace;
}
int main() {
int pages[100], frames[FRAME_SIZE];
int total_pages = 0, page_faults = 0;
int page_out_flow[100], page_out_index = 0; // 用来记录页面调出流
printf(" /*******************先进先出调度算法(FIFO)****************/\n\n");
// 输入页面访问流
printf("请输入页面访问流:\n");
int page, ch;
while ((ch = getchar()) != '\n' && ch != EOF) {
if (ch == ' ' || ch == '\t') continue; // 忽略空格和制表符
page = 0;
// 读取完整的数字
while (ch >= '0' && ch <= '9') {
page = page * 10 + (ch - '0');
ch = getchar();
}
ungetc(ch, stdin); // 将非数字字符放回输入缓冲区
pages[total_pages++] = page;
}
// 初始化页框
for (int i = 0; i < FRAME_SIZE; i++) {
frames[i] = -1; // 初始状态,页框为空
}
printf("最近最久未使用LRU算法主存页面放置情况:\n");
// 开始模拟页面置换过程
for (int i = 0; i < total_pages; i++) {
int page = pages[i];
// 如果页面已经在内存中,不发生缺页
if (page_in_frame(frames, FRAME_SIZE, page)) {
// 什么都不做,继续下一次循环
} else {
// 如果页面不在内存中,发生缺页
page_faults++;
// 找一个空的页框替换页面
int replace_index = -1;
for (int j = 0; j < FRAME_SIZE; j++) {
if (frames[j] == -1) {
replace_index = j;
break;
}
}
// 如果没有空的页框,使用最佳置换算法选择页面替换
if (replace_index == -1) {
replace_index = get_optimal_page_to_replace(frames, FRAME_SIZE, pages, i + 1, total_pages);
// 记录被调出的页面
page_out_flow[page_out_index++] = frames[replace_index];
}
// 替换页面
frames[replace_index] = page;
}
// 输出当前页框状态
printf("访问页面 %d -> ", page);
for (int j = 0; j < FRAME_SIZE; j++) {
if (frames[j] != -1) {
printf("%d ", frames[j]);
}
}
printf("\n");
}
printf("\n");
// 输出总缺页数
printf("总缺页数:%d\n", page_faults);
// 输出页面调出流
if (page_out_index > 0) {
printf("页面调出流:");
for (int i = 0; i < page_out_index; i++) {
printf("%d ", page_out_flow[i]);
}
printf("\n");
} else {
printf("没有页面被调出\n");
}
return 0;
}
4.运行结果
5.结果分析
访问1,缺页,将1加载到第一个主存块中[1,-1,-1],-1代表为空
访问4,缺页,将4加载到第二个主存块中[1,4,-1]
访问3,缺页,将3加载到第三个主存块中[1,4,3]
访问1,命中,已在主存中即[1,4,3]
访问2,缺页,替换未来最长时间内不再被访问的页面即3,即[1,4,2]
访问5,缺页,替换未来最长时间内不再被访问的页面即2,即[1,4,5]
访问1,命中,已在主存中即[1,4,5]
访问4,命中,已在主存中即[1,4,5]
访问2,缺页,替换未来最长时间内不再被访问的页面即5,即[1,4,2]
访问1,命中,已在主存中即[1,4,2]
访问4,命中,已在主存中即[1,4,2]
访问5,缺页,替换未来最长时间内不再被访问的页面即1,即[5,4,2]
故利用该算法进行页面调度最终缺页次数为7,页面调出流为3 2 5 1,图中仅输出了发生缺页时的情况,这与上述运行结果一致。
三、LRU置换算法
1.主要内容
基本思想是最近未访问的页面,将来一段时间也不会访问。利用局部性原理,根据一个进程在执行过程中过去的页面访问踪迹来推测未来的行为。最近的过去 → 最近的将来 思想:选择最近最久未使用的页面予以淘汰。利用页表中的访问字段,记录页面自上次被访问以来所经历的时间t,需要淘汰页面时,选择在内存页面中t值最大的,即最近最久未使用的页面予以淘汰
2.算法原理
最近最久未使用页面置换算法是根据页面调入内存后的使用情况做出决策的,由于无法预测各个页面将来的使用情况,只能利用“最近的过去”作为“最近的将来”的近似,因此,最近最久未使用页面置换算法是选择最近最久未使用的页面予以淘汰。
3.算法实现
#include <stdio.h>
#include <iostream>
#include <vector>
using namespace std;
const int MAXSIZE = 1000; // 定义页访问流的最大长度
const int MAXQUEUE = 3; // 定义可用页面数
typedef struct node {
int loaded;
int nextUse; // 记录下一次使用该页面的位置
} page;
page pages[MAXQUEUE]; // 定义页面表
int queue[MAXSIZE];
int quantity;
vector<int> page_out_flow; // 用来记录页面调出流
// 初始化结构函数
void initial() {
for (int i = 0; i < MAXQUEUE; i++) {
pages[i].loaded = -1;
pages[i].nextUse = MAXSIZE; // 初始化为最大长度,表示最远
}
for (int i = 0; i < MAXSIZE; i++) {
queue[i] = -1;
}
quantity = 0;
page_out_flow.clear();
}
// 初始化页面函数
void init() {
for (int i = 0; i < MAXQUEUE; i++) {
pages[i].loaded = -1;
pages[i].nextUse = MAXSIZE;
}
}
void readData() {
cout << "请输入页访问流数据:" << endl;
int page;
while (cin >> page) {
if (quantity < MAXSIZE) {
queue[quantity++] = page;
} else {
cout << "页访问流长度超过最大限制,输入结束。" << endl;
break;
}
if (cin.peek() == '\n') {
break;
}
}
}
// 最近最少使用调度算法(LRU)
void LRU() {
int absence = 0;
int i, j, flag, minIndex;
cout << '\n' << "最近最久未使用LRU算法主存页面放置情况:\n";
for (i = 0; i < quantity; i++) {
flag = -1;
for (j = 0; j < MAXQUEUE; j++) {
if (queue[i] == pages[j].loaded) {
flag = j;
pages[j].nextUse = MAXSIZE; // 更新为最远
break;
}
}
if (flag == -1) { // 缺页处理
absence++;
if (absence > MAXQUEUE) { // 需要替换页面
// 找到最久未使用的页面
minIndex = 0;
for (j = 1; j < MAXQUEUE; j++) {
if (pages[j].nextUse < pages[minIndex].nextUse) {
minIndex = j;
}
}
page_out_flow.push_back(pages[minIndex].loaded); // 记录被调出的页面
pages[minIndex].loaded = queue[i];
pages[minIndex].nextUse = i + 1;
} else { // 直接加载新页面
pages[absence - 1].loaded = queue[i];
pages[absence - 1].nextUse = i + 1;
}
}
// 输出当前主存中的页面放置情况
cout << "访问页面 " << queue[i] << " -> ";
for (j = 0; j < MAXQUEUE; j++) {
if (pages[j].loaded != -1) {
cout << pages[j].loaded << " ";
}
}
cout << endl;
}
cout << endl << "总缺页数:" << absence << endl;
cout << "页面调出流:";
for (int page : page_out_flow) {
cout << page << " ";
}
cout << endl;
}
// 显示
void version() {
cout << " /*******************最近最久未使用LRU算法****************/" << endl;
cout << endl;
}
int main() {
version();
initial();
readData();
LRU();
return 0;
}
4.运行结果
5.结果分析
从键盘输入页访问流数据为1 4 3 1 2 5 1 4 2 1 4 5,主存块数为3,采用先进先出调度算法,初始访问时主存中无数据
访问1,缺页,将1加载到第一个主存块中[1,-1,-1],-1代表为空
访问4,缺页,将4加载到第二个主存块中[1,4,-1]
访问3,缺页,将3加载到第三个主存块中[1,4,3]
访问1,命中,已在主存中,即[1,4,3]
访问2,缺页,将最近最久未使用的4替换掉,即[1,2,3]
访问5,缺页,将最近最久未使用的3替换掉,即[1,2,5]
访问1,命中,已在主存中,即[1,2,5]
访问4,缺页,将最近最久未使用的2替换掉,即[1,4,5]
访问2,缺页,将最近最久未使用的5替换掉,即[1,4,2]
访问1,命中,已在主存中即[1,4,2]
访问4,命中,已在主存中即[1,4,2]
访问5,缺页,将最近最久未使用的2替换掉,即[1,4,5]
故利用该算法进行页面调度最终缺页次数为8,页面调出流为4 3 2 5 2,这与上述运行结果一致。