《操作系统》实验二 进程调度
实验二、多任务操作系统下的进程调度
(综合性实验)
一、实验目的
进程是操作系统中最重要的概念之一,进程调度又是操作系统核心的主要内容。通过编写和调试进程调度程序,加深对进程调度工作和各种调度算法的了解。
二、实验要求
用C语言实现进程调度控制过程中的有关工作:现场保护与恢复,处理运行指针和进程调度。
三、实验内容
1、实验准备
首先进行系统初始化 , 在创建了系统进程、各用户进程之后转进程调度控制程序 , 即进入多进程并发执行的状态。
进程调度控制程序负责将 CPU 控制以从一个进程转向另一个进程。当进行控制权转接
时必须做以下几件事 :
?保护当前运行进程的现场;
?处理当前运行进程 , 将该进程插入就绪队列或其它队列;
?选一优先级最高的进程作为运行进程;
?恢复被选中进程的现场。
现场的保护与恢复是经过两个步骤完成的。其中,在保护现场时,首先将CPU现场信息送内存r区,然后再将r区内容保存到该进程pcb的reg区;恢复现场则相反,首先从被选中进程的 pcb的reg区中恢复现场信息到内存r区,然后用r区内容设置CPU各寄存器。
2、数据结构
struct pcb
{
int id; /*进程标识*/
int priority; /*优先数或轮转时间片数*/
int cputime; /*已占用CPU的时间片数*/
int alltime; /*进程所需的时间片数*/
char status; /*进程状态指针*/
*next; /*进程当前队列链接字*/
*all_q_next; /*总链队列链接字*/ }pcba[num];
3、算法
(1)优先数法
进程就绪链按优先数大小从高到低排列,链首进程首先投入运行。每过一个时间片,运行进程所需运行的时间片数减1,说明它运行了一个时间片;优先数减3,因为一个进程运行时如果在一个片中完成不了, 其优先级应降低一级。(注意,在本程序中通过对进程的PCB执行”优先数-3“和“进程所需时间片数-1”以及“进程已占用CPU的时间片数+1”来模拟进程的一次运行。而在实际的系统中,当一个进程被选中运行时, 必须恢复进程的现场,让它占有CPU运行,直到出现等待事件或运行结束)。然后比较现行程序和就绪链链首进程的优先数,如果仍是现行进程高或者相同,就让现行进程继续运行,否则,将现行运行进程按其优先数大小插入就绪链,将其状态改为“就绪态”,并调度就绪链链首进程投入运行。直到所有的进程都运行完其各自的时间片数。
(2)简单轮转法
进程就绪链按进程进入的先后排列,进程每次占用CPU的轮转时间按其重要程度登入PCB中的“优先数/轮转时间片数”
记录
混凝土 养护记录下载土方回填监理旁站记录免费下载集备记录下载集备记录下载集备记录下载
顶。每过一个时间片,运行进程占用CPU的时间片数加1,进程所需的时间片数减1(和优先数法类似,程序只是简单地通过PCB中的cputime+1,alltime-1来模拟进程的一次运行)。然后比较占用CPU的时间片数(即cputime)是否与该进程的轮转时间片数(即priority)相等,若相等则说明已到达轮转时间,应将现行运行进程排到就绪链链尾,调度链首进程占用CPU,并改变它们的进程状态,直到所有的进程完成各自的时间片(alltime=0)。
4.程序构成
由六个子程序构成,各子程序及其完成的工作为:
(1)init子程序
按照给定的进程调度算法(优先数法或简单轮转法)随机地产生5个进程控制块的内容,并形成进程控制块链。置链首进程为初始运行进程。
(2)print子程序
按格式打出调度信息及各进程控制块的内容,print子程序在每次进程调度的时候都要执行一次。
(3)prisch子程序
将优先数算法调度进程使之模拟运行。
(4)insert子程序
将落选的进程按优先数大小插入到就绪队到。
(5)timesch子程序
按简单轮转算法调度进程使之模拟运行。
(6)insert1子程序
将落选的进程链到就绪队队列末尾。
各子程序之间的调度关系如下图2.1:
main
prisch init timesch
insert print Insert1
图2.1程序之间调度关系
5、程序框图
开始
输入调度算法
优先数
轮转
优先数或轮转,
生成按优先数大小排列的PCB 生成按进入次序排列的PCB
链首进程投入运行 链首进程投入运行
时间片到,进程时间片数-1,占用CPU时间+1 时间片到,进程时间片数-1,优先数-3
N 进程时间片数为0, N N 进程时间片数为0, 占用CPU
Y 时间已到
Y 轮转时间 撤消该进程 优先数撤消该进程 - Y 不小于
从链首取一个进程 链首进从链首取一个进程 运行进程退
程, Y 出,排列进程
队列为空, 链尾 队列为空, N N N Y 运行进程退出,按优
先数插入进程链 结束 结束
四、进程调度示例
1、问题描述
本系统的同步机构采用的信号量上的P、V操作的机制;控制机构包括阻塞和唤醒操作;时间片中断处理程序处理模拟的时间片中断;进程调度程序负责为各进程分配处理机。系统中设计了3个并发进程。它们之间有如下同步关系:3个进程需要互斥使用临界资源s2,进程1和进程2又需互斥使用临界资源s1。本系统在运行过程中随机打印出
各进程的状态变换过程 , 系统的调度过程及公共变量的变化情况。
2、算法
系统为进程设置了5种运行状态:e一一执行态;r一一高就绪态;t一一低就绪态(执行进程因时间片到限而转入);w一等待态;c一完成态。各进程的初始状态均设置为r。
系统分时执行各进程,并规定3个进程的执行概率均为33%。通过产生随机数x来模拟时间片。当进程process1访问随机数x时,若x?0.33;当进程process2访问x时,若x<0.33或x?0.66;当进程process3访问x时,若x<0.66,则分别认为各进程的执行时间片到限,产生“时间片中断“而转入低就绪态t。
进程调度算法采用剥夺式最高优先数法。各进程的优先数通过键盘输入予以静态设置。调度程序每次总是选择优先数最大(优先权最高)的就绪进程投入执
行。先从r状态进程中选 择,再从t状态进程中选择。当现行进程唤醒某个等待进程,且被唤醒进程的优先数大于现行进程时,则剥夺现行进程的执行权。
各进程在使用临界资源s1和s2时,通过调用信号量sem1和sem2上的P、V操作来实现同步。 阻塞和唤醒操作负责完成从进程的执行态到等待态以及从等待态到就绪态的转换。
系统启动后,在完成必要的系统初始化后便执行进程调度程序。当执行进程
因“时间片
中断“或被排斥使用临界资源,或唤醒某个等待进程时,立即进行进程调度。当3个进程都处于完成状态后,系统退出运行。
3、数据结构
(1)、信号量semaphore,对应于临界资源s1和s2分别有sem1和sem2,均为互斥信号量,内容为:
value 信号量值,初值为1;
firstwr 等待链首指针,指示该信号量上第一个等待进程的标识数。 (2)、现场保留区,用数组savearea[3][4]表示。即每个进程都有一个大小为4个单元的保留区,用来保存被“中断”时的现场信息,如通用寄存器的内容和断点地址等。
此外 ,系统中还用到下列主要全程变量:
exe 执行进程指针,其值为进程标识数 ;
I 用来模拟一个通用寄存器 ;
Addr 用来模拟程序计数器
S1,s2 两个公共变量,用作共享临界资源
4、程序描述
#include
#define TRUE 1
#define FALSE 0
#define MAXPRI 100 #define NIL -1 struct{
int id;
char status;
int t nextwr;
int prority;
}pcb[3];
struct{
int value;
int firstwr;
}sem[2];
char savearea[3][4],addr;
int I,s1,s2,seed,exe=NIL;
init() /*initialization*/
{int j;
for(j=0;j<3;j++) {
pcb[j].id=j;
pcb[j].status=?r?;
pcb[j].nextwr=NIL; printf(“\nprocess%dpriority?”,j+1); scanf(“%d”,&I);
pcb[j].priority=I; }
sem[0].value=1;sem[0].firstwr=NIL;
sem[1].value=1; sem[1].firstwr=NIL; for(I=1;i<3;i++)
for(j=0;j<4;j++)
savearea[i][j]=?0?;
}
float random( ) {
int m;
m=-seed; if(seed=0)retrun(FALSE);
block(se);
savearea[exe][0]=I; savearea[exe][1]=ad; exe=NIL;
return(TRUE);}
block(se)
int se;
{
int w;
printf("process%d is blocked,n”,exe+1); pcb[exe].status=?w?;
pcb[exe].nextwr=NIL; if((w=sem[se].firstwr)==NIL)
sem[se].firstwr=exe; else{
while(pcb[w].nextwr!=NIL)
w=pcb[w].nextwr;
pcb[w].nextwr=exe; }
}
v(se,ad)
int se;
char ad;
{
if(++sem[se].value>O)return(FALSE);
wakeup(se);
savearea[exe][0]=I; savearea[exe][1]=ad; return(TRUE);
}
wakeup(se)
int se;
{
int w;
w=sem[se].firstwr; if(w!=NIL){
sem[se].firstwr=pcb[w].nextwr;
pcb[w].status=?r?;
printf("proces%d is waken up,n”,w+1); }
}
process1()
{
if(addr==?a?)goto a1;
if(addr=='b?) goto b1;
if(addr==?c?) goto c1;
if(addr=='d') goto d1;
if(addr==?e?) goto e1;
if(addr==?f?) goto fl;
for (i=1;i<6;i++){
printf(“process1 calls P on the semaphore 1 , n”); if(p(O,?a?))break;
a1:printf(“process1 is executing in the critical section 1\n”);
if(timeint(„b?)break;
b1:printf(“s1=%d\n”,++s1);
printf(“process1 calls V on semaphore 1 and quit critical section
1\n”);
if(v(O,?c?))break;
c1: printf(“process1 calls P on the semaphore 2 , n”); if(p(1,?d?))break;
d1: printf(“process1 is executing cretical section 2\n”); if(timeint(„e?)break;
e1: printf(“s2=%d\n”,++s2);
printf(“process1 calls V on semaphore 2 and quit cretical section
2\n”);
if(v(1,?f?))break;
f1:printf(“process 1 cycle count=%d\n”,I);
}
if(I<6)return;
eexit(0);
}
process2()
{
if(addr==?a?)goto a2;
if(addr=='b?) goto b2;
if(addr==?c?) goto c2;
if(addr=='d') goto d2;
if(addr==?e?) goto e2;
if(addr==?f?) goto f2;
for (i=1;i<6;i++){
printf(“process2 calls P on the semaphore 2 , n”); if(p(1,?a?))break;
a2:printf(“process2 is executing in the critical section 2\n”);
if(timeint(„b?)break;
b2:printf(“s2=%d\n”,++s2);
printf(“process2 calls V on semaphore 2 and quit critical section
2\n”);
if(v(1,?c?))break;
c2: printf(“process2 calls P on the semaphore 1 , n”); if(p(0,?d?))break;
d2: printf(“process2 is executing cretical section 1\n”); if(timeint(„e?)break;
e2: printf(“s1=%d\n”,++s1);
printf(“process2 calls V on semaphore 1 and quit cretical section
1\n”);
if(v(0,?f?))break;
f2:printf(“process 2 cycle count=%d\n”,I);
}
if(I<6)return;
eexit(1);
}
process3()
{
if(addr==?a?)goto a3;
if(addr=='b?) goto b3;
if(addr==?c?) goto c3;
for (i=1;i<6;++i){
printf(“process3 calls P on the semaphore 2 , n”); if(p(1,?a?))break;
a3:printf(“process3 is executing on its critical section\n”);
if(timeint(„b?)break;
b3:printf(“s2=%d\n”,++s2);
printf(“process3 calls V on semaphore 2 and quit critical
section.\n”);
if(v(1,?c?))break;
c3:printf(“process 3 cycle count=%d\n”,I); }
if(I<6)return;
eexit(2);
}
eexit(n)
int n;
{
pcb[n].status=?c?;
printf("process%d is completed!,n”,n+1); exe =NIL;
}
main()
int k;
printf("****process rnanagement ****\n\n”); Init():
printf(“process1, process2, process3 are all in ready !\n”); for( ; ;)
{
if((k=scheduler())!=NIL) switch(k){
case 0 process1();
break ;
case1:process2();
break;
case 2:process3();
break;
default:printf(“process identifer error\n”);
break;
}
else break;
printf(“s1=%d,s2=%d\n”,s1,s2);
printf(“\n****END****\n”);
}
五、实验题
1、增加下列功能,至少选择一种。
(1)将静态优先数改为动态优先数。可考虑随进程等待时间的增长或推进速度的快慢随机修改优先数。如,当某进程被时间片中断的次数增加到一定的数目时,减少其优先数以提高优先权;当某进程率先执行到某次循环时,为减慢其推进速度可增加其优先数。
(2)增加“发送”和“接受”两个消息缓冲通讯操作。
2、进程调度算法采用反馈队列法来解决这个问题。 六、实验报告
1、实习的目的、要求和题目;
2、示例程序的详细分析报告;
3、自编程序的设计思想、框图结构、所使用的数据结构及符号说明; 4、自编程序的源程序
清单
安全隐患排查清单下载最新工程量清单计量规则下载程序清单下载家私清单下载送货清单下载
(附上注释);
5、自编程序的运行结果;
6、实习总结