操作系統實驗一:進程管理(含成功運行C語言源代碼)

目錄

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

1.實驗目的

2.實驗內容 

3.實驗準備 

3.1.1進程的含義

3.1.2進程的狀態

3.1.3進程狀態之間的轉換

3.2 進程控制塊PCB

3.2.1進程控制塊的作用

3.2.2進程控制塊的內容

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進程的結束(殺死)

 1 //結束(殺死)進程 
 2 void kill(){
 3     if(!shumu){
 4         printf("當前沒有運行進程!\n");
 5         return;
 6     }
 7     printf("\n輸入殺死進程的ID值");
 8     scanf("%d",&pid);
 9     for(int i=0;i<20;i++){
10         //定位,找到所要殺死的進程,根據其狀態做出相應處理
11         if(pid==neicun[i].pid){
12             neicun[i].zhuangtai = 0;
13             shumu--;
14             printf("\n已經成功殺死進程\n");
15         }
16         else if(neicun[i].zhuangtai==0) printf ("\n要殺死的進程不存在\n");
17         else printf("\n要殺死的進程已被掛起\n");
18         flag=1;
19         break;
20     } 
21     //找不到,則說明進程不存在
22     if(!flag) printf("\n 要殺死的進程不存在\n");
23 
24 } 

 

4.1.5進程的喚醒

 1 //喚醒進程
 2 void huanxing(){
 3     if (!shumu) {
 4         printf("當前沒有運行進程\n");
 5         return;
 6     }
 7     if(!guaqi){ 
 8         printf("\n當前沒有掛起進程\n");
 9         return;
10     }
11     printf("\n輸入進程pid:\n");
12     scanf ("%d",&pid);
13     for (int i=0; i<20;i++) {
14         //定位,找到所要殺死的進程,根據其狀態做相應處理
15         if (pid==neicun[i].pid) {
16             flag=false;
17             if(neicun[i].zhuangtai==2){
18                 neicun[i].zhuangtai=1;
19                 guaqi--;
20                 printf ("\n已經成功喚醒進程\n");
21             }
22             else if(neicun[i].zhuangtai==0) printf("\n要喚醒的進程不存在\n");
23             else printf("\n要喚醒的進程已被掛起\n");
24             break;
25         }
26     }
27     //找不到,則說明進程不存在
28     if(flag) printf("\n要喚醒的進程不存在\n");
29 }

4.1.6 mian()函數

 1 //主函數 
 2 int main()
 3 {
 4     int n = 1;
 5     int num;
 6     //一開始所有進程都不在內存中 
 7     for(int i=0;i<20;i++)
 8         neicun[i].zhuangtai = 0;
 9     while(n){
10         printf("\n******************************************");
11         printf("\n*        進程演示系統            *");
12         printf("\n******************************************");
13         printf("\n*1.創建新的進程        2.查看運行進程      *");
14         printf("\n*3.換出某個進程        4.殺死運行進程      *");
15         printf("\n*5.喚醒某個進程        6.退出系統      *");
16         printf("\n******************************************");
17         printf("\n請選擇(1~6)\n");
18         scanf("%d",&num);
19         switch(num){
20             case 1: create();break;
21             case 2: run();break;
22             case 3: huanchu(); break;
23             case 4: kill();break;
24             case 5: huanxing(); break;
25             case 6: printf("已退出系統");exit(0);
26             default: printf("請檢查輸入數值是否在系統功能中1~6");n=0;
27         }
28         flag = 0;//恢復標記  
29     } 
30     return 0;
31 } 


5.運行結果截圖

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

(圖2–選擇功能1 創建新進程)

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

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

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

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

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

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

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


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

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