大作业2_单处理器系统的进程调度

实验二  单处理器系统的进程调度-编程实现采用优先数调度算法的进程调度

1.实验目的

加深对进程概念的理解,明确进程和程序的区别;

深入了解系统如何组织进程、创建进程;

进一步认识如何实现处理器调度。

2.实验预备知识

进程的概念;

进程的组织方式;

进程的创建;

进程的调度。

3.实验内容

编写程序完成单处理机系统中的进程调度,要求采用时间片轮转调度算法。实验具体包括:首先确定进程控制块的内容,进程控制块的组成方式;然后完成进程创建原语和进程调度原语;最后编写主函数对所作工作进程测试。

4.提示与讲解

这个实验主要要考虑三个问题:如何组织进程、如何创建进程和如何实现处理器调度。

考虑如何组织进程,首先就要设定进程控制块的内容。进程控制块PCB记录各个进程执行时的情况。不同的操作系统,进程控制块记录的信息内容不一样。操作系统功能越强,软件也越庞大,进程控制块记录的内容也就越多。这里的实验只使用了必不可少的信息。一般操作系统中,无论进程控制块中信息量多少,信息都可以大致分为以下四类:

① 标识信息

每个进程都要有一个惟一的标识符,用来标识进程的存在和区别于其他进程。这个标识符是必不可少的,可以用符号或编号实现,它必须是操作系统分配的。在后面给出的参考程序中,采用编号方式,也就是为每个进程依次分配一个不相同的正整数。

② 说明信息

用于记录进程的基本情况,例如进程的状态、等待原因、进程程序存放位置、进程数据存放位置等等。实验中,因为进程没有数据和程序,仅使用进程控制块模拟进程,所以这部分内容仅包括进程状态。

③ 现场信息

现场信息记录各个寄存器的内容。当进程由于某种原因让出处理器时,需要将现场信息记录在进程控制块中,当进行进程调度时,从选中进程的进程控制块中读取现场信息进行现场恢复。现场信息就是处理器的相关寄存器内容,包括通用寄存器、程序计数器和程序状态字寄存器等。在实验中,可选取几个寄存器作为代表。用大写的全局变量AX、BX、CX、DX模拟通用寄存器、大写的全局变量PC模拟程序计数器、大写的全局变量PSW模拟程序状态字寄存器。

④ 管理信息

管理信息记录进程管理和调度的信息。例如进程优先数、进程队列指针等。实验中,仅包括队列指针。

因此可将进程控制块结构定义如下:

struct pcb

{int name;                   /*进程标识符*/

 int status;                  /*进程状态*/

 int ax, bx, cx,dx;        /*进程现场信息,通用寄存器内容*/

 int pc;                  /*进程现场信息,程序计数器内容*/

 int psw;                 /*进程现场信息,程序状态字寄存器内容*/

 int next;                /*下一个进程控制块的位置*/

}

确定进程控制块内容后,要考虑的就是如何将进程控制块组织在一起。多道程序设计系统中,往往同时创建多个进程。在单处理器的情况下,每次只能有一个进程处于运行态,其他的进程处于就绪状态或等待状态。为了便于管理,通常把处于相同状态的进程的进程控制块链接在一起。单处理器系统中,正在运行的进程只有一个。因此,单处理器系统中进程控制块分成一个正在运行进程的进程控制块、就绪进程的进程控制块组织成的就绪队列和等待进程的进程控制块组成的等待队列。由于实验模拟的是进程调度,没有对等待队列的操作,所以实验中只有一个指向正在运行进程的进程控制块指针和一个就绪进程的进程控制块队列指针。操作系统的实现中,系统往往在主存中划分出一个连续的专门区域存放系统的进程控制块,实验中应该用数组模拟这个专门的进程控制块区域,定义如下:

#define  n   10            /*假定系统允许进程个数为10*/

struct pcb  pcbarea[n];       /*模拟进程控制块区域的数组*/

    这样,进程控制块的链表实际上是数据结构中使用的静态链表。进程控制块的链接方式可以采用单向和双向链表,实验中,进程控制块队列采用单向不循环静态链表。为了管理空闲进程控制块,还应该将空闲控制块链接成一个队列。

实验中采用时间片轮转调度算法,这种算法是将进程控制块按照进入就绪队列的先后次序排成队列。关于就绪队列的操作就是从队头摘下一个进程控制块和从队尾挂入一个进程控制块。因此为就绪队列定义两个指针,一个头指针,指向就绪队列的第一个进程控制块;一个尾指针,指向就绪队列的最后一个进程控制块。

实验中指向运行进程的进程控制块指针、就绪队列指针和空闲进程控制块队列指针定义如下:

int  run;            /*定义指向正在运行进程的进程控制块的指针*/

struct

{int  head;

 int  tail;

}ready;              /*定义指向就绪队列的头指针head和尾指针tail*/

int  pfree;          /*定义指向空闲进程控制块队列的指针*/

以上是如何组织进程,下面考虑如何创建进程。

进程创建是一个原语,因此在实验中应该用一个函数实现,进程创建的过程应该包括:

①申请进程控制块:进程控制块的数量是有限的,如果没有空闲进程控制块,则进程不能创建,如果申请成功才可以执行第②步;

②申请资源:除了进程控制块外,还需要有必要的资源才能创建进程,如果申请资源不成功,则不能创建进程,并且归还已申请的进程控制块;如果申请成功,则执行第三步,实验无法申请资源,所以模拟程序忽略了申请资源这一步;

③填写进程控制块:将该进程信息写入进程控制块内,实验中只有进程标识符、进程状态可以填写,每个进程现场信息中的寄存器内容由于没有具体数据而使用进程(模拟进程创建时,需输入进程标识符字,进程标识符本应系统建立,并且是惟一的,输入时注意不要冲突),刚刚创建的进程应该为就绪态,然后转去执行第四步;

④挂入就绪队列:如果原来就绪队列不为空,则将该进程控制块挂入就绪队列尾部,并修改就绪队列尾部指针;如果原来就绪队列为空,则将就绪队列的头指针、尾指针均指向该进程控制块,进程创建完成。

进程创建流程图如图2.2所示。

多道程序设计的系统中,处于就绪态的进程往往是多个,它们都要求占用处理器,可是单处理器系统的处理器只有一个,进程调度就是解决这个处理器竞争问题的。进程调度的任务就是按照某种算法从就绪进程队列中选择一个进程,让它占有处理器。因此进程调度程序就应该包括两部分,一部分是在进程就绪队列中选择一个进程,并将其进程控制块从进程就绪队列中摘下来,另一部分工作就是分配处理器给选中的进程,也就是将指向正在运行进程的进程控制块指针指向该进程的进程控制块,并将该进程的进程控制块信息写入处理器的各个寄存器中。

图2.2  进程创建流程图

实验中采用时间片轮转调度算法。时间片轮转调度算法让就绪进程按就绪的先后次序排成队列,每次总是选择就绪队列中的第一个进程占有处理器,但是规定只能使用一个“时间片”。时间片就是规定进程一次使用处理器的最长时间。实验中采用每个进程都使用相同的不变的时间片。

采用时间片轮转调度算法的进程调度流程图如图2.3所示。

完成上述功能后,编写主函数进行测试:首先建立一个就绪队列,手工输入信息建立几个进程;然后进行进程调度;最后将指向正在运行进程的指针指向的进程控制块的内容输出,察看结果。

5. 实验报告

实验报告格式及报告内容:

实验题目。

(1)    程序中使用的数据结构。

(2)    流程图。

(3)    附有源程序并附上必要的注释。

6. 参考提示

为便于处理,程序中进程的运行时间以时间片为基本计算单位。

1.        进程控制块PCB的格式为:

2.        优先数算法:进程就绪链按优先数大小排列,链首首先投入运行。每过一个时间片,运行进程所需运行的时间片数减1,说明它已运行了一个时间片,优先数也减3,理由是该进程如果在一个时间片中完成不了,优先数应降低一级。接着比较现行进程和就绪链首进程的优先数,如果仍是现行进程高或者相同,就让现行进程继续运行,否则,调度就绪链的链首进程投入运行。原运行进程再按照其优先数大小插入就绪链,且改变它们对应的进程状态,直至所有进程都运行完各自的时间片数。

3.        简单轮转法:进程就绪链按各进程进入的先后次序排列,进程每次占用处理机的轮转时间按其重要程度登入进程控制块中的轮转时间片数记录项(相应于优先数法的优先数记录项位置)。每过一个时间片,运行进程占用处理机的时间片数加1,然后比较占用处理机的时间片数是否与该进程的轮转时间片数相等,若相等说明已到达轮转时间,应将现运行进程排到就绪链末尾,调度链首进程占用处理机,且改变它们的进程状态,直至所有进程完成各自的时间片。

4.        进程控制块链结构:

RUN              HEAD                                 TAIL

其中:

RUN ——当前运行进程指针;

HEAD——进程就绪链链首指针;

TAIL ——进程就绪链链尾指针。

#include<iostream.h>
#include<string.h>
struct Data
{
 char P[10];
 int T;
 char S;
 int Q;
};
typedef struct PCB
{
 struct PCB *next;
 struct Data sj;
}JC;
JC *Head=new PCB;
void insert(JC *p)
{
    JC *tem;
 tem=Head->next;
 if(Head->next==NULL)
 {Head->next=p;}
 else
 {
  while(p->sj.Q<tem->sj.Q&&tem->next!=NULL)
   tem=tem->next;
     p->next=tem->next;
     tem->next=p;
 }

}
void run()
{
 JC *p;
 p=Head->next;
 cout<<"调用进程"<<p->sj.P<<",其优先数为"<<p->sj.Q<<"其剩余运行时间为"<<p->sj.T<<endl;
 p->sj.Q--;
 p->sj.T--;
 Head->next=p->next;
 if(p->sj.T==0)
 {
  p->sj.S='E';
  p->next=NULL;
  cout<<"进程"<<p->sj.P<<"运行结束,其运行状态为"<<p->sj.S<<endl;
 }
 else
 {
  insert(p);
  cout<<"运行后,进程"<<p->sj.P<<"优先数为"<<p->sj.Q<<",剩余运行时间为"<<p->sj.T<<"运行状态为"<<p->sj.S<<endl;
 } 
}

void create(JC *p)
{
 p=new JC;
 cout<<"请输入此进程的进程名:";
 cin>>p->sj.P;
 cout<<"请输入此进程的优先数:";
 cin>>p->sj.Q;
 cout<<"请输入此进程的执行时间:";
 cin>>p->sj.T;
 p->sj.S='R';
 p->next=NULL;
 insert(p);
}

void main()
{
    //JC *Head=new PCB;
 Head->next=NULL;
 JC p[5];
 for(int i=0;i<=4;i++)
 {
  //p[i]=new  PCB;
  //JC *a=&p[i];
  create(p+i);
 }
 while(Head->next!=NULL)
  run();
 cout<<"执行完毕";
}

 

第二篇:实验二 单处理器系统的进程调度

实验二  单处理器系统的进程调度

(附实验报告)

1.实验目的

加深对进程概念的理解,明确进程和程序的区别;

深入了解系统如何组织进程、创建进程;

进一步认识如何实现处理器调度。

2.实验预备知识

进程的概念;

进程的组织方式;

进程的创建;

进程的调度。

3.实验内容

编写程序完成单处理机系统中的进程调度,要求采用时间片轮转调度算法。实验具体包括:首先确定进程控制块的内容,进程控制块的组成方式;然后完成进程创建原语和进程调度原语;最后编写主函数对所作工作进程测试。

4.提示与讲解

这个实验主要要考虑三个问题:如何组织进程、如何创建进程和如何实现处理器调度。

考虑如何组织进程,首先就要设定进程控制块的内容。进程控制块PCB记录各个进程执行时的情况。不同的操作系统,进程控制块记录的信息内容不一样。操作系统功能越强,软件也越庞大,进程控制块记录的内容也就越多。这里的实验只使用了必不可少的信息。一般操作系统中,无论进程控制块中信息量多少,信息都可以大致分为以下四类:

    ① 标识信息

每个进程都要有一个惟一的标识符,用来标识进程的存在和区别于其他进程。这个标识符是必不可少的,可以用符号或编号实现,它必须是操作系统分配的。在后面给出的参考程序中,采用编号方式,也就是为每个进程依次分配一个不相同的正整数。

② 说明信息

用于记录进程的基本情况,例如进程的状态、等待原因、进程程序存放位置、进程数据存放位置等等。实验中,因为进程没有数据和程序,仅使用进程控制块模拟进程,所以这部分内容仅包括进程状态。

③ 现场信息

现场信息记录各个寄存器的内容。当进程由于某种原因让出处理器时,需要将现场信息记录在进程控制块中,当进行进程调度时,从选中进程的进程控制块中读取现场信息进行现场恢复。现场信息就是处理器的相关寄存器内容,包括通用寄存器、程序计数器和程序状态字寄存器等。在实验中,可选取几个寄存器作为代表。用大写的全局变量AX、BX、CX、DX模拟通用寄存器、大写的全局变量PC模拟程序计数器、大写的全局变量PSW模拟程序状态字寄存器。

④ 管理信息

管理信息记录进程管理和调度的信息。例如进程优先数、进程队列指针等。实验中,仅包括队列指针。

因此可将进程控制块结构定义如下:

struct pcb

{int name;                  

 int status;                 

 int ax, bx, cx,dx;              

 int pc;                

 int psw;               

 int next;               

}

确定进程控制块内容后,要考虑的就是如何将进程控制块组织在一起。多道程序设计系统中,往往同时创建多个进程。在单处理器的情况下,每次只能有一个进程处于运行态,其他的进程处于就绪状态或等待状态。为了便于管理,通常把处于相同状态的进程的进程控制块链接在一起。单处理器系统中,正在运行的进程只有一个。因此,单处理器系统中进程控制块分成一个正在运行进程的进程控制块、就绪进程的进程控制块组织成的就绪队列和等待进程的进程控制块组成的等待队列。由于实验模拟的是进程调度,没有对等待队列的操作,所以实验中只有一个指向正在运行进程的进程控制块指针和一个就绪进程的进程控制块队列指针。操作系统的实现中,系统往往在主存中划分出一个连续的专门区域存放系统的进程控制块,实验中应该用数组模拟这个专门的进程控制块区域,定义如下:

#define  n   10           

struct pcb  pcbarea[n];     

    这样,进程控制块的链表实际上是数据结构中使用的静态链表。进程控制块的链接方式可以采用单向和双向链表,实验中,进程控制块队列采用单向不循环静态链表。为了管理空闲进程控制块,还应该将空闲控制块链接成一个队列。

实验中采用时间片轮转调度算法,这种算法是将进程控制块按照进入就绪队列的先后次序排成队列。关于就绪队列的操作就是从队头摘下一个进程控制块和从队尾挂入一个进程控制块。因此为就绪队列定义两个指针,一个头指针,指向就绪队列的第一个进程控制块;一个尾指针,指向就绪队列的最后一个进程控制块。

实验中指向运行进程的进程控制块指针、就绪队列指针和空闲进程控制块队列指针定义如下:

int  run;           

struct

{int  head;

 int  tail;

}ready;             

int  pfree;         

以上是如何组织进程,下面考虑如何创建进程。

进程创建是一个原语,因此在实验中应该用一个函数实现,进程创建的过程应该包括:

①申请进程控制块:进程控制块的数量是有限的,如果没有空闲进程控制块,则进程不能创建,如果申请成功才可以执行第②步;

②申请资源:除了进程控制块外,还需要有必要的资源才能创建进程,如果申请资源不成功,则不能创建进程,并且归还已申请的进程控制块;如果申请成功,则执行第三步,实验无法申请资源,所以模拟程序忽略了申请资源这一步;

③填写进程控制块:将该进程信息写入进程控制块内,实验中只有进程标识符、进程状态可以填写,每个进程现场信息中的寄存器内容由于没有具体数据而使用进程(模拟进程创建时,需输入进程标识符字,进程标识符本应系统建立,并且是惟一的,输入时注意不要冲突),刚刚创建的进程应该为就绪态,然后转去执行第四步;

④挂入就绪队列:如果原来就绪队列不为空,则将该进程控制块挂入就绪队列尾部,并修改就绪队列尾部指针;如果原来就绪队列为空,则将就绪队列的头指针、尾指针均指向该进程控制块,进程创建完成。

进程创建流程图如图2.2所示。

多道程序设计的系统中,处于就绪态的进程往往是多个,它们都要求占用处理器,可是单处理器系统的处理器只有一个,进程调度就是解决这个处理器竞争问题的。进程调度的任务就是按照某种算法从就绪进程队列中选择一个进程,让它占有处理器。因此进程调度程序就应该包括两部分,一部分是在进程就绪队列中选择一个进程,并将其进程控制块从进程就绪队列中摘下来,另一部分工作就是分配处理器给选中的进程,也就是将指向正在运行进程的进程控制块指针指向该进程的进程控制块,并将该进程的进程控制块信息写入处理器的各个寄存器中。

 操作系统模拟实验:单处理器系统的进程调度

图2.2  进程创建流程图

实验中采用时间片轮转调度算法。时间片轮转调度算法让就绪进程按就绪的先后次序排成队列,每次总是选择就绪队列中的第一个进程占有处理器,但是规定只能使用一个“时间片”。时间片就是规定进程一次使用处理器的最长时间。实验中采用每个进程都使用相同的不变的时间片。

采用时间片轮转调度算法的进程调度流程图如图2.3所示。

完成上述功能后,编写主函数进行测试:首先建立一个就绪队列,手工输入信息建立几个进程;然后进行进程调度;最后将指向正在运行进程的指针指向的进程控制块的内容输出,察看结果。

 操作系统模拟实验:单处理器系统的进程调度

图2.3  进程调度流程图

5.课外题

编程实现采用优先数调度算法的进程调度。

我的实现代码(C语言):

#include <stdio.h>

#define N 10     //系统中所允许的最大进程数量
#define SLOT 5   //时间片大小

//进程状态枚举
typedef enum
{
 Running,     //运行状态
 Aready,      //就绪状态
 Blocking     //阻塞状态

} ProStatus;

//进程控制块
typedef struct
{
 int name;                  //进程标识符
 ProStatus status;          //进程状态
 int ax,bx,cx,dx;           //通用寄存器
 int pc;                    //程序计数器寄存器
 int psw;                   //程序状态字寄存器
 int next;                  //指向下一个进程的指针
} PCB;

//就绪队列指针
typedef struct          
{
 int head;            //头指针
 int tail;            //尾指针
} Ready;


//模拟寄存器
int PSW,AX,BX,CX,DX,PC,TIME;

//PCB的静态链表
PCB pcbArea[N];          //模拟PCB区域的数组
int run;                 //运行状态程序的指针
Ready ready;             //就绪队列指针              
int pfree;               //空闲队列的指针

 //初始化运行状态进程指针
void InitRun()
{
 run=-1;
}
 
 //初始化就绪状态队列
void InitReady()
{
 ready.head=ready.tail=-1;
}

 //初始化空闲队列
void InitFree()
{
 int temp;
 for(temp=0;temp<N-1;temp++)
 {
  pcbArea[temp].next=temp+1;
 }
 pcbArea[temp].next=-1;

 pfree=0;
}

 //就绪队列出队
int PopReady()          //返回结点在PCB区域数组的编号
{
 int temp;
 if(ready.head==-1)
 {
  printf("就绪队列为空,不能出队。\n");
  return -1;
 }
 temp=ready.head;
 ready.head=pcbArea[temp].next;
 if(ready.head==-1)
  ready.tail=-1;
 pcbArea[temp].next=-1;
 return temp;

}

 //空闲队列出队
int PopFree()          //返回结点在PCB区域数组的编号
{
 int temp;    
 if(pfree==-1)  
 {
  printf("空闲队列为空,不能出队。\n");
  return -1;
 }
 
 temp=pfree;
 pfree=pcbArea[temp].next;
 pcbArea[temp].next=-1;
 return temp;
}

 //就绪队列入队
void PushReady(int x)          //x为入队结点的编号
{
 int temp;
 if(ready.head==-1)
 {
  ready.head=x;
  ready.tail=x;
 }
 else
 {
  temp=ready.tail;
  ready.tail=x;
 }
 pcbArea[ready.tail].next=-1;
}

//创建PCB
void CreatePCB(int x,PCB pcb)         //x为要创建PCB在PCB区域数组的编号
{
 pcbArea[x].ax=pcb.ax;
 pcbArea[x].bx=pcb.bx;
 pcbArea[x].cx=pcb.cx;
 pcbArea[x].dx=pcb.dx;
 pcbArea[x].name=pcb.name;
 pcbArea[x].next=-1;
 pcbArea[x].pc=pcb.pc;
 pcbArea[x].psw=pcb.psw;
 pcbArea[x].status=pcb.status;
}

//创建进程函数
void Create(PCB pcb)
{
 int temp;
 if(pfree==-1)
 {
  printf("空闲队列为空,不能创建进程。\n");
  return;
 }
 temp=PopFree();
 pcb.status=Aready;
 CreatePCB(temp,pcb);
 PushReady(temp);
}

//进程调度函数
void Schedule()
{
 int temp;
 if(ready.head==-1)
 {
  printf("系统内没有进程可以调度。");
  return;
 }
 temp=PopReady();
 pcbArea[temp].status=Running;

 TIME=SLOT;                            //恢复CPU现场
 AX=pcbArea[temp].ax;
 BX=pcbArea[temp].bx;
 CX=pcbArea[temp].cx;
 DX=pcbArea[temp].dx;
 PC=pcbArea[temp].pc;
 PSW=pcbArea[temp].psw;
 
 run=temp;             //将选中的进程赋给运行指针

 printf("当前运行的程序:\n");          //输出调度结果
 printf("进程号:%d\n",pcbArea[run].name);
 printf("进程状态:%d\n",pcbArea[run].status);
 printf("寄存器内容:\nAX\tBX\tCX\tDX\tPC\tPSW\n");
 printf("%d\t%d\t%d\t%d\t%d\t%d\n",
  pcbArea[run].ax,pcbArea[run].bx,pcbArea[run].cx,pcbArea[run].dx,pcbArea[run].pc,pcbArea[run].psw);

}


void main()
{
 int temp;
 PCB tmp_pcb;

 printf("请输入进程号,以负数为结束(进程号应保持唯一)。\n\n按任意键进入输入模式:");
 getchar();

 InitRun();
 InitReady();
 InitFree();

 printf("请开始输入进程号:\n");
 while(1)
 {
  scanf("%d",&temp);

  if(temp<0)
   break;
  tmp_pcb.name=temp;
  tmp_pcb.ax=temp;
  tmp_pcb.bx=temp;
  tmp_pcb.cx=temp;
  tmp_pcb.dx=temp;
  tmp_pcb.pc=temp;
  tmp_pcb.psw=temp;

  Create(tmp_pcb);  
 }
 printf("\n");

 Schedule();

 

相关推荐