分页式存储管理
实验内容
在进程控制实验的基础上实现分页式存储管理内存分配和地址转换过程。进一步实现请求分页式存储管理过程,包括内存和置换空间管理、地址转换以及缺页处理,能够体现FIFO和LRU算法思想。
实验提示
1、 建立一个位示图数据结构,用来模拟内存的使用情况。位示图是利用若干位的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 具有随机值的位示图示例
1、 在实验1基础上扩充PCB,添加进程大小和页表:
struct PCB{ … int size; int* page_table; } |
创建进程时输入进程大小,并根据程序中设定的页面大小为进程分配页表空间,并分配物理块。例如,在上图基础上,若要建立一个大小为5000字节的进程,则:
1. 计算该进程共有“向上取整(5000/1024)=5”个页,需要占用5个内存块;
2. 建立空的页表,即长度为5的一维整数数组;
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); |
3. 从位示图中找出前5个“0”位在整个位示图中的位置号(即内存中的空闲块块号)(若第i字节第j位为0,则该位在位示图中的位置为8*i+j),并将这些位置号依次填入页表中,同时把对应的“0”改为“1”,以示对应内存块已经分配。
在位示图中判断某字节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、 输入当前执行进程所要访问的逻辑地址,并将其转换成相应的物理地址:
(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; |
2、 进程退出时,根据其页表内容将位示图对应位置的“1”回填为“0”。
3、 扩充页表,将其变成支持请求和置换功能的二维页表(增加存在位等)。创建进程时可装入固定的前三页(或键盘输入初始装入页数,不同进程的装入个数可以不同),其余页装入到置换空间内(置换空间大小应为内存空间大小的1.5-2倍,对其还需建立独立的置换空间位示图)。
4、 分别采用FIFO或LRU置换算法对地址转换过程中可能遇到的缺页现象进行页面置换。可将多次地址转换过程中所涉及到的页号视为进程的页面访问序列,从而计算置换次数和缺页率。以下是某次地址变换过程中的交互示例(红色为用户输入,蓝色为程序提示):
逻辑地址:3072 是否为写指令(Y/N):Y 逻辑地址3072对应的页号为:3,页内偏移地址为:0 3号页不在内存,外存块号为12,需置换… 利用FIFO算法选中内存1号页,该页内存块号为6,修改位为1,外存块号为10 将内存6号块内容写入外存10号块,成功 将外存12号块内容调入内存6号块中,置换完毕 逻辑地址3072对应的物理地址为6144 |
实验代码
#include<stdio.h> #include<stdlib.h> #include<time.h> #define BLOCK_SIZE 1024 //块大小,定义成宏,便于修改 #define MEM_SIZE 64 //块个数 typedef struct pcb { int Logical; //逻辑地址 int Physical; //物理地址 }PCB; typedef struct Page { int page; //页 int block; //物理块号 int p; //状态位 }PAGE; typedef struct Stack { int top; int bottom; int num[100]; }STACK; PCB pcb; PAGE P[100]; STACK M; int Flag = 0; int page_Num; //页的个数 int block_Num; //块的个数 int block_Size; //块的大小 //定义char型数组,每个char变量占用8位,长度为8,共64位 char bitmap[MEM_SIZE/8]; int miss_Page_Num = 0; //缺页数 int subst_Num = 0; //置换数 int totle = 0; // 初始化栈 void initStack() { M.top = -1; M.bottom = 0; for (int i = 0; i < block_Num; i++) { M.num[i] = -1; } } // 入栈 void Push(int Num) { if(M.top != block_Num - 1) { M.top++; M.num[M.top] = Num; } else printf("栈已满/n"); } // 出栈 void Pop() { if(M.top != -1) M.num[M.bottom] = -1; else printf("栈已空/n"); } // 初始化页表 void initPage() { for (int i = 0; i < page_Num; i++) { P[i].page = i; P[i].block = NULL; P[i].p = 0; } } void Setbit(char *b, int bit_no, int flag) { char mask = (char)1<<bit_no; if(flag) //flag为真,表示将第bit_no位设置为1 *b = *b|mask; //进行或操作,对应位变为1 else { //flag为假,表示将第bit_no位设置为0 mask = ~mask; //取反,得到如11101111值 *b = *b&mask; //与操作,对应位变成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 return 0; } void srand() { srand(time(NULL)); for(int i = 0; i < MEM_SIZE/8; i++) { bitmap[i] = (char)rand(); } } // 显示位示图 void display_Bitmap() { printf("/n位示图/n"); for (int i = 0; i < MEM_SIZE/8; i++) { printf("/t"); for (int j = 0; j < MEM_SIZE/8; j++) { printf("%d ", Getbit(bitmap[i], j)); } printf("/n"); } } // 显示页表 void display_Page() { printf("/n页表/n"); printf("/t页号/t块号/t状态位/n"); for (int i = 0; i < page_Num; i++) { printf("/t%d/t%d/t%d/n", P[i].page, P[i].block, P[i].p); } } // 创建进程 void create_Process() { while (1) { printf("请输入十进制逻辑地址:/n"); scanf("%d", &pcb.Logical); if (pcb.Logical/block_Size >= page_Num) { printf("输入地址超出页表范围,请重新输入/n"); } else { Flag++; break; } } } // 先进先出算法 void FIFO() { int i_Num = pcb.Logical / block_Size; int flag = 0; int flag1 = 0; totle++; for (int i = 0; i < page_Num; i++) { if (i_Num == P[i].page && P[i].p == 1) { //命中 flag = 1; break; } } if (M.top != block_Num-1 && flag == 0) { // 栈未满 P[i_Num].p = 1; for(int i = 0; i < MEM_SIZE/8; i++) { for (int j = 0; j < 8; j++) { if (Getbit(bitmap[i], j) == 0) { P[i_Num].block = i * 8 + j + 1; Setbit(bitmap, i * 8 + j, true); flag1 = 1; break; } } if (flag1 == 1) break; } Push(i_Num); miss_Page_Num++; } else { // 已满 if (flag == 0) { int d = M.num[M.bottom]; Pop(); for (int i = 0; i < M.top; i++) { M.num[i] = M.num[i+1]; } M.num[M.top] = i_Num; P[i_Num].block = P[d].block; P[i_Num].p = 1; P[d].block = 0; P[d].p = 0; miss_Page_Num++; subst_Num++; } } for (int i = 0; i < page_Num; i++) { if (i_Num == P[i].page) { pcb.Physical = P[i].block * block_Size + (pcb.Logical % block_Size); } } printf("/n/t块号/t物理地址/t缺页次数/t置换次数/t缺页率/t/t置换率/n"); printf("/t%d/t%d/t/t%d/t/t%d/t/t%.2f/t/t%.2f", P[i_Num].block, pcb.Physical, miss_Page_Num, subst_Num, ((float)miss_Page_Num / totle)*100, ((float)subst_Num / totle)*100); printf("/n栈(底->顶):/n"); for (int i = 0; i < block_Num; i++) { printf("/t%d/t", M.num[i]); } printf("/n"); } // LRU算法 void LRU() { int f = 0, t[block_Num]; int flag = 0; int flag1 = 0; totle++; int i_Num = pcb.Logical / block_Size; //商 if (M.top != block_Num-1) { //栈未满 for(int i = 0; i < page_Num; i++) { if (i_Num == P[i].page && P[i].p == 1) { //命中 flag = 1; for (int j = 0; j < block_Num; j++) { if (i_Num == M.num[j]) f = j; } int temp = M.num[f]; int H = f; for (int k = f+1; M.num[k] != -1; k++) { M.num[k-1] = M.num[k]; H++; } M.num[H] = temp; break; } } if (flag == 0) { P[i_Num].p = 1; for(int i = 0; i < MEM_SIZE/8; i++) { for (int j = 0; j < 8; j++) { if (Getbit(bitmap[i], j) == 0) { P[i_Num].block = i * 8 + j + 1; Setbit(bitmap, i * 8 + j, true); flag1 = 1; break; } } if (flag1 == 1) break; } Push(i_Num); miss_Page_Num++; } } //已满 else { for (int i = 0; i < page_Num; i++) { if (i_Num == P[i].page && P[i].p == 1) { //命中 flag = 1; for(int j = 0; j < block_Num; j++) { if (i_Num == M.num[j]) { f = j; } } int temp = M.num[f]; for (int k = f+1; k < block_Num; k++) { M.num[k-1] = M.num[k]; } M.num[M.top] = temp; } } if (flag == 0) { int d = M.num[M.bottom]; Pop(); for (int i = 0; i < M.top; i++) { M.num[i] = M.num[i+1]; } M.num[M.top] = i_Num; P[i_Num].block = P[d].block; P[i_Num].p = 1; P[d].block = 0; P[d].p = 0; miss_Page_Num++; subst_Num++; } } for (int i = 0; i < page_Num; i++) { if (i_Num == P[i].page) { pcb.Physical = P[i].block * block_Size + (pcb.Logical % block_Size); } } printf("/n/t块号/t物理地址/t缺页次数/t置换次数/t缺页率/t/t置换率/n"); printf("/t%d/t%d/t/t%d/t/t%d/t/t%.2f/t/t%.2f", P[i_Num].block, pcb.Physical, miss_Page_Num, subst_Num, ((float)miss_Page_Num / totle)*100, ((float)subst_Num / totle)*100); printf("/n栈(底->顶):/n"); for (int i = 0; i < block_Num; i++) { printf("/t%d/t", M.num[i]); } printf("/n"); } void menu() { printf("/n"); printf("/t*********************************/n"); printf("/t*/t1.查看当前位示图/t*/n"); printf("/t*/t2.查看当前页表/t/t*/n"); printf("/t*/t3.创建一个新进程/t*/n"); printf("/t*********************************/n"); } void initPrint() { printf("请设置页数/n"); scanf("%d", &page_Num); printf("请设置块数/n"); scanf("%d", &block_Num); printf("请设置块的大小(B)/n"); scanf("%d", &block_Size); } int main() { initPrint(); initStack(); srand(); initPage(); printf("请输入算法1.(FIFO)/2.(FRU)/n"); int n; scanf("%d", &n); switch(n) { case 1: while(1) { int k; menu(); printf("请输入要输入的选择:"); scanf("%d", &k); switch(k) { case 1: display_Bitmap(); break; case 2: display_Page(); break; case 3: create_Process(); FIFO(); break; default: printf("您输入的选项不存在!!!"); break; } } break; case 2: while(1) { int k; menu(); printf("请输入要输入的选择:"); scanf("%d", &k); switch(k) { case 1: display_Bitmap(); break; case 2: display_Page(); break; case 3: create_Process(); LRU(); break; default: printf("您输入的选项不存在!!!"); break; } } break; } }
原创文章,作者:kirin,如若转载,请注明出处:https://blog.ytso.com/268065.html