【操做系統】關於操做系統你不得不知的那些實驗--進程管理

目錄

操做系統實驗一:進程管理算法

1.實驗目的數據結構

2.實驗內容 併發

3.實驗準備 函數

3.1.1進程的含義學習

3.1.2進程的狀態spa

3.1.3進程狀態之間的轉換操作系統

3.2 進程控制塊PCB.net

3.2.1進程控制塊的做用指針

3.2.2進程控制塊的內容code

3.2.3進程控制塊(PCB)的組織形式

3.2.4進程控制原語

3.3進程的建立與撤銷   *重點

3.3.1進程的建立

3.3.2進程的撤銷

3.4進程的阻塞與喚醒

3.4.1進程的阻塞

3.4.2進程的喚醒

4.代碼實現

4.1代碼分解介紹

5.運行結果截圖

(本文知識點較多,如時間較多能夠詳細看看第3章的知識點;如時間很少可直接點上方目錄,直接看第4部分代碼實現來理解)


操做系統實驗一:進程管理


1.實驗目的

1.理解進程的概念,明確進程和程序的區別

2.理解併發執行的實質

3.掌握進程的建立、睡眠、撤銷等進程控制方法


2.實驗內容 

用C語言編寫程序,模擬實現建立新的進程;查看運行進程;換出某個進程;殺死運行進程等功能。


3.實驗準備 

如下將分別介紹

進程的概念,以及進程的各種狀態(就緒狀態執行狀態阻塞狀態);

進程控制塊PCB 做用內容信息

③進程的建立與撤銷 (🔺重點)

④進程的阻塞與喚醒(🔺重點)


3.1.1進程的含義

進程是程序在一個數據集上的運行過程,是系統資源分配和調度的一個獨立單位。一個程序在不一樣數據集上運行,乃至一個程序在一樣數據集上的屢次運行都是不一樣的進程。

3.1.2進程的狀態

一般狀況下,一個進程必須具備就緒執行阻塞三種基本狀態。

(1)就緒狀態

當進程已分配到處理器(CPU)之外的全部必要資源後,只要再得到處理器就能夠當即執行,此時進程的狀態稱爲就緒狀態

在一個系統裏,能夠有多個進程同時處於就緒狀態,一般把這些就緒進程排成一個或多個隊列,稱爲就緒隊列

(2)執行狀態

處於就緒狀態的進程一旦得到了處理器(分配有處理器資源),就能夠運行,進程狀態也就處於執行狀態。在單處理器系統中,只能有一個進程處於執行狀態,在多處理器系統中,則可能有多個進程處於執行狀態。

(3)阻塞狀態

正在執行的進程由於發生某些事件(如請求輸入輸出、申請額外空間等)而暫停運行,這種受阻暫停的狀態稱爲阻塞狀態,也能夠稱爲等待狀態。一般將處於阻塞狀態的進程排成一個隊列,稱爲阻塞隊列。在有些系統中,也會按阻塞緣由的不一樣將處於阻塞狀態的進程排成多個隊列。

拓展

除了進程的3種基本狀態外,在不少系統爲了更好地描述進程的狀態變化,又增長了兩種狀態。
     I新狀態
    當一個新進程剛剛創建,還未將其放入就緒隊列的狀態,稱爲新狀態。(例如一個,人剛開始接受教育,此時就能夠稱其處於新狀態)
     II終止狀態
    當一個進程已經正常結束或異常結束,操做系統已將其從系統隊列中移出,但還沒有撒消,這時稱爲終止狀態


3.1.3進程狀態之間的轉換


3.2 進程控制塊PCB

 

3.2.1進程控制塊的做用

進程控制塊是構成進程實體的重要組成部分,是操做系統中最重要的記錄型數據,在進程控制塊PCB中記錄了操做系統所須要的、用於描述進程狀況及控制進程運行所須要的所有信息。經過PCB,可以使得原來不能獨立運行的程序(數據),成爲一個能夠獨立運行的基本單位,一個可以併發執行的進程。換句話說,在進程的整個生命週期中,操做系統都要經過進程的PCB來對併發執行的進程進行管理和控制,進程控制塊是系統對進程控制採用的數據結構,系統是根據進程的PCB而感知進程是否存在。因此,進程控制塊是進程存在的惟一標誌。當系統建立一個新進程時,就要爲它創建一個PCB;進程結束時,系統又回收其PCB,進程也隨之消亡。

3.2.2進程控制塊的內容

進程控制塊主要包括如下四個方面的內容:

(1)進程標識信息

----進程標識符用於標識一個進程,一般又分外部標識符和內部標識符兩種。

(2)說明信息

----說明信息是有關進程狀態等一些與進程調度有關的信息,它包括:①進程狀態  ②進程優先權  ③與進程調度所需的其餘信息  ④阻塞事件

(3)現場信息(處理器狀態信息)

----現場信息是用於保留進程存放在處理器中的各類信息。主要由處理器內的各個寄存器的內容組成。尤爲是當執行中的進程暫停時,這些寄存器內的信息將被保存在PCB裏,當該進程得到從新執行時,能從上次中止的地方繼續執行

(4)管理信息(進程控制信息)

進程控制信息主要分四方面:

程序和數據的地址 它是指該進程的程序和數據所在的主存和外存地址再次執行時,可以找到程序和數據
進程同步和通訊機制 它是指實現進程同步和進程通訊時所採用的機制、指針、信號量等
資源清單 該清單中存放有除了CPU之外,進程所需的所有資源和已經分配到的資源
連接指針 它將指向該進程所在隊列的下一個進程的PCB的首地址

3.2.3進程控制塊(PCB)的組織形式

在一個系統中,一般擁有數十個、數百個乃至數千個PCB,爲了能對它們進行有效的管理,就必須經過適當的方式將它們組織起來,日前經常使用的組織方式有連接方式和索引方式兩種。.
(1)連接方式

把具備相同狀態的PCB,用連接指針連接成隊列,如就緒隊列、阻塞隊列和空閒隊列等。就緒隊列中的PCB將按照相應的進程調度算法進行排序。而阻塞隊列也能夠根據阻塞緣由的不一樣,將處於阻塞狀態的進程的PCB,排成等待I/O隊列、等待主存隊列等多個隊列。此外,系統主存的PCB區中空閒的空間將排成空閒隊列,以方便進行PCB的分配與回收。
(2)索引方式

系統根據各個進程的狀態,創建不一樣索引表,例如就緒索引表、阻塞索引表等。並把各個索引表在主存的首地址記錄在主存中的專用單元裏,也能夠稱爲表指針。在每一個索引表的表目中,記錄着具備相同狀態的各個PCB在表中的地址。


3.2.4進程控制原語

原語是指具備特定功能的不可被中斷的過程。它主要用於實現操做系統的一 些專門控制操做。用於進程控制的原語有:

原語 做用
建立原語 用於爲一個進程分配工做區和創建PCB,該進程爲就緒狀態
撤銷原語 用於一個進程工做完後,收回它的工做區和PCB
阻塞原語 用於進程在運行過程當中發生等待事件時,把進程的狀態改成等待態
喚醒原語 用於當進程等待的事件結束時,把進程的狀態改成就緒態

3.3進程的建立與撤銷   *重點

3.3.1進程的建立

一旦操做系統發現了要求建立進程的事件後,便調用進程建立原按下列步驟建立一個新進程。

①爲新進程分配唯一的進程標識符, 並從PCB隊列中申請一個空閒PCB。

②爲新進程的程序和數據,以及用戶棧分配相應的主存空間及其餘必要分配資源。

③初始化PCB中的相應信息,如標識信息、處理器信息、進程控制信息等。

④若是就緒隊列能夠接納新進程,便將新進程加入到就緒隊列中。

3.3.2進程的撤銷

一旦操做系統發現了要求終止進程的事件後,便調用進程終止原語按下列步驟終止指定的進程。

①根據被終止進程的標識符,從PCB集合中檢索該進程的PCB,讀出進程狀態。

②若該進程處於執行狀態,則當即終止該進程的執行。

③若該進程有子孫進程,還要將其子孫進程終止。

④將該進程所佔用的資源回收,歸還給其父進程或操做系統。

⑤將被終止進程的PCB從所在隊列中移出,並撤銷該進程的PCB。


3.4進程的阻塞與喚醒

3.4.1進程的阻塞

一旦操做系統發現了要求阻塞進程的事件後,便調用進程阻塞原語,按下列步驟阻塞指定的進程。

①當即中止執行該進程。

②修改進程控制塊中的相關信息。把進程控制塊中的運行狀態由「執行」狀態改成「阻塞」狀態,並填入等待的緣由,以及進程的各類狀態信息。

③把進程控制塊插入到阻塞隊列。根據阻塞隊列的組織方式插入阻塞隊列中。

④待調度程序從新調度,運行就緒隊列中的其餘進程。

3.4.2進程的喚醒

一旦操做系統發現了要求喚醒進程的事件後,便調用進程喚醒原語,按下列步驟喚醒指定的進程。

①從阻塞隊列中找到該進程。

②修改該進程控制塊的相關內容。把阻塞狀態改成就緒狀態,刪除等待緣由等。

③把進程控制塊插入到就緒隊列中。

④按照就緒隊列的組織方式,把被喚醒的進程的進程控制塊插入到就緒隊列中。


4.代碼實現

可成功運行代碼以下👇

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct jincheng_type{   //進程狀態定義 
	int pid;          //進程 
	int youxian;     //進程優先級 
	int daxiao;      //進程大小 
	int zhuangtai;   //標誌進程狀態,0爲不在內存,1爲在內存,3爲掛起 
	char info[10];   //進程內容 
}; 
struct jincheng_type neicun[20];
int shumu=0,guaqi=0,pid,flag=0;

//建立進程 
void create(){      
	if(shumu>=20) printf("\n內存已滿,請先換出或結束進程\n");   //內存容量大小設置爲20
	else{
		int i;
		printf("**當前默認一次性建立5個進程,內存容量20**"); 
		for(i=0;i<5;i++)  {              //默認一次建立5個進程
		//定位,找到能夠還未建立的進程
		if(neicun[i].zhuangtai==1) break;  //若是找到的進程在內存則結束 ,初始設置都不在內存中(main函數中設置狀態爲0)
		printf("\n請輸入新進程pid\n");
		scanf("%d",&(neicun[i].pid));
		for(int j=0;j<i;j++)
			if(neicun[i].pid==neicun[j].pid){
				printf("\n該進程已存在\n");
				return;
			}
		printf("請輸入新進程優先級\n");
		scanf("%d",&(neicun[i].youxian)); 
		printf("請輸入新進程大小\n");
		scanf("%d",&(neicun[i].daxiao)); 
		printf("請輸入新進程內容\n");
		scanf("%s",&(neicun[i].info)); 
		//建立進程,使標記位爲1
		neicun[i].zhuangtai=1;
		printf("進程已成功建立!"); 
		shumu++;
		}
	}
}

//進程運行狀態檢測 
void run(){   
	printf("運行進程信息以下:"); 
	for(int i=0;i<20;i++){
		if(neicun[i].zhuangtai==1){
			//若是進程正在運行,則輸出此運行進程的各個屬性值	
			printf("\n  pid=%d ",neicun[i].pid); 
			printf(" youxian=%d  ",neicun[i].youxian); 
			printf(" daxiao=%d  ",neicun[i].daxiao); 
			printf(" zhuanbgtai=%d  ",neicun[i].zhuangtai); 
			printf(" info=%s  ",neicun[i].info); 
			flag=1;
		} 
	}
	if(!flag) 
	printf("\n當前沒有運行進程!\n");
}

//進程換出
void huanchu(){
	if(!shumu){
		printf("當前沒有運行進程!\n");
		return;
	}
	printf("\n請輸入換出進程的ID值");
	scanf("%d",&pid);
	for(int i=0;i<20;i++){
		//定位,找到要換出的進程,根據其狀態進行相應處理
		if(pid==neicun[i].pid) {
			if(neicun[i].zhuangtai==1){
				neicun[i].zhuangtai==2;
				guaqi++;
				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(!shumu){
		printf("當前沒有運行進程!\n");
		return;
	}
	printf("\n輸入殺死進程的ID值");
	scanf("%d",&pid);
	for(int i=0;i<20;i++){
		//定位,找到所要殺死的進程,根據其狀態作出相應處理
		if(pid==neicun[i].pid){
			neicun[i].zhuangtai = 0;
			shumu--;
			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 (!shumu) {
		printf("當前沒有運行進程\n");
		return;
	}
	if(!guaqi){ 
		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;
				guaqi--;
				printf ("\n已經成功喚醒進程\n");
			}
			else if(neicun[i].zhuangtai==0) printf("\n要喚醒的進程不存在\n");
			else printf("\n要喚醒的進程已被掛起\n");
			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)\n");
		scanf("%d",&num);
		switch(num){
			case 1: create();break;
			case 2: run();break;
			case 3: huanchu(); break;
			case 4: kill();break;
			case 5: huanxing(); break;
			case 6: printf("已退出系統");exit(0);
			default: printf("請檢查輸入數值是否在系統功能中1~6");n=0;
		}
		flag = 0;//恢復標記  
	} 
	return 0;
}

4.1代碼分解介紹

源程序中一共構造了5個函數方法來實現進程的建立、(運行進程的)顯示、換出、結束(殺死)與喚醒。

4.1.1進程的建立

//建立進程 
void create(){      
	if(shumu>=20) printf("\n內存已滿,請先換出或結束進程\n");   //內存容量大小設置爲20
	else{
		int i;
		printf("**當前默認一次性建立5個進程,內存容量20**"); 
		for(i=0;i<5;i++)  {              //默認一次建立5個進程
		//定位,找到能夠還未建立的進程
		if(neicun[i].zhuangtai==1) break;  //若是找到的進程在內存則結束 ,初始設置都不在內存中(main函數中設置狀態爲0)
		printf("\n請輸入新進程pid\n");
		scanf("%d",&(neicun[i].pid));
		for(int j=0;j<i;j++)
			if(neicun[i].pid==neicun[j].pid){
				printf("\n該進程已存在\n");
				return;
			}
		printf("請輸入新進程優先級\n");
		scanf("%d",&(neicun[i].youxian)); 
		printf("請輸入新進程大小\n");
		scanf("%d",&(neicun[i].daxiao)); 
		printf("請輸入新進程內容\n");
		scanf("%s",&(neicun[i].info)); 
		//建立進程,使標記位爲1
		neicun[i].zhuangtai=1;
		printf("進程已成功建立!"); 
		shumu++;
		}
	}
}

(默認設置內存容量大小爲20,一次性建立5個進程;進程建立時首先檢測進程狀態,若爲1則代表該進程此前已在內存中了,不可再建立)

4.1.2進程的顯示(運行狀態檢測)

//進程運行狀態檢測 
void run(){   
	printf("運行進程信息以下:"); 
	for(int i=0;i<20;i++){
		if(neicun[i].zhuangtai==1){
			//若是進程正在運行,則輸出此運行進程的各個屬性值	
			printf("\n  pid=%d ",neicun[i].pid); 
			printf(" youxian=%d  ",neicun[i].youxian); 
			printf(" daxiao=%d  ",neicun[i].daxiao); 
			printf(" zhuanbgtai=%d  ",neicun[i].zhuangtai); 
			printf(" info=%s  ",neicun[i].info); 
			flag=1;
		} 
	}
	if(!flag) 
	printf("\n當前沒有運行進程!\n");
}

4.1.3進程的換出

//進程換出
void huanchu(){
	if(!shumu){
		printf("當前沒有運行進程!\n");
		return;
	}
	printf("\n請輸入換出進程的ID值");
	scanf("%d",&pid);
	for(int i=0;i<20;i++){
		//定位,找到要換出的進程,根據其狀態進行相應處理
		if(pid==neicun[i].pid) {
			if(neicun[i].zhuangtai==1){
				neicun[i].zhuangtai==2;
				guaqi++;
				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"); 
}

4.1.4進程的結束(殺死)

//結束(殺死)進程 
void kill(){
	if(!shumu){
		printf("當前沒有運行進程!\n");
		return;
	}
	printf("\n輸入殺死進程的ID值");
	scanf("%d",&pid);
	for(int i=0;i<20;i++){
		//定位,找到所要殺死的進程,根據其狀態作出相應處理
		if(pid==neicun[i].pid){
			neicun[i].zhuangtai = 0;
			shumu--;
			printf("\n已經成功殺死進程\n");
		}
		else if(neicun[i].zhuangtai==0) printf ("\n要殺死的進程不存在\n");
		else printf("\n要殺死的進程已被掛起\n");
		flag=1;
		break;
	} 
	//找不到,則說明進程不存在
	if(!flag) printf("\n 要殺死的進程不存在\n");

}

4.1.5進程的喚醒

//喚醒進程
void huanxing(){
	if (!shumu) {
		printf("當前沒有運行進程\n");
		return;
	}
	if(!guaqi){ 
		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;
				guaqi--;
				printf ("\n已經成功喚醒進程\n");
			}
			else if(neicun[i].zhuangtai==0) printf("\n要喚醒的進程不存在\n");
			else printf("\n要喚醒的進程已被掛起\n");
			break;
		}
	}
	//找不到,則說明進程不存在
	if(flag) printf("\n要喚醒的進程不存在\n");
}

4.1.6 mian()函數

//主函數 
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)\n");
		scanf("%d",&num);
		switch(num){
			case 1: create();break;
			case 2: run();break;
			case 3: huanchu(); break;
			case 4: kill();break;
			case 5: huanxing(); break;
			case 6: printf("已退出系統");exit(0);
			default: printf("請檢查輸入數值是否在系統功能中1~6");n=0;
		}
		flag = 0;//恢復標記  
	} 
	return 0;
}

5.運行結果截圖

(圖1--初始運行狀態界面)

(圖2--選擇功能1 建立新進程)

源程序一次性建立5個進程,內存容量爲20。

(圖3--依次輸入建立進程的pid、優先級、大小、內容)

(圖4--選擇功能2 顯示當前運行的進程)

(圖5--換出進程  輸入要換出的進程pid,換出後提示進程已換出)

(圖6--殺死進程  輸入要結束的進程pid,以後執行操做殺死此進程,再次查看運行進程時能夠看到進程1已不在運行進程列)

(圖7--喚醒進程  輸入要喚醒的進程pid,以後該進程將被掛起)

(圖8--退出系統  輸入6,選擇退出系統功,系統結束運行)


本文爲課程實驗記錄,參考學校實驗教材書籍,重在學習交流。

碼字不易,走過路過點個贊👍吧😉(❁´◡`❁)

相關文章
相關標籤/搜索