动态分区存储管理中地址转换.docx

上传人:b****8 文档编号:9707467 上传时间:2023-02-05 格式:DOCX 页数:26 大小:257.39KB
下载 相关 举报
动态分区存储管理中地址转换.docx_第1页
第1页 / 共26页
动态分区存储管理中地址转换.docx_第2页
第2页 / 共26页
动态分区存储管理中地址转换.docx_第3页
第3页 / 共26页
动态分区存储管理中地址转换.docx_第4页
第4页 / 共26页
动态分区存储管理中地址转换.docx_第5页
第5页 / 共26页
点击查看更多>>
下载资源
资源描述

动态分区存储管理中地址转换.docx

《动态分区存储管理中地址转换.docx》由会员分享,可在线阅读,更多相关《动态分区存储管理中地址转换.docx(26页珍藏版)》请在冰豆网上搜索。

动态分区存储管理中地址转换.docx

动态分区存储管理中地址转换

学号:

课程设计

动态分区存储管理中地址转换

计算机科学与技术学院

指导教师

吴利军

2013年1月16日

课程设计任务书

学生姓名:

指导教师:

吴利军工作单位:

计算机科学与技术学院

题目:

模拟设计动态分区存储管理中地址转换

初始条件:

1•预备内容:

阅读操作系统的内存管理章节内容,理解动态分区的思想,并体会动态分区分配主存的过程。

2•实践准备:

掌握一种计算机高级语言的使用。

要求完成的主要任务:

(包括课程设计工作量及其技术要求,以及说明书撰写

等具体要求)

1•要求首先采用动态分区方案,用最先适用算法对作业实施内存分配,然后把作业地址空间的某一逻辑地址转换成相应的物理地址。

能够处理以下的情形:

输入某一逻辑地址,程序能判断地址的合法性,如果合法,计算并输出相应的物理地址。

如果不能计算出相应的物理地址,说明原因。

2•设计报告内容应说明:

⑴需求分析;

⑵功能设计(数据结构及模块说明);

⑶开发平台及源程序的主要部分;

⑷测试用例,运行结果与运行情况分析;

⑸自我评价与总结:

i)你认为你完成的设计哪些地方做得比较好或比较出色;

ii)什么地方做得不太好,以后如何改正;

iii)从本设计得到的收获(在编写,调试,执行过程中的经验和教训);

iv)完成本题是否有其他方法(如果有,简要说明该方法);

时间安排:

设计安排一周:

周1、周2:

完成程序分析及设计。

周2、周3:

完成程序调试及测试。

周4、周5:

验收、撰写课程设计报告。

(注意事项:

严禁抄袭,一旦发现,一律按0分记)

指导教师签名:

系主任(或责任教师)签名:

1.需求分析

1.1判断内存地址合法性

每个进程都有自己独立的进程空间,如果一个进程在运行时所产生的地址在其地址空间

之外,则会发生地址越界。

当程序要访问某个内存单元时,先由硬件检查是否允许,如果允许则执行,否则产生地址越界中断,再由操作系统进行相应处理。

在本次课程设计中,需要

通过程序模拟硬件的判断过程,对用户输入的内存地址判断合法性,不合法时需要给出原因。

1.2模拟动态分区存储管理

动态分区和静态分区存储管理的不同之处在于,不事先将内存划分成一块块的分区,而

是在作业进入内存时,根据作业的大小动态的建立分区,并使分区的大小正好适应作业的需

要。

因此系统中分区的大小是可变的,分区数目也是可变的。

在系统初启时刻,除了操作系

统常驻内存外只有一个大的分区,随着进程的执行,会出现一系列的分配和释放。

比如在某

一时刻,一进程执行结束并释放内存之后,管理程序又要为另一个进程分配内存。

如果分配

的空闲区比所要求的大,则管理程序将该空闲区分为两个部分,其中一部分成为已分配区而

另一部分成为一个新的小空闲区。

在本次课程设计中需要通过程序模拟该过程的执行。

1.3实现将逻辑地址映射成为物理地址

进程执行时,cpu要将需要访问的逻辑地址映射成物理地址,即动态重定位。

重定位机构需要用到基址寄存器(BR)和虚拟地址寄存器(VR),物理地址(MA)=装入内存首地址(BR)+逻辑地址(VR)。

1.4实现内存管理中的最先适应算法

最先适应算法的算法过程是:

空闲分区(链)按地址递增的次序排列。

在进行内存分配时,从空闲分区表/链首开始顺序查找,直到找到第一个满足其大小要求的空闲分区为止。

然后按照作业大小,从该分区中划出一块内存空间分配给请求者,余下的空闲分区仍留在空

闲分区表/链中。

2.功能设计

2.1数据结构

系统用来管理空闲分区的数据结构有两种,空闲分区表和空闲分区链,在本次的课程设

计中,我采用的是空闲分区链。

此外,对于每一个模拟进程,采用表结构来标志和操作。

2.1.1空闲分区链

每一个空闲分区链节点都包括分区号,分区的起始地址,分区大小和指向下一个节点的

指针,数据结构用C++描述如下所示:

//定义空闲分区链表节点的数据结构

structNode

{

intSA;//空闲分区在内存的首地址

intID;//空闲分区号

intsize;//空闲分区占用内存的大小

Node*next;//指向下个节点的指针

};

对此空闲分区链表初始化如下所示:

Node*head,*p1;

voidlnit()

{

p1=newNode;

p1->ID=1;

p1->SA=0;

〃初始化内存的大小为1000

p1->size=1000;

p1->next=NULL;

head=p1;

}

其他的空闲分区节点尾接上链表时就将构成空闲分区链。

2.1.2进程的结构

在这里用户负责每一个进程的添加和删除,因此需要用户为每个进程指派一个唯一的进

程号。

为了方便地对进程进行操作,这里使用到表的结构。

进程包含进程号,起始地址和大小,其数据结构用C++描述如下所示:

//定义模拟进程的数据结构

structProcess

{

intSA;//模拟进程在内存的首地址

intPID;//模拟进程的进程号

intsize;//模拟进程占用内存的大小

};

同时建立一个进程数组,初始化大小为20:

ProcessPro[20];

这里便可以通过用户指派的进程号作为数组下标来找到进程,结构体和数组一起构成了

一张进程表。

2.2模块说明2.2.1重定位和越界判断

当程序装入内存时,其逻辑地址会在这个物理空间展开,转换成相应的物理地址。

这个

过程被称为地址映射或地址重定位。

在动态分区管理中,物理地址=程序首址+逻辑地址。

操作系统通过上界/下界寄存器或者基址/限长寄存器来进行界限保护。

在本课程设计

中,以Process结构体中的SA模拟基址寄存器,size模拟限长寄存器,而且从地址0开始

编址,因此对Process"],其合理的逻辑地址范围为[0,Process[i].size]。

相应的函数实现如下

所示:

//重定位

voidrelocate。

{

intpid,location;

cout<<"----请输入要重定位的进程的进程号:

";

cin>>pid;

if(Pro[pid-1].PID==0)

cout<<"----此进程不存在,请重试!

"<

else

{

cout<<"----请输入该进程的逻辑地址:

";

cin>>location;

if(location>Pro[pid-1].size-1)

cout<<"----地址越界,请重试!

"<

else

{

cout<<"----映射物理地址为:

"<

2.2.2最先适应算法的实现

系统初启时,内存中除了操作系统常驻的部分外只有一个大的空闲分区,在这里我将其

初始化为1000。

随着进程的添加和删除过程的交替进行,原来完整的大分区必定将被分割成许多物理空间上并不连续的小分区,为了管理内存,每一个进行进程添加删除操作后需要

重构空闲分区链。

这样的话,每当有新的进程需要使用内存,程序首先按地址顺序遍历分区

链,直到找到第一个满足其大小的分区为止。

如果遍历结束仍然没找到合适的分区,则说明

请求的内存过大,系统无法满足新进程的请求,只能等待其他进程释放内存后再尝试。

创建进程以及最先适应算法的函数实现如下所示:

//创建进程

voidcreate。

{

intpid,size;

cout<<"----输入进程的进程号([1,20]):

";

cin>>pid;

cout<<"----输入进程占用内存大小([1,999]):

";

cin>>size;

if(pid>=1&&pid<=20&&size>=1&&size<=999)

{

//数组索引位置和用户输入相差1

intrpid=pid-1;

if(Pro[rpid].PID==0)

{//PID为零说明该数组项为占用,即PID有效

Pro[rpid].PID=pid;

Pro[rpid].size=size;

〃开始通过首次适应算法找到空闲分区

firstFit(head,rpid);

}else{

cout<<"----该PID已被占用,请重试!

"<

}

}else{

cout<<"----输入参数有误,请重试!

"<

}

}

//首次适应算法

voidfirstFit(Node*node,intindex)

{

if(node)

{

if(node->size>Pro[index].size)

{

node->size-=Pro[index].size;Pro[index].SA=node->SA;

node->SA+=Pro[index].size;cout<<"----成功添加"<

}

elseif(node->size==Pro[index].size){

head=NULL;

Pro[index].SA=node->SA;

delete(node);

cout<<"----成功添加"<

}

elseif(node->next)

{//第一个节点不满足,且不止一个节点

Node*p=node;

node=node->next;

while(node)

{

if(node->size>Pro[index].size)

{

node->size-=Pro[index].size;

Pro[index].SA=node->SA;

node->SA+=Pro[index].size;

break;

}

elseif(node->size==Pro[index].size)

{

p->next=node->next;

Pro[index].SA=node->SA;

delete(node);break;

}

p=node;

node=node->next;

}

if(!

node)

{

"<

Pro[index].PID=0;

cout<<"----该进程请求的内存过大,找不到适合的空闲分区

}else{

cout<<"----成功添加"<

}

}

else

{

Pro[index].PID=0;

cout<<"----该进程请求的内存过大,找不到适合的空闲分区"<

}

}else{

Pro[index].PID=0;

cout<<"----内存已满"<

}

}

2.2.3进程的删除

进程删除涉及到两个方面,第一是从进程表中删除进程,第二是释放进程所占用的内存

空间。

对于删除进程而言,我定义了一个进程数组,可以用户输入的进程号来作为数组的下

标找到对应的进程。

而且在定义数组时,每一项的进程号,起始地址和大小初始都为o,而

指定的进程号为[1,100],所以删除进程只要直接将对应进程的进程号置为0即可。

对于释放

进程所占用的内存空间,需要分几种情况,第一是删除的进程起始地址在分区链第一个节点

起始地址之前,此时只需要以“new”的方式生成一个新的节点,并使原空闲分区链的链头

指向此节点,同时此节点指向原链头指向的节点即可;第二是所删除进程的起始地址在节点

起始地址之间时需要将新节点插入到链表节点之间;第三是进程的起始地址在所有节点之后,这时就应该把新节点放到链表的末尾。

除此之外还需要考虑到链表为空或者只有一个节

点的特殊情况,并且如果出现新节点和原节点发生相连的情况还需要对节点进行合并,即地

址较小的分区节点的size变为二者之后,且指向下个节点的指针现在需要指向地址较大的

分区节点的指针所指节点。

以下是函数实现:

〃删除进程

voidpdelete(Node*node)

{

intpid;

cout<<"----请输入要删除进程的进程号:

";

cin>>pid;

if(Pro[pid-1].PID==0)

cout<<"----此进程不存在,请重试!

"<

else

{

intsa=Pro[pid-1].SA;

intsize=Pro[pid-1].size;

Node*n=newNode;

n->SA=sa;n->size=size;n->next=NULL;if(node)

{//初始的空闲分区表不为空

if(saSA)

{//进程在所有空闲分区之前

head=n;

n->next=node;

if(sa+size==node->SA)

{//进程之后连接一个空闲分区

n->size=size+node->size;

n->next=node->next;

delete(node);

}

}else{

for(;node->next!

=NULL;node=node->next);//找至U最后一个节点if(node->SA

{//进程在所有空闲分区之后

if(node->SA+node->size==sa)

{//进程之前连接一个空闲分区

node->size+=size;

delete(n);

}else{

node->next=n;

}

}else{

〃进程在空闲分区中间

node=head;

Node*p=node;

node=node->next;

while(node)

{

if(p->SASA)break;//跳出循环

p=node;

node=node->next;

}

〃不存在找不到的情况,插入

p->next=n;

n->next=node;

if(p->SA+p->size==sa&&sa+size==node->SA)

{//进程和空闲分区上下相连

p->next=node->next;

p->size=p->size+size+node->size;

delete(n);

delete(node);

}

elseif(p->SA+p->size!

=sa&&sa+size==node->SA){//只和下个空闲分区相连

n->next=node->next;

n->size+=node->size;

delete(node);

}

elseif(p->SA+p->size==sa&&sa+size!

=node->SA){//只和上个空闲分区相连

p->next=node;

p->size+=size;delete(n);

}

}

}

}else{

head=n;

}

//释放进程,只要操作PID就行,后来的值会覆盖原来的值

Pro[pid-1].PID=0;

cout<<"----成功删除"<

}

}

3.开发平台及说明

3.1开发平台

本次课程设计采用的开发平台为VS2010,是使用C++语言进行编写的。

3.2系统运行流程

4.测试用例

4.1添加进程

从初始状态开始添加新的进程:

退出〉

210

指令

指令

黑的进程号<[1.20]>:

5

呈占用丙荐大小<【直理汕切809

青求的内存过;L找不到适合的空闲分区

空赫篋链:

二霉帝程,2~社4一查看运行中雉程,其他整

可以看到,当添加新的进程时,空闲分区链会发生相应的改变,此时空闲分区链中仍然

只存在一个节点,且分区从地址750开始,大小为250。

当输入重复的PID时会报重复错误,当进程需要的内存过大时,在空闲分区链遍历一遍没找到适合的分区后则会报请求内存过大之错。

如果输入进程号不在[1,20]区间内或者占用内存大小不在[1,999]之间则会报参数错误。

扌旨令;1

——输入进程的进程号<[1,201>:

21

—一输入进程占用内注犬小<【「9刘]〉:

2

——端入参薮有误,诸重试!

扌旨令,1

■——対入进矍的进程号<n,20n=6

一-嶺△世程占用内存大小250

咸功添加

指令;0

——按照代分区号,起始地址,大小尹格式打印指令:

当所有内存空间都被分配完,如上所示,最后请求了一个内存占用为250的进程,占用了最后一块空闲分区,再打印空闲分区链时没有显示,说明空闲分区链已经为空,不存在节点。

4.2打印运行中程序

齢=4

--一正在运行的进程如下所示

12345

齧令’

指令4可以将正在运行的进程的进程号打印出来。

4.3重定位

指令3用来实现重定位功能模块,用户输入需要进行重定位的进程的进程号,然后输入

在进程中的逻辑地址。

如果进程号不存在会报告不存在的错误。

观察到进程1的起始地址为0,所以当输入的逻辑地址为100时,物理地址为0+100=100;

进程2起始地址为200,当输入的逻辑地址为30时,映射到物理地址即为200+30=230;进程5的起始地址为600,大小为150,当输入逻辑地址为300>149(逻辑地址从0开始)时,会报告地址越界;当输入逻辑地址为10时,映射到物理地址则为600+10=610。

4.4删除进程

---feesK<分区号,起始地址.大小严格式打印

<1.0,1000>

添加完所有进程后,内存中共有6个进程,无剩余内存。

具体情况如下所示:

进程号

起始地址

大小

1

0

200

2

200

90

3

290

100

4

390

210

5

600

150

6

750

250

此时空闲进程链为空。

删除进程1后,由上表可知空闲分区链中添加了一个节点(从上

到下依次为空闲分区号,起始地址,大小和指向下一个空闲分区的指针,下同):

1

0

200

null

删除进程5后,空闲分区链在尾部添加一个新的节点:

1

0

/6。

0

200/

’150

/

null

删除进程3后,在链中的1、2号节点中插入一个新的节点:

1

0

沟0

200丿

卡100/

*150

/

/

null

删除进程6后,由于进程6的起始地址为750,所以进程6的空间会和节点3的空间进行合并,最终形成一个新的节点:

1

0

y^90

/600

200/

f100/

卡400

/

/

null

删除进程2后,由于进程2的起始地址为200且大小为90,所以删除此进程后,分区1、进程所占内存以及分区2会合并:

1

/

0

^00

390/

f400

/

null

删除进程4后,同上,所有分区和进程所占空间会合并:

1

0

1000

null

实验结果和分析到的实验预期一致,在一定程度上验证了程序的正确性。

4.5最先适应算法

■D:

\ProgramData\viIstudio2010\Projects\0&\0SKeshe\Debug\OSKeshe.exe

指令七0

——按照化分区号,起始地址,犬小严格式打印

(l,fi,200X2,290,100J<3p600,iS0>聶令:

4

一一正在运行的进程如下所示

246

扌旨■1

输入进程的进程号

——做入进程占用丙荐大小灯596110

成功添加指令:

B一一按鹅U分区号,起始地址*大小疋格式打印

(1,110.90X2,290.100X3,600,1505

扌旨.i

输入进程的进程号^C1*201>:

3「——聲入进程占用内存大小心/99":

50——成功添加

指令’©

—-按照喰分区号,起始地址,大小严格式打印

<1,160,40X2,290,100X3,600,150>

扌旨1

——-蠶入进程的进程#<(V201>=5碱入进程占用内存大小<9B

成功添加

指令:

0

一一按嵋化分区号,起始地址,大小疋格式打印

(1,160,40X2,380.105<3,600,150>

卡旨令’4

一一正在运行的进程如下所示

---^入遁呈的进程号£[1.20]>;7

——魅进程占用内存大小<[1.99?

1>=50

r—成功添加

P旨令乂0

卜一按照化分区号,起始地址,大小严格式打印

<1,160,40X2,38iB,10X3,650,100>

卜一正在运行的进程如下所示

12345

67

初始状态是系统中存在三个进程,分别为2、4、6,占用大小为90、210和250,空闲

分区链为(1,0,200)(2,290,100)(3,600,150)。

当加入大小为110的进程1时,通过最先适应算法得出,应该从分区1中分配110大小的内存给进程1,此时空闲分区链变为

(1,110,90)(2,290,100)(3,600,150);当加入大小为50的进程3时,应该从分区1中分配50大

小的内存给进程3,此时空闲分区链变为(1,160,40)(2,290,100)(3,600,150);当加入大小为90

的进程5时,应该从分区2中分配90,此时分区链更新为(1,160,40)(2,380,10)(3,600,150);当加入大小为50的进程7时,应该从分区3中分配50,此时分区链更新为

(1,110,90)(2,380,10)(3,650,100)。

可以看到,程序的运行结果和预期一致,可以说明程序的正

确性。

5.自我评价和总结

本学期学习了操作系统之后终于对我们每天都要使用到的计算机有了一个基本系统的了解,我看到计算机管理设备、文件、内存等都有着一套合理而且非常巧妙的办法,在学习的过程中不禁要去感叹前人聪颖的智慧和不懈的尝试。

操作系统对于内存的管理有很多种方

法,从最初的静态分区到动态分区,再到页式管理、段式管理、段页式管理,每一次进步都是提高了内存的利用率和便于内存信息的共享。

而操作系统动态分区式管理内存可以使用两

种数据结构来进行,空闲分区表或者空闲分区链,为了便于进行节点的插入删除操作我选择了代码相对好写的空闲分区链来模拟整个过程。

但是相对于空闲分区表而言,查找只能顺序

进行,相对较繁琐。

通过这次的课程设计,我对操作系统管理内存的办法来了个系统的复习,自己也动手实

现了动态分区中的最先适应算法。

总体来说,程序的运行没有什么问题,基本可以很好的模

拟动态分区。

虽说原理上并不复杂,但也是一个有益的尝试,对于进一步的学习操作

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 研究生入学考试

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1