操作系统实验进程控制(C语言版)

操作系统实验进程控制

  • 1、实验目的
  • 2、实验内容
  • 3、实验准备
    • 3.1进程
      • 3.1.1进程的含义
      • 3.1.2进程状态
      • 3.1.3进程状态之间的装换
    • 3.2进程控制块
    • 3.3进程的创建与撤消
    • 3.4进程的阻塞与唤醒
  • 4.实验要求
  • 5.结果分析


1、实验目的


1.1理解进程的概念,明确进程和程序的区别。

1.2理解并发执行的实质。

1.3掌握进程的创建、睡眠、撤销等进程控制方法。


2、实验内容


用C语言编写程序,模拟实现创建新的进程:查看运行进程;换出某个进程;杀死运行进程等功能。

3、实验准备

3.1进程

3.1.1进程的含义


进程是程序在一个数据集合上的运行过程,是系统资源分配和调度的一个独立单位。一个程序在不同数据集合上运行,乃至一个程序在同样数据集合上的多次运行都是不同的进程。

3.1.2进程状态

通常,一个进程必须具有就绪、执行和阻塞3种基本状态。

(1)就绪状态

当进程已分配到除处理器(CPU)以外的所有必要资源后,只要再获得处理器就可以立即执行,这时进程的状态称为就绪状态。在一个系统里,可以有多个进程同时处于就绪状态通常把这些就绪进程排成一个或多个队列,称为就绪队列。

(2)执行状态

处于就绪状态的进程一且获得了处理器,就可以运行,进程状态也就处于执行状态。在单处理器系统中,只能有一个进程处于执行状态,在多处理器系统中,则可能有多个进程处于执行状态。

(3)阻塞状态

正在执行的进程因为发生某些事件(如请求输入输出、中请额外空间等)而暂停运行,这种受阻暂停的状态称为阻塞状态,也可以称为等待状态。通常将处于阻塞状态的进程排成一个队列,称为阻塞队列。在有些系统中,也会按阻塞原因的不同将处于阻塞状态的进程排成多个队列。
除了进程的3种基本状态外,在很多系统为了更好地描述进程的状态变化,又增加了两种状态。

(4)新状态

当一个新进程刚刚建立,还未将其放入就绪队列的状态,称为新状态。例如一个人刚开始接受教育,此时就可以称其处于于新状态。

(5)终止状态

当一个进程己经正常结東或异常结東,操作作系统已将其从系统队列中移出,但尚未撤消,这时称为终止状态。


3.1.3进程状态之间的装换

进程状态之间转换图如下:
在这里插入图片描述


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.进程控制原语

系统根据各个进程的状态,建立不同索引表,例如就绪索引表、阻塞索引表等。并把各个索引表在主存的首地址记录在主存中的专用单元里,也可以称为表指针。在每个索引表的表目中,记录着具有相同状态的各个PCB在表中的地址。

(1)创建原语。用于为一个进程分配工作区和PCB,并初始化PCB,进程处于就绪态。
(2)撤销原语。用于一个进程完成后,收回它的工作区和PCB。
(3)阻塞原语。用于进程在运行过程中发生等待时间时,将进程改为阻塞态。
(4)唤醒原语。用于当进程等待的事件发生时,将进程改为就绪态。


3.3进程的创建与撤消

1.进程的创建

一旦操作系统发现了要求创建进程的事件后,便调用进程创建原按下列步骤创建一个新进程。
①为新进程分配惟一的进程标识符,并从PCB队列中申请一个空闲PCB。
②为新进程的程序和数据,以及用户栽分配相应的主存空间及其他必要分配资源。
③初始化PCB中的相应信息,如标识信息、处理器倌息、进程控制倌息等
④如果就绪队列可以接纳新进程,便将新进程加入到就绪队列中。

2.进程的撤销
一旦操作系统发现了要求终止进程的事件后,便调用进程终止原语按下列步骤终止指定的进程。
①根据被终止进程的标识符,从PCB集合中检索该进程的PCB,读出进程状态。
②若该进程处于执行状态,则立即终止该进程的执行。
③若该进程有子孙进程,还要将其子孙进程终止。
④将该进程所占用的资源回收,归还给其父进程或操作系统。
⑤将被终止进程的PCB从所在队列中移出,并撇销该进程的PCB。


3.4进程的阻塞与唤醒

1.进程的阻塞
一旦操作系统发现了要求阻塞进程的事件后,便调用进程阻塞原语,按下列步骤阻塞指定的进程。

①立即停止执行该进程。
②修改进程控制块中的相关信息。把进程控制块中的运行状态由“执行”状态改为“阻塞”状态,并填入等待的原因,以及进程的各种状态信息。
③把进程控制块插入到阻塞队列。根据阻塞队列的组织方式插入阻塞队列中。
④待调度程序重新调度,运行就绪队列中的其他进程。

2.进程的唤醒
一且操作系统发现了要求唤醒进程的事件后,便调用进程唤醒原语,按下列步骤唤醒指定的进程。

①从阻塞队列中找到该进程。
②修改该进程控制块的相关内容。把阻塞状态改为就绪状态,删除等待原因等。
③把进程控制块插入到就绪队列中。
按照就绪队列的组织方式,把被唤醒的进程的进程控制块插入到就绪队列中。


4.实验要求

1.必须独立完成程序的调试和运行,记录运行结果并对结果进行分析;
2.分析程序所定义使用的数据结构;
3.分析程序的结构,并画出程序流程图;
4.撰写分析报告


2.数据类型定义:

struct type
{
	int pid;
	int youxian;
	int daxiao;
	int zhuangtai;//标识进程状态,0-不在内存,1-在内存,2-阻塞
	char info[10];
};

struct type neicun[20];
	int sum = 0;
	int zuse = 0;
	int pid = 0;
	int flag = 0;

3.流程图:
在这里插入图片描述

完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct type
{
	int pid;
	int youxian;
	int daxiao;
	int zhuangtai;//标识进程状态,0-不在内存,1-在内存,2-阻塞
	char info[10];
};

struct type neicun[20];
	int sum = 0;
	int zuse = 0;
	int pid = 0;
	int flag = 0;

void create()
{
	if(sum >= 20)
	{
		printf("\n内存已满,请先唤醒或杀死进程n");
	}
	else	
	{	
		int i;
		for(i = 0; i < 20; i++)
		{
			if(neicun[i].zhuangtai == 0)
			break;
		}
		printf("\n请输入新进程pid\n");
		scanf("%d", &(neicun[i].pid));
		
		for(int j = 0; j < i; j++)	
		{
			if(neicun[j].pid == neicun[i].pid)
			{
				printf("\n该进程已存在n");	
				return;
			}	
		}//定位,找到可以还未创建的进程
		
		printf("\n请输入新进程优先级\n");
		scanf("%d", &(neicun[i].youxian));
		printf("\n请输入新进程大小\n");
		scanf("%d", &(neicun[i].daxiao));
		printf("\n请输入新进程内容\n");
		scanf("%s", &(neicun[i].info));
		
		//创建进程,使标记位为1
		neicun[i].zhuangtai = 1;
		sum++;
	}
}	

void run()
{ 
	for(int i  = 0; i < 20; i++)
	{
		if(neicun[i].zhuangtai == 1)
		{
			//输出运行进程的各个属性值
			printf("\npid=%d\t",neicun[i].pid);
			printf("youxian=%d\t",neicun[i].youxian);
			printf("daxiao=%d\t",neicun[i].daxiao);
			printf("zhuangtai=%d\t",neicun[i].zhuangtai);
			printf("info=%s\t",neicun[i].info);
			flag = 1;
		}	
	}	
	
	if(!flag)
	{
		printf("\n当前没有运行进程\n");
	}	
}

void zusetai()
{
	if(!sum)
	{
		printf("当前没有运行进程\n");
		return;
	}
	printf("\n输入阻塞进程的PID值");
	scanf("%d", &pid);
	
	for(int i = 0; i < 20; i++)
	{
	//定位,找到所要唤醒的进程,根据其状态做相应处理
		if(pid == neicun[i].pid)
		{
			if(neicun[i].zhuangtai == 1)
			{
				neicun[i].zhuangtai = 2;
				zuse++;
				printf("\n已经成功阻塞进程\n");
			}
			else 
			if(neicun[i].zhuangtai == 0)
			{
				printf("\n要阻塞的进程不存在\n");
			}	
		else 
		{
			printf("\n要阻塞的进程已被阻塞\n");
		}
			flag = 1; 
			break;
		}
		
		} //找不到,则说明进程不存在
		
		if(flag == 0)
		{
			printf("\n要阻塞的进程不存在\n");
		}
}

void kill()
{
	if(!sum)
	{
		printf("当前没有运行进程\n"); 
		return;
	}
	printf("\n输入杀死进程的工PID值");
	scanf("%d", &pid);
	
	for(int i = 0; i < 20; i++)
	{
	//定位,找到所要杀死的进程,根据其状态做相应处理
		if(pid == neicun[i].pid)
		{
			if(neicun[i].zhuangtai == 1)
			{
				neicun[i].zhuangtai = 0;
				sum--;
				printf("\n已成功杀死进程\n");
			}
		else 
			if(neicun[i].zhuangtai == 0)
			{
				printf("\n:要杀死的进程不存在\n");
			}
		else
		{ 
			printf("\n要杀死的进程已被阻塞\n");
		} 
			flag = 1;
			break;
		}
	}//找不到,则说明进程不存在

	if(!flag)
	{
		printf("\n要杀死的进程不存在\n");
	}	
}

void huanxing()
{ 
	if(sum == 0) 
	{ 
		printf("当前没有运行的进程\n");
		return;
	}
	if(zuse == 0)  /*guaqi       =       0,表示没有挂起的进程*/
	{
		printf("\n当前没有唤醒的进程\n");
		return;
	}
		printf("\n请输入要唤醒的进程的pid值:\n");
		scanf("%d",&pid);
	
	for(int i = 0; i < 20; i++)
	{
		if(pid == neicun[i].pid)
		{
			flag = false;
	      	if(neicun[i].zhuangtai == 2)
	       	{
				neicun[i].zhuangtai = 1; /*将该进程的状态设为挂起*/
		        zuse--;
		        sum++;
		        printf("\n该进程已成功唤醒\n" );
	        }
	       else  
		   if(neicun[i].zhuangtai == 0)
		   {
		   		printf("\n要唤醒的进程不存在\n" );
		   } 
	       else 
	       {
		   		printf("\n要唤醒的进程已经在内存中\n" );
		   } 
		   flag = 1;
			break;
	    }
	}
		if(!flag)
	{
		printf("\n要唤醒的进程不存在\n");
	}	
} 

int main()
{
	int n = 1;
	int num;
	//一开始所有进程都不在内存中
	for(int i = 0;i < 20; i++)
	{
		neicun[i].zhuangtai = 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", &num);
		
		switch(num)
		{
		case 1:
			create();
			break;
		case 2:
			run();
			break;
		case 3:
			zusetai();
			break;
		case 4:
			kill();
			break;
		case 5:
			huanxing();
			break;
		case 6:
			exit(0);
			default:
				n = 0;
		}
		flag = 0;//恢复标记
	}
}

5.结果分析


1.运行创建三个进程:如下图所示

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

2.查看进程运行状态如下图所示:
在这里插入图片描述

3阻塞一个进程如下图所示:

在这里插入图片描述

4唤醒进程1如下图所示:
在这里插入图片描述

5.杀死进程2如下图所示:

在这里插入图片描述

6:

  1. 创建原来有的进程如下图所示:
    在这里插入图片描述

  2. 唤醒正在运行的进程如下图所示:
    在这里插入图片描述

  3. 唤醒没有创建的进程如下图所示:
    在这里插入图片描述

  4. 阻塞进程1,杀死进程1,如下图所示:
    在这里插入图片描述

  5. 当进程都不存在时:杀死进程如下图所示
    在这里插入图片描述

要唤醒进程如下图所示:

在这里插入图片描述

阻塞进程如下图所示:
在这里插入图片描述

实验二 进程管理   Windows所创建的每个进程都从调用CreateProcess() API函数开始,该函数的任务是在对象管理器子系统内初始化进程对象。每一进程都以调用ExitProcess() 或TerminateProcess() API函数终止。通常应用程序的框架负责调用 ExitProcess() 函数。对于C++ 运行库来说,这一调用发生在应用程序的main() 函数返回之后。 1. 创建进程 CreateProcess() 调用的核心参数是可执行文件运行时的文件名及其命令行。表 2-1详细地列出了每个参数的类型和名称。   表2-1 CreateProcess() 函数的参数 参数名称 使用目的 LPCTSTR lpApplivationName 全部或部分地指明包括可执行代码的EXE文件的文件名 LPCTSTR lpCommandLine 向可执行文件发送的参数 LPSECURIITY_ATTRIBUTES lpProcessAttributes 返回进程句柄的安全属性。主要指明这一句柄是否应该由其他子进程所继承 LPSECURIITY_ATTRIBUTES lpThreadAttributes 返回进程的主线程的句柄的安全属性 BOOL bInheritHandle 一种标志,告诉系统允许新进程继承创建进程的句柄 DWORD dwCreationFlage 特殊的创建标志 (如CREATE_SUSPENDED) 的位标记 LPVOID lpEnvironment 向新进程发送的一套环境变量;如为null值则发送调用者环境 LPCTSTR lpCurrentDirectory 新进程的启动目录 STARTUPINFO lpStartupInfo STARTUPINFO结构,包括进程的输入和输出配置的详情 LPPROCESS_INFORMATION lpProcessInformation 调用的结果块;发送新应用程序的进程和主线程的句柄和ID   可以指定第一个参数,即应用程序的名称,其中包括相对于当前进程的当前目录的全路径或者利用搜索方法找到的路径;lpCommandLine参数允许调用者向新应用程序发送数据;接下来的三个参数与进程和它的主线程以及返回的指向该对象的句柄的安全性有关。 然后是标志参数,用以在dwCreationFlags参数中指明系统应该给予新进程什么行为。经常使用的标志是CREATE_SUSPNDED,告诉主线程立刻暂停。当准备好时,应该使用ResumeThread() API来启动进程。另一个常用的标志是CREATE_NEW_CONSOLE,告诉新进程启动自己的控制台窗口,而不是利用父窗口。这一参数还允许设置进程的优先级,用以向系统指明,相对于系统中所有其他的活动进程来说,给此进程多少CPU时间。 接着是CreateProcess() 函数调用所需要的三个通常使用缺省值的参数。第一个参数是lpEnvironment参数,指明为新进程提供的环境;第二个参数是lpCurrentDirectory,可用于向主创进程发送与缺省目录不同的新进程使用的特殊的当前目录;第三个参数是STARTUPINFO数据结构所必需的,用于在必要时指明新应用程序的主窗口的外观。 CreateProcess() 的最后一个参数是用于新进程对象及其主线程的句柄和ID的返回值缓冲区。以PROCESS_INFORMATION结构中返回的句柄调用CloseHandle() API函数是重要的,因为如果不将这些句柄关闭的话,有可能危及主创进程终止之前的任何未释放的资源。 2. 正在运行的进程 如果一个进程拥有至少一个执行线程,则为正在系统中运行的进程。通常,这种进程使用主线程来指示它的存在。当主线程结束时,调用ExitProcess() API函数,通知系统终止它所拥有的所有正在运行、准备运行或正在挂起的其他线程。当进程正在运行时,可以查看它的许多特性,其中少数特性也允许加以修改。 首先可查看的进程特性是系统进程标识符 (PID) ,可利用GetCurrentProcessId() API函数来查看,与GetCurrentProcess() 相似,对该函数的调用不能失败,但返回的PID在整个系统中都可使用。其他的可显示当前进程信息的API函数还有GetStartupInfo()和GetProcessShutdownParameters() ,可给出进程存活期内的配置详情。 通常,一个进程需要它的运行期环境的信息。例如API函数GetModuleFileName() 和GetCommandLine() ,可以给出用在CreateProcess() 中的参数以启动应用程序。在创建应用程序时可使用的另一个
操作系统进程调度是计算机操作系统中的一个重要部分,它负责协调和管理系统中的各个进程,以实现资源的有效利用和任务的高效执行。在C语言行操作系统进程调度实验,可以通过模拟不同的调度算法来理解和掌握进程调度的原理和实现过程。 首先,可以使用C语言编写一个简单的程序,模拟进程创建、就绪、运行和结束等状态。通过定义进程控制块(PCB)、进程队列等数据结构,以及编写相应的进程管理函数,来实现对进程的管理和调度。例如,可以编写函数来创建进程、将进程加入就绪队列、根据调度算法选择下一个要执行的进程等。 其次,可以选择不同的调度算法来实验,如先来先服务(FCFS)、最短作业优先(SJF)、时间片轮转(RR)等。针对不同的调度算法,通过C语言实现相应的调度函数,并在模拟程序中行调用,观察不同算法对进程执行顺序和响应时间的影响。 最后,可以通过对进程调度实验的结果行分析和比较,来深入理解各种调度算法的优缺点,以及在不同场景下的适用性。同时,也可以通过一步的实验和优化,来改模拟程序,增加更多的实际场景和特性,以更好地理解和应用操作系统进程调度的相关知识。 通过C语言行操作系统进程调度实验,可以帮助我们更深入地理解和掌握操作系统的核心概念和原理,为今后的系统设计和开发打下坚实的基础。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

又菜又爱巻

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

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

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

打赏作者

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

抵扣说明:

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

余额充值