操作系统 内存管理

简介: 操作系统 内存管理

一、实验目的
1、了解虚拟存储技术的特点,掌握虚拟存储请求页式存储管理中几种基本页面置换算法的基本思想和实现过程,并比较它们的效率。
2、了解程序设计技术和内存泄露的原因

二、实验内容
1、模拟实现请求页式存储管理的几种基本页面置换算法
(1)最佳淘汰算法(OPT)
(2)先进先出的算法(FIFO)
(3)最近最久未使用算法(LRU))
思考题:
1、从几种算法的命中率看,哪个算法最高?哪个算法最低?对每个页面的执行结果进行分析。
2、OPT算法在执行过程中可能会发生错误,为什么?

三、实验原理
1、虚拟存储系统
UNIX中,为了提高内存利用率,提供了内外存进程对换机制;内存空间的分配和回收均以页为单位进行;一个进程只需将其一部分(段或页)调入内存便可运行;还支持请求调页的存储管理方式。
当进程在运行中需要访问某部分程序和数据时,发现其所在页面不在内存,就立即提出请求(向CPU发出缺中断),由系统将其所需页面调入内存。这种页面调入方式叫请求调页。
为实现请求调页,核心配置了四种数据结构:页表、页框号、访问位、修改位、有效位、保护位等。
2、页面置换算法
当CPU接收到缺页中断信号,中断处理程序先保存现场,分析中断原因,转入缺页中断处理程序。该程序通过查找页表,得到该页所在外存的物理块号。如果此时内存未满,能容纳新页,则启动磁盘I/O将所缺之页调入内存,然后修改页表。如果内存已满,则须按某种置换算法从内存中选出一页准备换出,是否重新写盘由页表的修改位决定,然后将缺页调入,修改页表。利用修改后的页表,去形成所要访问数据的物理地址,再去访问内存数据。整个页面的调入过程对用户是透明的。
(1)最佳淘汰算法(OPT):选择永不使用或在未来最长时间内不再被访问的页面予以替换。
(2)先进先出的算法(FIFO):选择在内存中驻留时间最久的页面予以替换。
(3)最近最久未使用算法(LRU):选择过去最长时间未被访问的页面予以替换。
3、首先用srand( )和rand( )函数定义和产生指令序列,然后将指令序列变换成相应的页地址流,并针对不同的算法计算出相应的命中率。
(1)通过随机数产生一个指令序列,共320条指令。指令的地址按下述原则生成:
A:50%的指令是顺序执行的
B:25%的指令是均匀分布在前地址部分
C:25%的指令是均匀分布在后地址部分
具体的实施方法是:
A:在[0,319]的指令地址之间随机选取一起点m
B:顺序执行一条指令,即执行地址为m+1的指令
C:在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为m’
D:顺序执行一条指令,其地址为m’+1
E:在后地址[m’+2,319]中随机选取一条指令并执行
F:重复步骤A-E,直到320次指令
(2)将指令序列变换为页地址流
设:页面大小为1K;
用户内存容量4页到32页;
用户虚存容量为32K。
在用户虚存中,按每K存放10条指令排列虚存地址,即320条指令在虚存中的存放方式为:
第 0 条-第 9 条指令为第0页(对应虚存地址为[0,9])
第10条-第19条指令为第1页(对应虚存地址为[10,19])
………………………………
第310条-第319条指令为第31页(对应虚存地址为[310,319])
按以上方式,用户指令可组成32页。

四、实验中用到的系统调用函数(包括实验原理中介绍的和自己采用的),自己采用的系统调用函数要按照指导书中的格式说明进行介绍。
因为是模拟程序,可以不使用系统调用函数。

五、实验步骤
1、 画出每个页面置换算法流程图;
FIFO:
在这里插入图片描述

LRU:
在这里插入图片描述

OPT:
在这里插入图片描述

2、 对算法所用的数据结构进行说明;

int instruction[instructionNum];一维数组,用于存放指令序列
int page[pageNum];一维数组,用于存放指令序列对应的页面地址流
int phyMemory[blockNum];一维数组,用于存放地址块中的页面
int flag[instructionNum];//一维数组,用于存放最久未被使用的页面
3、 测试数据随机产生。不可手工输入;
4、 编写程序并调试;
5、 多次测试程序,截屏输出实验结果;
6、 根据实验结果与理论课讲述的原理进行实验分析。

六、实验数据及源代码(学生必须提交自己设计的程序源代码,并有注释,源代码电子版也一并提交),包括思考题的程序。

include<stdio.h>

include<stdlib.h>

include<time.h>

define instructionNum 320 //总指令数

define MAX 2000

define pageNum 320

int instruction[instructionNum]; //指令序列
int blockNum;//可用内存块数
float count; //记录中断次数
int stayTime;//在内存中的停留时间
double FIFOmiss;//FIFO的缺失率
double LRUmiss;//LRU的缺失率
double OPTmiss;//OPT的缺失率
int *page=NULL;//指令序列对应的页面
int *phyMemory=NULL;//物理内存块
int flag[instructionNum]={0};//存放最久未被使用的页面
int max=-1, maxflag=0; //标记替换物理块进程下标

void initInstruction();//创建指令序列
void instructpage();//将指令序列变为页地址流
void initMemory(int n);//初始化内存块
int isExist(int target);//检测需要页面是否已经在内存中
int searchMemory();//寻找空闲内存块
void showPage();//输出页面
void delete();//释放内存
void FIFO();//FIFO算法
void LRU();//LRU算法
void OPT();//OPT算法
void main(){

initInstruction();//初始化指令序列
instructpage();//将指令序列转化为对应的页面地址流
srand((unsigned int)time(0));
int num=(rand()%29)+4;//内存块数随机生成,范围为4-32块
initMemory(num);//初始化内存块
printf("++++++++++++++++++++++++++++++++++++\n");
printf("先进先出的算法(FIFO):\n");
FIFO();//FIFO算法
initMemory(num);//清空内存块
printf("++++++++++++++++++++++++++++++++++++\n");
printf("最近最久未使用算法(LRU):\n");
LRU();//LRU算法
initMemory(num);//清空内存块
printf("++++++++++++++++++++++++++++++++++++\n");
printf("最近最久未使用算法(OPT):\n");
OPT();//OPT算法
printf("内存可用块数为%d\n",num);//输出内存块
printf("FIFO算法缺页率为%f\n",FIFOmiss);//FIFO算法的缺失率
printf("LRU算法缺页率为%f\n",LRUmiss);//LRU算法的缺失率
printf("OPT算法缺页率为%f\n",OPTmiss);//OPT算法的缺失率
delete();//释放内存

}

//创建指令序列
void initInstruction(){

srand((unsigned int)time(0));
for(int i=0;i<instructionNum;i+=4){
    int temp=0;
    temp=rand()%instructionNum;  //在[0,319]的指令地址之间随即选取一起点m

if(temp==319) break; //确保m点不能取319

    instruction[i]=temp+1;  //顺序执行一条指令,即执行地址为m+1的指令
    temp=rand()%(temp+2);  /*在前地址[0,m+1]中随机选取一条指令,该指令的地址为m’*/
    instruction[i+1]=temp;  //执行地址为m'的指令
    instruction[i+2]=temp+1;  //顺序执行一条指令,其地址为m’+1
    temp=(rand()%(instructionNum-(temp+2)))+(temp+2);  /*在后地址[m’+2,319]中随机选取一条指令*/
    instruction[i+3]=temp;   //执行该指令
}    

}

//将指令序列变为页地址流
void instructpage(){

page=(int*)malloc(sizeof(int) * instructionNum);
int n=10;//每一页的指令数
for(int i=0;i<instructionNum;i++){
    page[i]= instruction[i]/n;
}
printf("指令序列对应的页面地址流为:\n");//打印输出
for(int i=0;i<instructionNum;i++){
    printf(">>%d",page[i]);
}
printf("\n");

}

//初始化内存快
void initMemory(int n){//n为传进来的内存块数

blockNum=n;
phyMemory=(int*)malloc(sizeof(int)*blockNum);
for(int i=0;i<blockNum;i++){
    phyMemory[i]=-1;
}

}

//寻找空闲内存块
int searchMemory(){

for(int i=0;i<blockNum;i++){
    if(phyMemory[i]==-1){
        return i;
    }
}
return -1;

}

//检测需要页面是否已经在内存中
int isExist(int target){//target为需求页面

for(int i=0;i<blockNum;i++){
    if(phyMemory[i]==target){
        return i;
    }
}
return -1;

}

//输出页面
void showPage(){

printf("当前所在内存块号:");
for(int i=0;i<blockNum;i++){
    if(phyMemory[i]!=-1){
        printf("%d   ",phyMemory[i]);
    }
}
printf("\n\n");

}

//释放内存
void delete(){

free(page);
free(phyMemory);
printf("程序运行完毕,内存已释放!\n");

}

//先进先出算法FIFO
//选择在内存中驻留时间最久的页面予以替换
void FIFO(){//n为物理快数

count=0;//重置中断次数
int exit;//空闲内存块号
int same=-1;//内存中是否存在需求页面的标志
for(int i=0;i<pageNum;i++){
    same=isExist(page[i]);//先判断内存中是否存在该页
    //找flag值最大的
    for(int j = 0;j<blockNum;j++){
        if(flag[j]>maxflag){
            maxflag=flag[j];
            max=j;
        }
    }
    if(same!=-1){//存在该页
        printf("当前请求页号%d已在内存中!\n\n",page[i]);
        for(int j=0;j<blockNum;j++){
                flag[j]++;
        }
    }
    else{//产生缺页中断
        count++;//中断次数加1
        exit=searchMemory();//检查内存中有无空闲内存块
        if(exit!=-1){//存在空闲物理块
            phyMemory[exit]=page[i];
            showPage();
            flag[exit]=0;
            for(int j=0;j<=exit;j++){
                flag[j]++;
            }
        }
        else{//无空闲内存块,需要换出调度
            printf("当前请求页号%d请求换出页号%d\n",page[i],phyMemory[max]);
            phyMemory[max]=page[i];//调换页面
            flag[max]=0;
            for(int j=0;j<blockNum;j++){
                flag[j]++;
            }
            max=-1;
            maxflag=0;
            showPage();
        }
    }
}
FIFOmiss=(count/instructionNum)*100;
printf("FIFO算法缺页率为%f\n",FIFOmiss);

}

//最近最久未使用算法LRU
//选择过去最长时间未被访问的页面予以替换
void LRU(){//n为物理快数

count=0;//重置中断次数
int exit;//空闲内存块号
int same=-1;//内存中是否存在需求页面的标志
for(int i=0;i<pageNum;i++){
    same=isExist(page[i]);//先判断内存中是否存在该页
    //找flag值最大的
    for(int j = 0;j<blockNum;j++){
        if(flag[j]>maxflag){
            maxflag=flag[j];
            max=j;
        }
    }
    if(same!=-1){//存在该页
        printf("当前请求页号%d已在内存中!\n\n",page[i]);
        for(int j=0;j<blockNum;j++){
                flag[j]++;
        }
        flag[same]=0;//该页面刚访问过,flag置0
    }
    else{//产生缺页中断
        count++;//中断次数+1
        exit=searchMemory();//检查内存中有无空闲内存块
        if(exit!=-1){//存在空闲物理块
            phyMemory[exit]=page[i];
            showPage();
            flag[exit]=0;
            for(int j=0;j<=exit;j++){
                flag[j]++;
            }
        }
        else{//无空闲内存块,需要换出调度
            printf("当前请求页号%d请求换出页号%d\n",page[i],phyMemory[max]);
            phyMemory[max]=page[i];//调换页面
            flag[max]=0;
            for(int j=0;j<blockNum;j++){
                flag[j]++;
            }
            max=-1;
            maxflag=0;
            showPage();
        }
    }
}
LRUmiss=(count/instructionNum)*100;
printf("LRU算法缺页率为%f\n",LRUmiss);

}

//最佳淘汰算法OPT
//选择过去最长时间未被访问的页面予以替换
void OPT(){

count=0;//重置中断次数
int exit;//空闲内存块号
int same=-1;//内存中是否存在需求页面的标志
int distance[blockNum];//已在内存的页面对下次需要自己时的距离
for(int i=0;i<blockNum;i++){//重置距离
    distance[i]=0;
}
for(int i=0;i<pageNum;i++){
    same=isExist(page[i]);//先判断内存中是否存在该页
    if(same!=-1){//内存中存在该页
        printf("当前请求页号%d已在内存中!\n\n",page[i]);
    }
    else{//产生缺页中断
        //每次中断重置distance数组
        for(int i=0;i<blockNum;i++){
            distance[i]=0;
        }
        count++;//中断次数+1
        exit=searchMemory();//检查内存中有无空闲内存块
        if(exit!=-1){//存在空闲块
            phyMemory[exit]=page[i];
            showPage();
        }
        else{//无空闲内存块,需换出调度
            for(int j=0;j<blockNum;j++){
                for(int k=i;k<pageNum;k++){
                    if(phyMemory[j]!=page[k]){
                        distance[j]++;
                    }
                    else 
                        break;
                }
            }
            int temp=0;
            for(int i=0;i<blockNum;i++){
                if(distance[i]>=distance[temp])
                    temp=i;
            }
            printf("当前请求页号%d换出页号%d\n",page[i],phyMemory[temp]);
            phyMemory[temp]=page[i];//调换页面
            showPage();
                    
        }
    }    
}
OPTmiss=(count/pageNum)*100;
printf("OPT的缺页率为%f\n",OPTmiss);

}
七、实验结果分析(截屏的实验结果,与实验结果对应的实验分析)
指令序列对应的页面地址流:

FIFO算法:
(1)内存块为5:

(2)内存块为19:

LRU算法:
(1)内存块为5:

(2)内存块为19:

OPT算法:
(1)内存块为5:

(2)内存块为19:

通过多次运行测试,可以看出程序可已正确运行FIFO、LRU和OPT算法,其页面调换过程也可以清楚地看到。FIFO算法总是淘汰在内存中停留时间最长的一页,所以可以看到该算法的实用性较差,在只有5页内存块时缺页率高达百分之六十多。LRU算法是将最近最长一段时间内不曾使用的页面替换掉,但是由于在生成的页面走向中很多页面在过去用过在不久的将来又会需要,而这时该页面已经被替换出去了,所以该算法对于这个程序来说也没有表现得很好,和FIFO算法差不多。但是OPT算法相比于前面两个算法要好很多,因为该算法对将来的页面走向事先进行了遍历,并且它能知道内存中哪个页面是最久不被使用的,并且将它替换出去,所以它的缺页率很低。由于在我的程序里面内存块数我是用随机值的,而且该值的范围是4-32,所以存在很多种可能结果。除此之外,程序中还用到了很多全局变量,这些变量始终占用数据段上的空间,一定程度上造成了内存浪费,所以该程序的鲁棒性不佳。这些全局变量在任何地方都可以被更改,程序的安全性也就无法得到保证。

三种算法缺页率分析:

思考题:
1、从几种算法的命中率看,哪个算法最高?哪个算法最低?对每个页面的执行结果进行分析。
从上面的结果来看,可以看到OPT算法的缺页率最低,也就是命中率最高,命中率最低的有可能是FIFO算法,也有可能是LRU算法,两者的命中率都差不多,当内存块数较少时,大多数情况下是LRU算法比FIFO算法好一点,但是当内存块数逐渐增加时候,两者之间的差距开始逐渐增大,很少出现FIFO算法缺页率比LRU算法低的情况,所以总体来说LRU算法比FIFO算法要好。而OPT算法因为是对将来的页面进行了访问,已经预知了将来的页面走向,所以其良好性是不言而喻的,从数据中也可以看得出来,OPT算法比FIFO算法和LRU算法要好很多,但是随着内存块数的增加,其缺页率最终也只是稳定在10%左右。三者的唯一特性是命中率都随着内存块数的增加而增加,书本中介绍到FIFO页面置换算法存在Belady异常现象,也即缺页率随着内存块增加而增加,但是在我程序中并没有体现出来。因为导致Belady异常现象的页面走向实际上是很罕见的,即使我程序使用了随机生成的页面走向也没看到这种现象。
2、OPT算法在执行过程中可能会发生错误,为什么?
因为OPT最佳置换算法是选择将来不被使用,或者是在最远的将来才被访问,我的程序中因为是确定好的页面走向,所以可以对其进行预测。但是在实际中计算机要预先知道一个进程在整个运行过程中页面走向的全部情况是很困难的,所以OPT算法在执行过程中很难实现。

相关文章
|
3月前
|
存储 Linux 调度
深入理解操作系统:从进程管理到内存分配
【8月更文挑战第44天】本文将带你深入操作系统的核心,探索其背后的原理和机制。我们将从进程管理开始,理解如何创建、调度和管理进程。然后,我们将探讨内存分配,了解操作系统如何管理计算机的内存资源。最后,我们将通过一些代码示例,展示这些概念是如何在实际操作系统中实现的。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的视角和深入的理解。
|
23天前
|
C语言 开发者 内存技术
探索操作系统核心:从进程管理到内存分配
本文将深入探讨操作系统的两大核心功能——进程管理和内存分配。通过直观的代码示例,我们将了解如何在操作系统中实现这些基本功能,以及它们如何影响系统性能和稳定性。文章旨在为读者提供一个清晰的操作系统内部工作机制视角,同时强调理解和掌握这些概念对于任何软件开发人员的重要性。
|
22天前
|
Linux 调度 C语言
深入理解操作系统:从进程管理到内存优化
本文旨在为读者提供一次深入浅出的操作系统之旅,从进程管理的基本概念出发,逐步探索到内存管理的高级技巧。我们将通过实际代码示例,揭示操作系统如何高效地调度和优化资源,确保系统稳定运行。无论你是初学者还是有一定基础的开发者,这篇文章都将为你打开一扇了解操作系统深层工作原理的大门。
|
1月前
|
算法 调度 开发者
深入理解操作系统:从进程管理到内存分配
本文旨在为读者提供一个深入浅出的操作系统知识之旅,从进程管理的基础概念出发,探索内存分配的策略与技巧。我们将通过实际代码示例,揭示操作系统背后的逻辑与奥秘,帮助读者构建起对操作系统工作原理的直观理解。文章不仅涵盖理论知识,还提供实践操作的指导,使读者能够将抽象的概念转化为具体的技能。无论你是初学者还是有一定基础的开发者,都能在这篇文章中找到有价值的信息和启发。
|
1月前
|
算法 调度 C++
深入理解操作系统:从进程管理到内存分配
【10月更文挑战第42天】本文将带你进入操作系统的神秘世界,探索其核心概念和关键技术。我们将从进程管理开始,了解操作系统如何协调和管理多个程序的运行;然后,我们将深入研究内存分配,看看操作系统如何有效地分配和管理计算机的内存资源。通过这篇文章,你将获得对操作系统工作原理的深入理解,并学会如何编写高效的代码来利用这些原理。
|
2月前
|
分布式计算 算法 大数据
探索操作系统的核心:调度与内存管理机制
【10月更文挑战第11天】 本文深入探讨了操作系统中两大核心功能——调度与内存管理机制。通过分析调度算法、进程状态转换及内存分配策略等关键方面,揭示了它们如何共同维护系统性能和稳定性。旨在为读者提供对操作系统内部运作的深刻理解,同时引起对优化策略的思考。
83 5
|
2月前
|
算法
深入理解操作系统:内存管理机制的探索之旅
【10月更文挑战第2天】在数字世界的浩瀚海洋中,操作系统犹如一艘精密的航船,承载着软件与硬件的和谐共舞。本文将揭开内存管理的神秘面纱,从基础概念到高级策略,引领读者领略操作系统内存分配的智慧。通过深入浅出的解释和生动的比喻,我们一同遨游在内存的江河之中,感受操作系统如何巧妙地协调资源,确保数据的有序流动。让我们跟随内存的脚步,探索那些隐藏在每次点击、每次命令背后的奥秘。
|
2月前
|
监控 开发者
深入理解操作系统:内存管理的艺术
【10月更文挑战第2天】在数字世界的幕后,操作系统扮演着至关重要的角色。本文将深入探索操作系统的心脏——内存管理,揭示它是如何协调和管理计算机的宝贵资源。通过浅显易懂的语言和生活化的比喻,我们将一起走进内存管理的奥秘世界,了解它的原理、机制以及为何对整个系统的性能和稳定性有着不可替代的影响。无论你是技术新手还是资深开发者,这篇文章都将为你打开新的视角,让你对日常使用的设备有更深层次的认识和尊重。
|
2月前
|
缓存 算法 调度
深入浅出操作系统:从进程管理到内存优化
本文旨在为读者提供一次深入浅出的操作系统之旅。我们将从进程管理的基本概念出发,逐步深入到内存管理的复杂世界,最终探索如何通过实践技巧来优化系统性能。文章将结合理论与实践,通过代码示例,帮助读者更好地理解操作系统的核心机制及其在日常技术工作中的重要性。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开一扇通往操作系统深层次理解的大门。
|
2月前
|
存储 算法 C语言
MacOS环境-手写操作系统-17-内存管理算法实现
MacOS环境-手写操作系统-17-内存管理算法实现
43 0