目錄
1.實驗目的數據結構
2.實驗內容 併發
3.實驗準備 函數
3.1.2進程的狀態spa
3.1.3進程狀態之間的轉換操作系統
3.2 進程控制塊PCB.net
3.2.2進程控制塊的內容code
(本文知識點較多,如時間較多能夠詳細看看第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,選擇退出系統功,系統結束運行)
本文爲課程實驗記錄,參考學校實驗教材書籍,重在學習交流。
碼字不易,走過路過點個贊👍吧😉(❁´◡`❁)