操作系统课程设计实验报告
设计题目六:模拟实现请求分页虚存页面替换
——
一、设计题目要求
(1)了解存储管理的基本目的和功能;
(2)理解实存管理的原理和实现技术;
(3)理解虚存管理的原理和实现技术;
(4)通过编程模拟实现请求分页虚存管理和替换算法。
二、程序设计思路及流程图
程序功能简介:
实现虚存页面替换算法的模拟程序含有以下功能:
(1)接收用户输入的参数:程序长度(页面数)、页框个数及页面大小;
(2)程序结果采用不同的颜色区分命中、替换及直接加入空闲块;
(3)实现FIFO替换算法。
维护两个数据结构,即请求页面队列和主存块队列。其中请求页面队列为进程所用, 记录当前进程请求的页面块信息。而主存块队列由系统维护,该队列保存当前系统 中给人主存块的状态(包括最后访问时间、忙闲状态等)。各种替换算法将以这两个 数据结构为基础,在系统中为用户请求寻求最佳块节点。
程序设计思路:
程序主要分为三大部分:
1.模拟进程访问页面:
(1)根据用户输入的进程号找到相应的页表,在该页表中查询该页号的页面是否在主存 中,若已在主存中,则直接访问,修改贮存队列中的访问时间。
(2)若不在主存队列中,则需要将其装入至主存队列,若可以找到一块空闲帧,则将其 装入至主存队列中,修改在主存中的信息:processID,pageID,state,time_last, BlockQueue中。
(3)若未在主存队列中,找到空闲帧,则需在驻留集队列中替换一个页面,替换哪一个 呢?根据FIFO算法,替换队头结点,即删除队头结点,同时将待访问页面构造为一 个新结点,插入至队尾,同时根据所删除结点的帧号在主存中队列中相应帧信息 processID,pageID,state,time_last,也要修改访问页面的信息frameID, state。
2.模拟进程结束:
进程结束,即将此进程在内存中所占的空间释放要做3件事:
(1)从驻留集中将processID为此进程的结点删除;
(2) 主存队列中释放该进程的所有页面,即修改processID,pageID,state,time_last, 重置为初始状态;
(3)修改此进程的页表的frameID, state,置为初始状态。
3.显示部分:
(1)遍历进程的页表,打印;
(2)遍历主存队列的页表,打印;
(3)display queue,显示驻留集队列的信息。
程序流程图:
见附录A
三、涉及的背景知识及所用函数简介
1、malloc函数
函数原型 :void *malloc(unsigned int num_bytes);
头文件 :stdlib.h
作用 :分配长度为num_bytes个字节的内存块
返回值 :分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
2、asctime函数
函数原型 :char *asctime(const struct tm * timeptr);
头文件 :#include <time.h>
作用 :将时间日期以字符串格式表示参数
返回值 :若再调用相关的时间日期函数,此字符串可能会被破坏。此函数与 ctime 不同处在于传入的参数是不同的结构。
函数说明 :asctime()将参数timeptr 所指的tm 结构中的信息转换成真实世 界所使用的时间日期表示方法,然后将结果以字符串形态返回。
3、gmtime函数
函数原型 :struct tm *gmtime(const time_t *timep);
头文件 :#include <time.h>
作用 :获取当前时间和日期
函数说明 :gmtime()将参数timep 所指的time_t 结构中的信息转换成真实世 界所使用的时间日期表示方法,然后将结果由结构tm 返回。
返回值 :返回结构tm 代表目前UTC 时间。
4、strcpy 函数
函数原型 :extern char strcpy(char dest,const char src);
头文件 :string,h
作用 :把从src地址开始且含有NULL结束符的字符串复制到已dest开始 的地址空间
参数 :scr:原串
Dest:目标串
返回值 :目标串的地址
四、程序所用数据结构简介
#define BUSY 1
#define IDLE 0 分别定义驻留集和主存队列中的结点的状态;
#define IN 1
#define OUT 0 分别定义页表中的结点的状态,即是否装入内存;
#define processNumber 3 提前定义好进程数目,不需要用户输入;
#define pageNumber 5 提前定义好一个进程的页面数目,不需要用户输入;
#define blockNumber 6 提前定义好主存帧数目,不需要用户输入;
#include<time.h> 时间函数需要的头文件;
typedef struct Page
{
int frameID;
int state;
}Page; 定义一个页表项的信息;
typedef struct Block
{
int processID;
int pageID;
int state;
char time_last[50];
}Block; 定义一个主存队列表项的信息;
typedef struct process
{
Page page_table[pageNumber + 1];
unsigned int pageLength;
}process;定义一个进程的信息;
typedef struct node
{
int frameID;
int processID;
int pageID;
struct node *next;
}BlockNode; 定义一个驻留集结点的信息;
typedef struct
{
BlockNode *front;
BlockNode *rear;
}BlockQueue; 定义一个驻留集队列;
BlockQueue Q;//!!!! 全局变量,驻留集队列;
Block block_table[blockNumber + 1]; 全局变量,主存队列表;
process pro[processNumber + 1] ; 全局变量,数组存放多个进程;
int input_proID, input_pagID; 全局变量,用户输入丰进程号和页号;
//choose;
void Visit_a_page(); 访问一个页面;
void End_a_process(); 结束一个进程;
void Show_the_queue_information(); 显示驻留集队列中的信息;
void Show_the_block_table_information(); 显示主存队列表中的信息;
void Show_the_page_table_information_of_the_process(); 显示一个进程中的页表 的信息;
void Exit_the_system(); 退出系统;
void init_system(); 初始化系统;
void get_current_time(char *time_last); 获取当前时间;
int is_in_pagetable(int proID, int pagID); 判断某个进程的某页是否已装入内存;
int search_blocktable(int proID, int pagID); 在主存队列中查找某个进程的某一页;
int search_IDLE_block_in_blocktable(); 在驻留集队列中查找空闲帧;
void clear_all_process_in_blocktable(int proID); 在主存队列删除某个进程的全部 页面;
void clear_pagetable(int proID); 删除某个进程的全部页面,即清空其页表;
//queue;
void init_queue(BlockQueue *Q); 初始化队列;
void enter_queue(BlockQueue *Q, BlockNode *elem); 入队;
void get_head_queue(BlockQueue *Q, BlockNode *elem); 获取对头信息;
void delete_head_queue(BlockQueue *Q, BlockNode *elem);出队;
void delete_all_process_in_queue(BlockQueue *Q, int proID);删除一个进程在驻留 集队列中的全部页面 ;
void display_queue(BlockQueue *Q); 显示整个队列的信息;
五、程序源代码
见附录B
六、调试方案及调试过程记录与分析
测试结果、调试过程:
第一次测试结果:没有任何输出,显示内存信息错误。
第一次调试:在所有可能的地方加printf,测试是在哪里崩溃的,找到错误的地方,是因为没有初始化队列数据结构,另外在定义一个队列时,应该是包含有头指针和尾指针的结构体,而不是结构体指针,若是结构体指针,应该给它申请一块内存空间。当时找这个错误浪费了很长时间,还一度怀疑是自己的init_queue函数没有写对,malloc不能申请到内存。
第二次测试结果:几次修改之后,队列可以正确初始化。但是,只能插入一个结点,猜想是入队函数enter_queue没有写正确。
第二次调试:当初忘记自己写的是带头结点的队列了,在对新节点赋值是写成了Q->front,应该是Q->front->next。
第三次测试结果:enter_queue可以正确执行,随后又对delete_head_queue函数进行了验证修改,声明临时指针变量,指向被删除结点,以备释放内存使用。
第三次调试:至此,main中的case1验证完毕,然后验证case2。主要是对delete_all_process_in_queue的验证,开始以为,队列里只允许从队头删除元素,队尾插入元素, 若要从中间删除一个结点,则跟单链表的delete函数是一样的。于是便模仿着写了,但执行时,第一个结里点可以正确删除,之后的却不对,在显示队列结点的信息时,会有垃圾数字出现。后来突然明白,之前动态多分区里的链表的delete函数只是执行一次最多就删除一个,而delete_all_process_in_queue要删除多个,当删除一个节点之后,指向当前结点的指针curr所指向的结点已经没有了,curr是一个游离的指针,此时应该重新给curr赋值。
第四次测试结果:case2可以正确执行case3-6,比较简单,没有错误,程序可以正确执行。
七、思考与体会
这次实验主要是对带有头结点、包含队头队尾指针的队列数据结构的操作,受前面动态多分区链表结构的影响,也出现了一些错误,在改正这些错误的过程中,也逐渐体会到了二者的区别和用法,以及当初为什么要建立这两个模型,感觉这些前辈们真的是太聪明了。从动态多分区和请求分页替换算法(虽然只实现了FIFO)中,深深地体会到了逻辑思维严密的重要性,同时还要考虑一些特殊情况。
这学期的操作系统课程设计的六个题目,有大有小,有自己觉得写得好的,也有自己不满意的,但是都是自己亲自写的,很佩服自己能写出这么多行的代码,很佩服自己能找出错误,改正错误,同时也很讨厌自己很马虎,犯一些不该犯的错误,但是应该坚信,没有什么不可以的,一切皆有可能。还记得第一节课的时候,听到要写这么难的程序真的是头都大了,因为那些理论知识都忘了,当初学得也不好,但有些事,当硬着头皮去做的时候,收获往往很大,而且完成之后,自己也对自己很惊讶,“哇!我竟然写完了!真不可思议!”。这次课程不仅对操作系统的理论知识理解更加深刻了,而且在动手编程能力上对自己也有了信心,操作系统的一些思想让我们在以后思考问题时思维更加严谨,更有逻辑,对于解决生活中的一些问题也是有帮助的。
/************************************************************************* > File Name: PageDemanding.h > Author: > Mail: > Created Time: 2014年10月29日 星期三 15时11分10秒 ************************************************************************/ #ifndef _PAGEDEMANDING_H #define _PAGEDEMANDING_H #define BUSY 1 #define IDLE 0 #define IN 1 #define OUT 0 #define processNumber 3 #define pageNumber 5 #define blockNumber 6 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> #include<time.h> typedef struct Page { int frameID; int state; }Page; typedef struct Block { int processID; int pageID; int state; char time_last[50]; }Block; typedef struct process { Page page_table[pageNumber + 1]; unsigned int pageLength; }process; typedef struct node { int frameID; int processID; int pageID; struct node *next; }BlockNode; typedef struct { BlockNode *front; BlockNode *rear; }BlockQueue; BlockQueue Q;//!!!! Block block_table[blockNumber + 1]; process pro[processNumber + 1] ; int input_proID, input_pagID; //choose; void Visit_a_page(); void End_a_process(); void Show_the_queue_information(); void Show_the_block_table_information(); void Show_the_page_table_information_of_the_process(); void Exit_the_system(); void init_system(); void get_current_time(char *time_last); int is_in_pagetable(int proID, int pagID); int search_blocktable(int proID, int pagID); int search_IDLE_block_in_blocktable(); void clear_all_process_in_blocktable(int proID); void clear_pagetable(int proID); //queue; void init_queue(BlockQueue *Q); void enter_queue(BlockQueue *Q, BlockNode *elem); void get_head_queue(BlockQueue *Q, BlockNode *elem); void delete_head_queue(BlockQueue *Q, BlockNode *elem); void delete_all_process_in_queue(BlockQueue *Q, int proID); void display_queue(BlockQueue *Q); #endif /************************************************************************* > File Name: main.c > Author: > Mail: > Created Time: 2014年10月29日 星期三 15时08分08秒 ************************************************************************/ #include"PageDemanding.h" int main(void) { int choose; init_system(); init_queue(&Q); while(1) { printf("\nWelcome To The DemandPagingSystem_FIFO!\n"); printf("******1.Visit a page; ******\n"); printf("******2.End a process; ******\n"); printf("******3.Show the queue_information; ******\n"); printf("******4.Show the block_table_information; ******\n"); printf("******5.Show the page_table_information of the Process;******\n"); printf("******6.Exit the system. ******\n"); printf("Please input your choose:\n"); scanf("%d", &choose); switch(choose) { case 1: Visit_a_page(); break; case 2: End_a_process(); break; case 3: Show_the_queue_information(); break; case 4: Show_the_block_table_information(); break; case 5: Show_the_page_table_information_of_the_process(); break; case 6: Exit_the_system(); break; default: printf("ERROR!Please input again!\n"); } }//while return 0; } /************************************************************************ > File Name: FIFO.c > Author: > Mail: > Created Time: 2014年10月29日 星期三 15时09分08秒 ************************************************************************/ #include"PageDemanding.h" void Visit_a_page() { int temp_blockID; int i; int x; printf("Please input the input_proID, input_pagID:"); scanf("%d%d", &input_proID, &input_pagID); if(is_in_pagetable(input_proID, input_pagID)) { temp_blockID = search_blocktable(input_proID, input_pagID); get_current_time(block_table[temp_blockID].time_last); } else { temp_blockID = search_IDLE_block_in_blocktable(); if(temp_blockID != 0) { pro[input_proID].page_table[input_pagID].frameID = temp_blockID; pro[input_proID].page_table[input_pagID].state = IN; block_table[temp_blockID].processID = input_proID; block_table[temp_blockID].pageID = input_pagID; block_table[temp_blockID].state = BUSY; get_current_time(block_table[temp_blockID].time_last); BlockNode *create_node; create_node = (BlockNode *)malloc(sizeof(BlockNode)); create_node->frameID = temp_blockID; create_node->processID = input_proID; create_node->pageID = input_pagID; create_node->next = NULL; enter_queue(&Q, create_node); } else { BlockNode *elem, *p; elem = (BlockNode*)malloc(sizeof(BlockNode));//** p = (BlockNode *)malloc(sizeof(BlockNode));//** get_head_queue(&Q, elem); *p = *elem;//*** p->next = NULL; elem->processID = input_proID; elem->pageID = input_pagID; get_current_time(block_table[elem->frameID].time_last);// elem->next = NULL; enter_queue(&Q, elem); delete_head_queue(&Q, p); pro[input_proID].page_table[input_pagID].frameID = elem->frameID; pro[input_proID].page_table[input_pagID].state = IN; block_table[elem->frameID].processID = input_proID; block_table[elem->frameID].pageID = input_pagID; block_table[elem->frameID].state = BUSY; get_current_time(block_table[temp_blockID].time_last); }//else }//else } void End_a_process() { printf("Please input the input_proID:"); scanf("%d", &input_proID); clear_all_process_in_blocktable(input_proID); clear_pagetable(input_proID); delete_all_process_in_queue(&Q, input_proID); return; } void Show_the_queue_information() { printf("The BlockQueue information is :\n"); display_queue(&Q); return; } void Show_the_block_table_information() { int i; printf("The block_table information:\n"); printf("framID\tproID\tpagID\tstate\ttimlast\n"); for(i = 1; i <= blockNumber; i++) { printf("%d\t%d\t%d\t%d\t%s\n", i, block_table[i].processID, block_table[i].pageID, block_table[i].state, block_table[i].time_last); } return ; } void Show_the_page_table_information_of_the_process() { int i; printf("Please input the input_proID:"); scanf("%d", &input_proID); printf("\n"); printf("The process %d page_table is:\n", input_proID); printf("pagID\tframID\tstate\n"); for(i = 1; i <= pageNumber; i++) { printf("%d\t%d\t%d\n", i,pro[input_proID].page_table[i].frameID, pro[input_proID].page_table[i].state); } return ; } void Exit_the_system() { exit(0); return; } /*************************************************************************i > File Name: PageDemanding.c > Author: > Mail: > Created Time: 2014年10月29日 星期三 15时10分52秒 ************************************************************************/ #include"PageDemanding.h" void init_system() { int i, j; for(i = 1; i <= blockNumber; i++)//init BlockTable; { block_table[i].processID = 0; block_table[i].pageID = 0; block_table[i].state = IDLE; strcpy(block_table[i].time_last, "0000"); } for(i = 1; i <= processNumber; i++)//init all ProcessTable; { pro[i].pageLength = pageNumber; for(j = 1; j <= pageNumber; j++)//init all pageTable { pro[i].page_table[j].frameID = 0; pro[i].page_table[j].state = OUT; } } return; } void get_current_time(char *time_last) { time_t timep; time(&timep); sprintf(time_last,"%s", asctime(gmtime(&timep))); return ; } int is_in_pagetable(int proID, int pagID) { if(pro[proID].page_table[pagID].state == IN) { return 1; } else return 0; } int search_blocktable(int proID, int pagID)//return the blockID; { int i; for(i = 1; i <= blockNumber; i++) { if(block_table[i].processID == input_proID && block_table[i].pageID == pagID) return i; } return 0; } int search_IDLE_block_in_blocktable() { int i; for(i = 1; i <= blockNumber; i++) { if(block_table[i].state == IDLE) { return i; } } return 0; } void clear_all_process_in_blocktable(int proID) { int i; for(i = 1; i <= blockNumber; i++)//modify the block_table; { if(block_table[i].processID == proID) { block_table[i].processID = 0; block_table[i].pageID = 0; block_table[i].state = IDLE; strcpy(block_table[i].time_last, "0000"); } } return ; } void clear_pagetable(int proID) { int i; for(i = 1; i <= pageNumber; i++)//modify the page_table; { pro[proID].page_table[i].frameID = 0; pro[proID].page_table[i].state = OUT; } return ; } /************************************************************************* > File Name: queue.c > Author: > Mail: > Created Time: 2014年10月29日 星期三 16时59分55秒 ************************************************************************/ #include"PageDemanding.h" //with head node queue; void init_queue(BlockQueue *Q) { Q->front = (BlockNode *)malloc(sizeof(BlockNode)); Q->front->next = NULL; Q->rear = Q->front; return ; } void enter_queue(BlockQueue *Q, BlockNode *elem) { Q->rear->next = elem; Q->rear = elem; return ; } void get_head_queue(BlockQueue *Q, BlockNode *elem) { *elem = *(Q->front->next); elem->next = NULL; return; } void delete_head_queue(BlockQueue *Q, BlockNode *elem) { if(Q->front == Q->rear) return ; *elem = *(Q->front->next);// BlockNode *item; item = Q->front->next; Q->front->next = item->next; if(Q->rear == item) Q->rear = Q->front; free(item); return ; } void delete_all_process_in_queue(BlockQueue *Q, int proID) { BlockNode *prev,*curr, *p; for(curr = Q->front->next, prev = NULL; curr != NULL; prev = curr,curr = curr->next) { if(curr->processID == proID) { p = curr; if(prev == NULL) { Q->front->next = curr->next; curr = Q->front;//** } else if(curr == Q->rear) { prev->next = NULL; Q->rear = prev; // return ; } else { prev->next = curr->next; curr = prev;//** } free(p);//** } } return; } void display_queue(BlockQueue *Q) { BlockNode *p; p = Q->front->next; if(p == NULL) printf("The BlockQueue is NULL\n"); else printf("framID\tproID\tpagID\n"); while(p) { printf("%d\t%d\t%d\n", p->frameID, p->processID, p->pageID); p = p->next; } return ; }