实验目的
1.理解进程的概念,明确进程和程序的区别
2.理解并发执行的实质。
3.掌握进程的创建、睡民、撤销等进程控制方法。
实验内容
用C语言编写程序,模拟实现创建新的进程:查看运行进程;换出某个进程:杀死运行进程等功能。
实验步骤
进程
1.进程的含义
进程是程序在一个数据集合上的运行过程,是系统资源分配和调度的一个独立单位。一个程序在不同数据集合上运行,乃至一个程序在同样数据集合上的多次运行都是不同的进程。
2.进程的状态
通常,一个进程必须具有就绪、执行和阻塞3种基本状态。
1)就绪状态
当进程已分配到除处理器(CPU)以外的所有必要资源后,只要再获得处理器就可以立即执行,这时进程的状态称为就绪状态。在一个系统里,可以有多个进程同时处于就绪状态通常把这些就绪进程排成一个或多个队列,称为就绪队列。
2)执行状态态
处于就绪状态的进程一且获得了处理器,就可以运行,进程状态也就处于执行状态。在单处理器系统中,只能有一个进程处于执行状态,在多处理器系统中,则可能有多个进程处于执行状态。
3)阻塞状态
正在执行的进程因为发生某些事件(如请求输入输出、中请额外空间等)而暂停运行,这种受阻暂停的状态称为阻塞状态,也可以称为等待状态。通常将处于阻塞状态的进程排成一个队列,称为阻塞队列。在有些系统中,也会按阻塞原因的不同将处于阻塞状态的进程排成多个队列。
除了进程的3种基本状态外,在很多系统为了更好地描述进程的状态变化,又增加了两种状态
1)新状态
当一个新进程刚刚建立,还未将其放入就绪队列的状态,称为新状态。例如一个人刚开始接受教育,此时就可以称其处于于新状态。
2)终止状态
当一个进程己经正常结東或异常结東,操作作系统已将其从系统队列中移出,但尚未撤消,这时称为终止状态。
2.3.2进程控制块
1.进程控制块的作用
进程控制块是构成进程实体的重要组成部分,是操作系统中最重要的记录型数据,在进程控制块PCB中记录了操作系统所需要的、用于描述进程情况及控制进程运行所需要的全基本单位,一个能够并发执行的进程。换句话说,在进程的整个生命周期中,操作系统都要通过进程的PCB来对并发执行的进程进行管理和控制,进程控制块是系统对进程控制采用的数据结构,系统是根据进程的PCB而感知进程是否存在。所以,进程控制块是进程存在的唯一标志。当系统创建一个新进程时,就要为它建立一个PCB:进程结束时,系统又回收其PCB,进程也随之消亡
2.进程控制块的内容
进程控制块主要包括下述四个方面的信息。
1)进程标识信息
进程标识符用于标识一个进程,通常有外部标识符和内部标识符两种。
(1)外部标识符。由进程创建者命名,通常是由字母、数字所组成的一个字符串,在用户(进程)访问该进程时使用。外部标识符都便于记忆,如计算进程、打印进程、发送进程、接接收进程等
(2)内部标识符。是为方便系统使用而设置的,操作系统为每一个进程赋予唯一的一个整数,作为内部标识符。它通常就是一个进程的序号。
2)说明信息(进程调度信息)
说明信息是有关进程状态等一些与进程调度有关的信息,包括:
(1)进程状态。指明进程当前的状态,作为进程调度和对换时的依据。
(2)进程优先权。用于描述进程使用处理器的优先级别,通常是一个整数,优先权高的进程将可以优先获得处理器
(3)进程调度所需的其他没信息。其内容与所采用的进程调度算法有关,如进程等待时间、进程己执行时间等。
(4)阻塞事件。指进程由执行行状态转变为阻塞所等待发生的事件,即阻塞原因。
3)现场信息(处理器状态信息)
现场信息是用于保留进程存放在处理器中的各种信息,主要由处理器中各种寄存器的内容组成。尤其是当执行中的进程暂停时,这些寄存器内的信息将被保存在PCB里,当该中再次获得执行时,能从上次停止的地方继续执行。
(1)通用寄存器。其中的内容可以被用户程序访问,用于暂存信息。
(2)指今计数器。存放要访问的下一条指令的地址
(3)程序状态字。保存当前处理器状态的信息,如执行方式、中断屏蔽标志等。
(4)用户栈指针。每个用户进程都有一个或若干个与之相关的关系栈,用于存放过程和系统调用参数及调用地址,栈指针指向堆栈的栈顶。
4)管理信息(进程控制信息)
(1)程序和数据的地址。它是指该进程的程序和数据所在的主存和外存地址再次执行时,能够找到程序和数据。
(2)进程同步和通信机制。它是指实现进程同步和进程通信时所采用的机制、指针、信号量等。
(3)资源清单。该清单中存放有除了CPU以外,进程所需的全部资源和已经分配到的资源。
(4)链接指针。它将指向该进程所在队列的下一个进程的PCB的首地址
3.进程控制块的组织方式
在一个系统中,通常拥有数十个、数百个乃至数千个PCB,为了能对它们进行有效的管理,就必须通过适当的方式将它们组织起来,日前常用的组织方式有链接方式和索引方式两种。
1)链接方式
把具有相同状态的PCB,用链接指针链接成队列,如就绪队列、阻塞队列和空闲队列等。就绪队列中的PCB将按照相应的进程调度算法进行排序。而阻塞队列也可以根据阻塞原因的不同,将处于阻塞状态的进程的PCB,排成等待I/O队列、等待主存队列等多个队列。此外,系统主存的PCB区中空闲的空间将排成空闲队列,以方便PCB的分配和回收。
2)索引方式
系统根据各个进程的状态,建立不同索引表,例如就绪索引表、阻塞索引表等。并把各个索引表在主存的首地址记录在主存中的专用单元里,也可以称为表指针。在每个索引表的表目中,记录着具有相同状态的各个PCB在表中的地址。
4.进程控制原语
原语是指具有特定功能的不可被中断的过程。它主要用于实现操作系统的一些专门控制操作。用于进程控制的原语有:
1)创建原语。用于为一个进程分配工作区和PCB,并初始化PCB,进程处于就绪态。
2)撤销原语。用于一个进程完成后,收回它的工作区和PCB。
3)阻塞原语。用于进程在运行过程中发生等待时间时,将进程改为阻塞态。
4)唤醒原语。用于当进程等待的事件发生时,将进程改为就绪态。
进程的创建与撤消
1.进程的创建
一旦操作系统发现了要求创建进程的事件后,便调用进程创建原按下列步骤创建一个新进程。
①为新进程分配惟一的进程标识符,并从PCB队列中申请一个空闲PCB。
②为新进程的程序和数据,以及用户栽分配相应的主存空间及其他必要分配资源。
③初始化PCB中的相应信息,如标识信息、处理器倌息、进程控制倌息等
④如果就绪队列可以接纳新进程,便将新进程加入到就绪队列中、
2.进程的撤销
一旦操作系统发现了要求终止进程的事件后,便调用进程终止原语按下列步骤终止指定的进程。
①根据被终止进程的标识符,从PCB集合中检索该进程的PCB,读出进程状态。
②若该进程处于执行状态,则立即终止该进程的执行。
③若该进程有子孙进程,还要将其子孙进程终止。
④将该进程所占用的资源回收,归还给其父进程或操作系统。
⑤将被终止进程的PCB从所在队列中移出,并撇销该进程的PCB
2.3.4进程的阻塞与唤醒
1.进程的阻塞
一旦操作系统发现了要求阻塞进程的事件后,便调用进程阻塞原语,按下列步骤阻塞指定的进程。
①立即停止执行该进程。
②修改进程控制块中的相关信息。把进程控制块中的运行状态由“执行”状态改为“阻
塞”状态,并填入等待的原因,以及进程的各种状态信息。
③把进程控制块插入到阻塞队列。根据阻塞队列的组织方式插入阻塞队列中。
④待调度程序重新调度,运行就绪队列中的其他进程。
2.进程的唤醒
一且操作系统发现了要求唤醒进程的事件后,便调用进程唤醒原语,按下列步骤唤醒指定的进程。
①从阻塞队列中找到该进程。
②修改该进程控制块的相关内容。把阻塞状态改为就绪状态,删除等待原因等。
③把进程控制块插入到就绪队列中。
按照就绪队列的组织方式,把被唤醒的进程的进程控制块插入到就绪队列中.
实验代码
程序代码
#include "stdio.h"
#include "conio.h"
#include "stdlib.h"
struct PCB_type
{ int pid;
int youxianji;
int daxiao;
char neirong[10];
int state; /* 0表示不在内存,1表示在内存,2表示挂起*/
};
struct PCB_type neicun[20];
int num=0,guaqi=0,bj,i,j,pid;/*定义相关参数变量*/
/*********************创建新进程*************************/
void create()
{
if(num>=20) /*判断是否有存储空间*/
printf("\n 内存已满,请删除或挂起其他程序");
else{
for(i=0;i<20;i++)
if(neicun[i].state==0) break; /*按顺序在内存中寻找创建新进程的空间*/
printf("\n请输入新建进程的pid值\n");
scanf("%d",&neicun[i].pid);
for(j=0;j<i;j++) /*判断是否之前已存储相同pid进程*/
if(neicun[j].pid==neicun[i].pid)
{ printf("\n该进程已存在于内存中");
return;
}
printf("\n请输入新进程的优先级 \n" );
scanf("%d",&neicun[i].youxianji);
printf("\n请输入新进程的大小\n" );
scanf("%d",&neicun[i].daxiao);
printf("\n请输入新进程的内容\n" );
scanf("%s",&neicun[i].neirong);
neicun[i].state=1; /*将进程状态设为1,表示在内存中*/
num++;/*内存中进程数目加一*/
}
}
/********************查看当前运行进程**************************/
void run()
{ bj=0;
for(i=0;i<20;i++)
if(neicun[i].state==1) /*若进程状态设为1,表示在内存中,输出查看*/
{printf("\n pid=%d",neicun[i].pid);
printf(" youxianji=%d",neicun[i].youxianji);
printf(" daxiao=%d",neicun[i].daxiao);
printf(" neirong=%s",neicun[i].neirong);
bj=1;/*存在于内存中的进程标记为1 */
}
if(bj==0)
printf("\n当前没有运行该进程\n" );/*标记为0,表示该进程未创建于内存中*/
}
/********************换出进程***************************/
void huanchu()
{if(num==0) /*num=0,表示内存中无进程*/
{ printf("\n当前没有运行的进程\n" );
return;
}
printf("\n请输入要换出的进程的pid值\n" );
scanf("%d",&pid);
bj=0;
for(i=0;i<20;i++)
{
if(pid==neicun[i].pid)
{ if(neicun[i].state==1)
{neicun[i].state=2; /*将该进程的状态设为挂起*/
guaqi++;
printf("\n该进程已成功挂起换出\n" );
}
else if(neicun[i].state==0)
printf("\n要换出的进程不存在\n" );
else printf("\n要换出的进程已经被挂起\n" );
bj=1;break;
}
}
if(bj==0) printf("\n要换出的进程不存在\n" );
}
/********************杀死进程***************************/
void kill()
{
if(num==0)
{ printf("\n当前没有运行的进程\n" );
return;
}
printf("\n请输入要杀死的进程的pid值\n" );
scanf("%d",&pid);
bj=0;
for(i=0;i<20;i++)
{
if(pid==neicun[i].pid)
{ if(neicun[i].state==1)
{neicun[i].state=0; /*将该进程的状态设为不在内存中*/
num--; /*内存中的进程数目减少一个*/
printf("\n该进程已成功杀死\n" );
}
else if(neicun[i].state==0)
printf("\n要杀死的进程不存在\n" );
else printf("\n要杀死的进程已经被挂起\n" );/*剩余状态为2,挂起*/
bj=1;break;
}
}
if(bj==0) printf("\n要杀死的进程不存在\n" );
}
/********************唤醒进程***************************/
void huanxing()
{
if(num==0)
{ printf("当前没有运行的进程\n");
return;
}
if(guaqi==0) /*guaqi=0,表示没有挂起的进程*/
{ printf("\n当前没有换出的进程\n");
return;
}
printf("\n请输入要唤醒的进程的pid值:\n");
scanf("%d",&pid);
for(i=0;i<20;i++){
if(pid==neicun[i].pid)
{
if(neicun[i].state==2)
{neicun[i].state=1; /*将该进程的状态设为挂起*/
guaqi--;
num++;
printf("\n该进程已成功唤醒\n" );
}
else if(neicun[i].state==0)
printf("\n要唤醒的进程不存在\n" );
else printf("\n要唤醒的进程已经在内存中\n" );
}
}
}
/********************主程序***************************/
void main()
{int xuhao,n=1,i;
for(i=0;i<20;i++)
neicun[i].state=0; /*使所有进程都初始设为不在内存中*/
while(n){
printf("\n**********************************************");
printf("\n* 进程演示系统 *");
printf("\n**********************************************");
printf("\n 1.创建新的进程 2.查看运行进程");
printf("\n 3.换出某个进程 4.杀死运行进程");
printf("\n 5.唤醒某个进程 6.退出程序 ");
printf("\n**********************************************");
printf("\n请选择(1~6):");
scanf("%d",&xuhao);
switch(xuhao)
{
case 1: create( ); break;
case 2 :run(); break;
case 3 :huanchu();break;
case 4 :kill();break;
case 5 :huanxing();break;
case 6 :exit(0);
default: n=0;
break;
}
}
}