操作系统实验四处理机调度|实验五存储管理

文末也可直接获取实验文档

实验四 处理机调度

1 实验目的

多道程序设计中,往往有若干个进程同时处于就绪状态。当就绪进程个数大于处理器数时,就必须依照某种策略来决定哪些进程优先占用处理器。本实验模拟在单处理器情况下的处理器调度,帮助学生加深理解处理机调度算法。

2 实验内容

(1)设计一个按优先数调度算法实现处理器调度的程序。
(2)设计一个按时间片轮转法实现处理器调度的程序。

3实验详细操作步骤及程序清单:

程序参考源码及结果:
1.按优先数调度算法实现处理器调度的参考程序

	#include<iostream>
	#include<string>
	usingnamespacestd;
	//-----------------------
	struct_proc//定义一个结构体表示进程的PCB
	{
		charname[32];//进程名
		struct_proc*next;//指向下一个PCB的指针
		intrun_time;//要求运行的时间
		intpriority;//优先数
		intstate;//进程状态
	};
	_proc*root;

//向就绪队列中插入进程,按照降序
	voidInsert(_proc*pr)
	{
		_proc*q=root;
		_proc*p=root->next;
		if(root->state!=0)
		{
			while(p!=NULL)
			{
				if(p->priority>pr->priority)
				{
					q=p;
					p=p->next;
				}
				else
				{
					break;
				}
			}
		}	
		pr->next=p;//插入进程结点
		q->next=pr;
		++root->state;//进程个数加1
	}

	//创建进程
	_procCreat(charname[],intpriority,intrun_time)
	{
		_procpr;
		strcpy(pr.name,name);
		pr.priority=priority;
		pr.run_time=run_time;
		pr.state=0;
		pr.next=NULL;
		returnpr;
	}

	//删除就绪队列中队首进程
	_proc*Delete()
	{
		_proc*pr=root->next;//此时pr为指向结构体的指针,指向队首进程
		root->next=root->next->next;
		--root->state;//进程个数减1
		returnpr;//返回被删除进程的地址
	}

	//对就绪队列排序,按照降序
	voidSort()
	{
		if(root->next->run_time==0)//队首进程的要执行时间为0时,从就绪队列中删除该进程
		{
			Delete();
			root->next->state=1;
		}
		else//不为0时,先删除,再根据变化后的优先级,插入到相应位置
		{
			_proc*pr=Delete();
			Insert(pr);
		}
	}
	
	voidOutPut()
	{//取队首的进程,模拟进程执行,输出执行进程名
		cout<<root->next->name<<endl;
		--root->next->priority;//动态改变优先数,优先级减1
		--root->next->run_time;//运行时间减1
	}

	voidSolve()
	{//定义根结点,指向第一个进程
		root=new_proc;
		root->state=0;//队列中的进程个数
		root->next=NULL;
		//创建几个进程,并插入就绪队列
		_procpr1=Creat("p1",2,1);
		Insert(&pr1);
		_procpr2=Creat("p2",3,5);
		Insert(&pr2);
		_procpr3=Creat("p3",1,3);
		Insert(&pr3);
		_procpr4=Creat("p4",2,4);
		Insert(&pr4);
		_procpr5=Creat("p5",4,2);
		Insert(&pr5);
		cout<<"调度序列:"<<endl;//输出
		while(root->state!=0)
		{
			OutPut();//取队首进程模拟执行一个单位时间数
			Sort();//重新排序
		}
	}
	intmain()
	{
		Solve();
		getchar();
		getchar();
		return0;
	}

输入参考代码并进行相关修改
在这里插入图片描述

2.按时间片轮转法实现处理器调度的参考程序。
#include
#include
using namespace std;
//-----------------------
struct_proc//定义一个结构体表示进程的PCB
{
charname[32];//进程名
struct_procnext;//指针
intrun_time;//要求运行的时间
intalloc_time;//已运行的时间
intstate;//进程状态
};
_proc
root;

//向就绪队列中插入进程,按照降序
voidInsert(_proc*pr)
{
	if(root->next==NULL)
	{
		root=pr;
		pr->next=pr;
		return;
	}
	pr->next=root->next;//插入
	root->next=pr;
	root=pr;		
}

//创建进程
_procCreat(charname[],intrun_time,intalloc_time)
{
	_procpr;
	strcpy(pr.name,name);//进程名取参数名
	pr.run_time=run_time;//取参数的要求运行时间
	pr.alloc_time=alloc_time;//取参数的已运行时间
	pr.state=0;
	pr.next=NULL;
	returnpr;
}

//删除就绪队列中对首进程
voidDelete()
{
	if(root->next==root)
	{//队列已为空
		root=NULL;
		return;
	}
	root->next=root->next->next;
}

//输出进程号,模拟进程执行
voidOutPut()
{
	cout<<root->next->name<<endl;//输出
	++root->next->alloc_time;//已运行时间加1
}
voidSolve()
{//定义根结点,指向下一个运行的进程
	root=new_proc;
	root->state=0;//队列中的进程个数
	root->next=NULL;
	//创建几个进程,并插入就绪队列
	_procpr1=Creat("Q1",2,1);
	Insert(&pr1);
	_procpr2=Creat("Q2",3,0);
	Insert(&pr2);
	_procpr3=Creat("Q3",1,0);
	Insert(&pr3);
	_procpr4=Creat("Q4",2,0);
	Insert(&pr4);
	_procpr5=Creat("Q5",4,0);
	Insert(&pr5);
	cout<<"调度序列:"<<endl;//输出
	while(root!=NULL)//一直循环遍历
	{
		OutPut();//执行root指向的进程
		if(root->next->alloc_time==root->next->run_time)//执行完毕
		{
			Delete();//从就绪队列中删除该进程
			continue;
		}
		root=root->next;//root指针后移,执行下一个进程做准备
	}
}
intmain()
{
	Solve();
	getchar();
	return0;
}

输入参考代码并进行相关修改
在这里插入图片描述

4相关问题分析

时间片轮转算法中,系统将所有的就绪程序按先来先服务的原则排成一个队列,每次调度时,把CPU分配给队首进程,并令其执行一个时间片(一个较小的时间单元)。轮转法是一种剥夺式调度,当执行的时间片用完时,调度程序停止该进程的执行,并将它送往就绪队列的末尾;然后,再把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片,就这样一次又一次地执行,一次又一次地等待,直到该进程的任务完成。时间片轮转调度算法特别适合于分时系统中使用。该算法的难度和关键在于选择合理的时间片。如果时间片过长,时间片轮转法就变成了先来先服务调度算法,如果时间片过小,则系统会花费大部分时间用于上下文切换。

5总结

本次实验通过对几个处理机调度算法的算法实现,对这几种调度算法的理解更加深刻了,也通过实验结果的比较,掌握了几种算法在不同进程情况下的优劣情况。同时通过一个个小实验一定程度上锻炼了我的编程能力。
当然由于对这部分知识掌握不够,对实验的理解还不够透彻还需要继续学习与总结。

实验五 存储管理

1实验目的

1.加深对操作系统存储管理的理解。
2.能够模拟页面调试算法,加深理解操作系统对内存的高度管理。

2实验内容

设计一个虚拟存储区和内存工作区,并使用下列算法计算访问命中率。
1.先进先出的算法(FIFO)
2.最近最少使用的算法(LRU)
3.最佳淘汰算法(OPT)
4.最少访问页面算法(LFU)(选做)
5.最近最不经常使用算法(NUR)(选做)
命中率=1-页面失效次数/页地址流长度(缺页率)

3实验详细操作步骤及程序清单:

<程序设计〉
本实验的程序设计基本上按照实验内容进行。即首先用srand()和rand()函数定义和产生指令序列,然后将指令序列变换成相应的页地址流,并针对不同的算法计算出相应的命中率。相关定义如下:
1.数据结构
(1)页面类型

typedefstruct{
intpn,pfn,counter,time;
}pl_type;

其中pn为页号,pfn为面框号(块号),counter为一个周期内访问该页面的次数,time为访问时间.
(2)页面控制结构

pfc_struct{
intpn,pfn;
structpfc_struct*next;
}
typedefstructpfc_structpfc_type;
pfc_typepfc[total_vp],*freepf_head,*busypf_head;
pfc_type*busypf_tail;
其中pfc[total_vp]定义用户进程虚页控制结构,
*freepf_head为空页面头的指针,
*busypf_head为忙页面头的指针,
*busypf_tail为忙页面尾的指针.

2.函数定义
(1)voidinitialize():初始化函数,给每个相关的页面赋值.
(2)voidFIFO():计算使用FIFO算法时的命中率.
(3)voidLRU():计算使用LRU算法时的命中率.
(4)voidOPT():计算使用OPT算法时的命中率.
(5)voidLFU():计算使用LFU算法时的命中率.
(6)voidNUR():计算使用NUR算法时的命中率.
3.变量定义
(1)inta[total_instruction]:指令流数据组.
(2)intpage[total_instruction]:每条指令所属的页号.
(3)intoffset[total_instruction]:每页装入10条指令后取模运算页号偏移值.
(4)inttotal_pf:用户进程的内存页面数.
(5)intdisaffect:页面失效次数.
4.程序参考源码及结果
<程序>

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#defineTRUE1
#defineFALSE0
#defineINVALID-1
//#defineNULL0
#definetotal_instruction320/*指令流长*/
#definetotal_vp32/*虚页长*/
#defineclear_period50/*清0周期*/
typedefstruct/*页面结构*/
{
intpn;//页号logicnumber
intpfn;//页面框架号physicalframenumber(物理块号)
intcounter;//计数器
inttime;//时间
}pl_type;
pl_typepl[total_vp];/*页面线性结构---指令序列需要使用地址*/
typedefstructpfc_struct/*页面控制结构,调度算法的控制结构*/
{
intpn;
intpfn;
structpfc_struct*next;
}pfc_type;
pfc_typepfc[total_vp],*freepf_head,*busypf_head,*busypf_tail;
intdiseffect,a[total_instruction];/*a[]为指令序列*/
intpage[total_instruction],offset[total_instruction];/*地址信息*/
intinitialize(int);
intFIFO(int);
intLRU(int);
intLFU(int);
intNUR(int);//notuserecently
intOPT(int);
int main()
{
ints,i,j;
srand(10*getpid());/*由于每次运行时进程号不同,故可用来作为初始化随机数队列的“种子”*/
//s=(float)319*rand()/32767/32767/2+1;/*正态分布*/
s=(float)319*rand()/32767+1;
for(i=0;i<total_instruction;i+=4)/*产生指令队列*/
{
if(s<0||s>319)
{
printf("Wheni==%d,Error,s==%d\n",i,s);
exit(0);
}
a[i]=s;/*任选一指令访问点m*/
a[i+1]=a[i]+1;/*顺序执行一条指令*/
a[i+2]=(float)a[i]*rand()/32767;/*执行前地址指令m*/
a[i+3]=a[i+2]+1;/*顺序执行一条指令*/
//s=(float)(318-a[i+2])*rand()/32767/32767/2+a[i+2]+2;
s=(float)(318-a[i+2])*rand()/32767+a[i+2]+2;
if((a[i+2]>318)||(s>319))
printf("a[%d+2],anumberwhichis:%dands==%d\n",i,a[i+2],s);

}
for(i=0;i<total_instruction;i++)/*将指令序列变换成页地址流*/
{
printf("a[%d]=%d,",i,a[i]);
page[i]=a[i]/10;
offset[i]=a[i]%10;
}
for(i=4;i<=32;i++)/*用户内存工作区从4个页面到32个页面*/
{
printf("---%2dpageframes---\n",i);
FIFO(i);
LRU(i);
LFU(i);
NUR(i);
OPT(i);

}
return0;
}
/*初始化相关数据结构total_pf表示内存的块数*/

intinitialize(inttotal_pf)
{
inti;
diseffect=0;
for(i=0;i<total_vp;i++)
{

pl[i].pfn=INVALID;/*置页面控制结构中的页号,页面为空*/
pl[i].counter=0;/*页面控制结构中的访问次数为0*/
pl[i].time=-1;/*访问的时间*/
}

for(i=0;i<total_pf-1;i++)/*建立pfc[i-1]和pfc[i]之间的链接*/
{
pfc[i].next=&pfc[i+1];
pfc[i].pfn=i;
}

pfc[total_pf-1].next=NULL;
pfc[total_pf-1].pfn=total_pf-1;
freepf_head=&pfc[0];/*空页面队列的头指针为pfc[0]*/
return0;
}
intFIFO(inttotal_pf)/*先进先出算法total_pf:用户进程的内存页面数*/
{
inti,j;
pfc_type*p;/*中间变量*/
initialize(total_pf);/*初始化相关页面控制用数据结构*/
busypf_head=busypf_tail=NULL;/*忙页面队列头,队列尾链接*/
for(i=0;i<total_instruction;i++)
{
if(pl[page[i]].pfn==INVALID)/*页面失效,即缺页*/
{
diseffect+=1;/*失效(缺页)次数*/
if(freepf_head==NULL)/*无空闲页面*/
{
p=busypf_head->next;
pl[busypf_head->pn].pfn=INVALID;
freepf_head=busypf_head;/*释放忙页面队列的第一个页面*/
freepf_head->next=NULL;/*表明还是缺页*/
busypf_head=p;
}
p=freepf_head->next;
freepf_head->pn=page[i];
pl[page[i]].pfn=freepf_head->pfn;
freepf_head->next=NULL;/*使busy的尾为null*/
if(busypf_tail==NULL)
{
busypf_tail=busypf_head=freepf_head;
}
else
{
busypf_tail->next=freepf_head;
busypf_tail=freepf_head;
}
freepf_head=p;
}
}
printf("FIFO:%6.4f\n",1-(float)diseffect/320);
return0;
}
intLRU(inttotal_pf)/*最近最久未使用算法leastrecentlyused*/
{
intmin,minj,i,j,present_time;/*minj为最小值下标*/
initialize(total_pf);
present_time=0;
for(i=0;i<total_instruction;i++)
{
if(pl[page[i]].pfn==INVALID)/*页面失效*/
{
diseffect++;
if(freepf_head==NULL)/*无空闲页面*/
{
min=32767;/*设置最大值*/
for(j=0;j<total_vp;j++)/*找出time的最小值*/
{
if(min>pl[j].time&&pl[j].pfn!=INVALID)
{
min=pl[j].time;
minj=j;
}
}
freepf_head=&pfc[pl[minj].pfn];//腾出一个单元
pl[minj].pfn=INVALID;
pl[minj].time=0;
freepf_head->next=NULL;
}
pl[page[i]].pfn=freepf_head->pfn;//有空闲页面,改为有效
pl[page[i]].time=present_time;
freepf_head=freepf_head->next;//减少一个free页面
}
else
{
pl[page[i]].time=present_time;//命中则增加该单元的访问次数
present_time++;
}
}
printf("LRU:%6.4f\n",1-(float)diseffect/320);
return0;
}
intNUR(inttotal_pf)/*最近未使用算法NotUsedrecentlycount表示*/
{
inti,j,dp,cont_flag,old_dp;
pfc_type*t;
initialize(total_pf);
dp=0;
for(i=0;i<total_instruction;i++)
{
if(pl[page[i]].pfn==INVALID)/*页面失效(缺页)*/
{
diseffect++;
if(freepf_head==NULL)/*无空闲页面*/
{
cont_flag=TRUE;
old_dp=dp;
while(cont_flag)
{
if(pl[dp].counter==0&&pl[dp].pfn!=INVALID)
cont_flag=FALSE;
else
{
dp++;
if(dp==total_vp)
dp=0;
if(dp==old_dp)
for(j=0;j<total_vp;j++)
pl[j].counter=0;
}
}
freepf_head=&pfc[pl[dp].pfn];
pl[dp].pfn=INVALID;
freepf_head->next=NULL;
}

pl[page[i]].pfn=freepf_head->pfn;

freepf_head->pn=page[i];

freepf_head=freepf_head->next;
}
else
pl[page[i]].counter=1;
if(i%clear_period==0)
for(j=0;j<total_vp;j++)
pl[j].counter=0;
}
printf("NUR:%6.4f\n",1-(float)diseffect/320);
return0;
}

intOPT(inttotal_pf)/*最佳置换算法*/
{
inti,j,max,maxpage,d,dist[total_vp];
pfc_type*t;
initialize(total_pf);
for(i=0;i<total_instruction;i++)
{
if(pl[page[i]].pfn==INVALID)/*页面失效*/
{
diseffect++;
if(freepf_head==NULL)/*无空闲页面*/
{
for(j=0;j<total_vp;j++)
{
if(pl[j].pfn!=INVALID)
dist[j]=32767;
else
dist[j]=0;
}
for(j=0;j<total_vp;j++)
{
if((pl[j].pfn!=INVALID)&&(dist[j]==32767))
{
dist[j]=j;
}
}
max=0;
for(j=0;j<total_vp;j++)
if(max<dist[j])
{
max=dist[j];
maxpage=j;
}
freepf_head=&pfc[pl[maxpage].pfn];
freepf_head->next=NULL;
pl[maxpage].pfn=INVALID;
}
pl[page[i]].pfn=freepf_head->pfn;
freepf_head=freepf_head->next;
}
}
printf("OPT:%6.4f\n",1-(float)diseffect/320);
return0;
}
/*该算法时根据已知的预测未知的,leastfrequencyUsed是最不经常使用置换法*/
intLFU(inttotal_pf)
{
inti,j,min,minpage;
pfc_type*t;
initialize(total_pf);
for(i=0;i<total_instruction;i++)
{
if(pl[page[i]].pfn==INVALID)/*页面失效*/
{
diseffect++;
if(freepf_head==NULL)/*无空闲页面*/
{
min=32767;
/*获取counter的使用用频率最小的内存*/
for(j=0;j<total_vp;j++)
{
if(min>pl[j].counter&&pl[j].pfn!=INVALID)
{
min=pl[j].counter;
minpage=j;
}
}
freepf_head=&pfc[pl[minpage].pfn];
pl[minpage].pfn=INVALID;
pl[minpage].counter=0;
freepf_head->next=NULL;
}
pl[page[i]].pfn=freepf_head->pfn;//有空闲页面,改为有效
pl[page[i]].counter++;
freepf_head=freepf_head->next;//减少一个free页面
}
else
{
pl[page[i]].counter;
pl[page[i]].counter=pl[page[i]].counter+1;
}
}
printf("LFU:%6.4f\n",1-(float)diseffect/320);
return0;
}

输入参考代码并进行相关修改
在这里插入图片描述

结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4相关问题分析及思考

<分析>
从上述结果可知,在内存页面数较少(45页)时,五种算法的命中率差别不大,都是30%左右。在内存页面为718个页面之间时,5种算法的访内命中率大致在35%60%之间变化。但是,FIFO算法与OPT算法之间的差别一般在610个百分点左右。在内存页面为25~32个页面时,由于用户进程的所有指令基本上都已装入内存,使命中率增加,从而算法之间的差别不大。比较上述5种算法,以OPT算法的命中率最高,NUR算法次之,再就是LFU算法和LRU算法,其次是FIFO算法。就本问题,在15页之前,FIFO的命中率比LRU的高。

5总结

在这个实验过程中,加深了我对操作系统存储管理的理解,能够模拟页面调试算法,并且加深理解操作系统对内存的高度管理。由于对知识掌握不足,阅读代码及查阅相关资料后有了些浅显理解,但还需要继续学习,深入学习。

关注公众号:Time木
回复:操作系统实验
可获得相关代码,数据,文档
回复:操作系统试卷
可获取操作系统试卷整理
更多大学课业实验实训可关注公众号回复相关关键词
学艺不精,若有错误还望指点

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Time木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值