分页式存储管理
●实验目的:
- 熟练掌握分页式管理基本原理,并在实验过程中体现内存空间的分配与回收、地址转换过程。
- 掌握利用“位示图”管理内存与置换空间的分配与回收。
- 掌握基本的位运算。
- 掌握请求分页式存储管理基本原理,并在实验过程中体现内存与置换空间的分配与回收、地址转换以及缺页处理过程。
●实验内容:在实验1基础上实现分页式存储管理内存分配和地址转换过程。进一步实现请求分页式存储管理过程,包括内存和置换空间管理、地址转换以及缺页处理,能够体现FIFO和LRU算法思想。
●参考学时:6学时
●实验提示:
- 建立一个位示图数据结构,用来模拟内存的使用情况。位示图是利用若干位的0/1值代表某类空间的占用状态的数据结构。在本实验中,位示图的位数与设定的物理块个数相同。程序启动时可利用一组随机0或1填充位示图,以模拟程序开始执行是内存已被占用状态。
假设内存大小为64K,块大小为1K,则共有64个块,需要创建如下的位示图数据结构:
#define BLOCK_SIZE 1024 //块大小,定义成宏,便于修改 #define MEM_SIZE 64 //块个数 //定义char型数组,每个char变量占用8位,长度为8,共64位 char bitmap[MEM_SIZE/8]; |
随机填充的代码如下:
#include "time.h" … srand(time(NULL)); for(i=0;i<MEM_SIZE/8;i++) …bitmap[i]=(char)rand(); |
随机填充后的位示图可能的值如图2-1所示。该位示图表示内存的2(0字节第2位)、3(0字节第3位)、6(0字节第6位)、8(1字节第0位)、9(1字节第1位)、12(1字节第4位)、15(1字节第7位)…等块没有被占用。
2.在实验1基础上扩充PCB,添加进程大小和页表:
struct PCB{ … int size; int* page_table; } |
创建进程时输入进程大小,并根据程序中设定的页面大小为进程分配页表空间,并分配物理块。例如,在上图基础上,若要建立一个大小为5000字节的进程,则:
- 计算该进程共有“向上取整(5000/1024)=5”个页,需要占用5个内存块;
- 建立空的页表,即长度为5的一维整数数组;
- 从位示图中找出前5个“0”位在整个位示图中的位置号(即内存中的空闲块块号)(若第i字节第j位为0,则该位在位示图中的位置为8*i+j),并将这些位置号依次填入页表中,同时把对应的“0”改为“1”,以示对应内存块已经分配。
tmp=(struct PCB *)malloc(sizeof(struct PCB));//所创建进程的PCB tmp->size=size;//进程大小 //计算出块个数 block_count=(int)ceil(tmp->size/(double)BLOCK_SIZE); //分配页表 tmp->page_table=(int *)malloc(sizeof(int)*block_count); |
在位示图中判断某字节b的第bit_no位是1还是0代码如下:
int getbit(char b,int bit_no){ //将00000001左移bit_no位,得到如00010000值 char mask=(char)1<<bit_no; if(b&mask) //进行与操作后结果不为0,则第bit_no位一定是1 return 1; else//进行与操作后结果为0,则第bit_no位一定是0 return 0; } |
设置位示图的某字节b的第bit_no位为1或0代码如下:
void setbit(char *b,int bit_no,int flag){ char mask=(char)1<<bit_no;//得到如00010000值 if(flag)//flag为真,表示将第bit_no位设置为1 *b=*b|mask;//进行或操作,对应位变成1 else{//flag为假,表示将第bit_no位设置为0 mask=~mask;//取反,得到如11101111值 *b=*b&mask;//进行与操作,对应位变成0 } } |
- 输入当前执行进程所要访问的逻辑地址,并将其转换成相应的物理地址:
(1)首先编写根据页面大小得到逻辑地址中页号和页内地址分界值(如页面大小为1024,则分界值为log21024=10)
int mylog2(int size){//size为页面大小 return (int)ceil((log10(size)/log10(2))); } |
(2)根据输入的逻辑地址la,计算其页号和页内地址:
int la,pageno,offset,mask; printf("逻辑地址:(<0退出)"); scanf("%d",&la); //将逻辑地址右移若干位,得到页号 pageno=la>>mylog2(BLOCK_SIZE); //将1111…111左移若干位,得到11…11000..00 mask=(0xffffffff)<<mylog2(BLOCK_SIZE); //将11…11000..00取反,得到00…00111..11 mask=~mask; //将逻辑地址与mask进行与操作,得到页内地址 offset=la&mask; |
- 进程退出时,根据其页表内容将位示图对应位置的“1”回填为“0”。
- 扩充页表,将其变成支持请求和置换功能的二维页表(增加存在位等)。创建进程时可装入固定的前三页(或键盘输入初始装入页数,不同进程的装入个数可以不同),其余页装入到置换空间内(置换空间大小应为内存空间大小的1.5-2倍,对其还需建立独立的置换空间位示图)。
- 分别采用FIFO或LRU置换算法对地址转换过程中可能遇到的缺页现象进行页面置换。可将多次地址转换过程中所涉及到的页号视为进程的页面访问序列,从而计算置换次数和缺页率。以下是某次地址变换过程中的交互示例(红色为用户输入,蓝色为程序提示):
逻辑地址:3072 是否为写指令(Y/N):Y 逻辑地址3072对应的页号为:3,页内偏移地址为:0 3号页不在内存,外存块号为12,需置换… 利用FIFO算法选中内存1号页,该页内存块号为6,修改位为1,外存块号为10 将内存6号块内容写入外存10号块,成功 将外存12号块内容调入内存6号块中,置换完毕 逻辑地址3072对应的物理地址为6144 |
代码
#include<iostream>
#include<string.h>
#include<queue>
#include<vector>
#include<list>
#include <array>
#include <random>
#include <algorithm>
#include <numeric>
#include<deque>
using namespace std;
int yechang, kuaichang, kuaidaxiao;
int yehao;
int kuaihao;
int fangwencishu=0, luojidizhi;
vector<int> weishitu(64, 1);
//FIOF
struct FIFO
{
int kuaihao=-1;
int zhuangtaiwei=0;
};
queue<int> FIOFQ;
int queyecishu = 0, zhihuancishu = 0;
int Fwulidizhi;
//LRU
struct LRU
{
int kuaihao = -1;
int zhuangtaiwei = 0;
};
deque<int> LRUQ;
int Lqueyecishu = 0, Lzhihuancishu = 0;
int Lwulidizhi;
void createbitmap()//创建位视图
{
std::random_device rd;
std::mt19937 gen(rd());
std::vector<int> indices(64);
std::iota(indices.begin(), indices.end(), 0);
std::shuffle(indices.begin(), indices.end(), gen);
for (int i = 0; i < kuaichang; ++i) {
weishitu[indices[i]] = 0;
}
}
void showbitmap() {
int flag = 0;
for (int i = 0; i < 64; i++) {
cout << weishitu[i] << ' ';
flag++;
if (flag == 8) {
cout << endl;
flag = 0;
}
}
}
int main() {
cout << "输入页长 块长 块大小(kb):";
cin >> yechang >> kuaichang >> kuaidaxiao;
cout << "位示图" << endl;
vector<FIFO> fifo(yechang);
vector<LRU> lru(yechang);
createbitmap();
showbitmap();
while (1) {
cout << "输入逻辑地址:";
cin >> luojidizhi;
fangwencishu++;
//页号=逻辑地址/(块大小*1024)
yehao = luojidizhi / (kuaidaxiao * 1024);
if (fifo[yehao].zhuangtaiwei != 1) {
FIOFQ.push(yehao);
queyecishu++;
int flag = 0;
for (int i = 0; i < 64; i++) {
if (FIOFQ.size() > kuaichang) {
break;
}
if (weishitu[i] == 0) {
kuaihao = i;
flag = 1;//找到了
fifo[yehao].kuaihao = kuaihao;
fifo[yehao].zhuangtaiwei = 1;
break;
}
}
if (flag == 0) {//没找到
int tyehao = FIOFQ.front();
FIOFQ.pop();
fifo[yehao].kuaihao = fifo[tyehao].kuaihao;
fifo[yehao].zhuangtaiwei = 1;
fifo[tyehao].kuaihao = -1;
fifo[tyehao].zhuangtaiwei = 0;
zhihuancishu++;
}
}
//LRU
if (lru[yehao].zhuangtaiwei != 1) {
LRUQ.push_back(yehao);
Lqueyecishu++;
int flag = 0;
for (int i = 0; i < 64; i++) {
if (LRUQ.size() > kuaichang) {
break;
}
if (weishitu[i] == 0) {
kuaihao = i;
weishitu[i] = 1;
flag = 1;//找到了
lru[yehao].kuaihao = kuaihao;
lru[yehao].zhuangtaiwei = 1;
break;
}
}
if (flag == 0) {//没找到
int tyehao = LRUQ.front();
LRUQ.pop_front();
lru[yehao].kuaihao = lru[tyehao].kuaihao;
lru[yehao].zhuangtaiwei = 1;
lru[tyehao].kuaihao = -1;
lru[tyehao].zhuangtaiwei = 0;
Lzhihuancishu++;
}
}
else if(LRUQ.back()!=yehao){
/* LRUQ.push_back(LRUQ.front());
LRUQ.pop_front();*/
deque<int> t;
while (LRUQ.size()) {
if (LRUQ.front() != yehao) {
t.push_back(LRUQ.front());
}
LRUQ.pop_front();
}
t.push_back(yehao);
LRUQ = t;
}
cout << "位示图\n";
showbitmap();
cout << "FIFO:" << endl;
//物理地址=逻辑地址%1024 + 块号*1024 块大小不是1,1024换成块大小*1024
cout << "物理地址:" << luojidizhi % (1024 * kuaidaxiao) + kuaihao * (1024 * kuaidaxiao) << endl;
cout << "页号 块号 状态位" << endl;
for (int i = 0; i < yechang; i++) {
cout << i << " " << fifo[i].kuaihao << " " << fifo[i].zhuangtaiwei << endl;
}
cout << "栈(底->顶):";
queue<int> t = FIOFQ;
while (t.size())
{
cout << t.front() << ' ';
t.pop();
}
cout << endl;
cout << "第 " << fangwencishu << " 次访问。\n";
cout << "缺页次数:" << queyecishu << " " << "置换次数:" << zhihuancishu << endl;
cout << "缺页率:" << (double)queyecishu / fangwencishu * 100 << "%" << " " << "置换率:" << (double)zhihuancishu / fangwencishu * 100 << "%" << endl;
cout << "LRU:" << endl;
cout << "物理地址:" << luojidizhi % (1024 * kuaidaxiao) + kuaihao * (1024 * kuaidaxiao) << endl;
cout << "页号 块号 状态位" << endl;
for (int i = 0; i < yechang; i++) {
cout << i << " " << lru[i].kuaihao << " " << lru[i].zhuangtaiwei << endl;
}
cout << "栈(底->顶):";
deque<int> tt = LRUQ;
while (tt.size())
{
cout << tt.front() << ' ';
tt.pop_front();
}
cout << endl;
cout << "第 " << fangwencishu << " 次访问。\n";
cout << "缺页次数:" << Lqueyecishu << " " << "置换次数:" << Lzhihuancishu << endl;
cout << "缺页率:" << (double)Lqueyecishu / fangwencishu * 100 << "%" << " " << "置换率:" << (double)Lzhihuancishu / fangwencishu * 100 << "%" << endl;
}
}