Linux進程控制開發基礎——Linux進程狀態/fork()/waitpid()/wait()詳解

第一部分 Linux進程概述

       在Linux下進程被創建後可以有5種狀態。
在這裏插入圖片描述
在這裏插入圖片描述

關於Linux進程狀態不再贅述,參考下面這2個博客鏈接即可

不過有一些值得補充的說明:

  • 很多操作系統教科書將正在CPU上執行的進程定義爲RUNNING狀態、而將可執行但是尚未被調度執行的進程定義爲READY狀態,這兩種狀態在linux下統一爲 TASK_RUNNING狀態。

  • 進程狀態變遷
           進程自創建以後,狀態可能發生一系列的變化,直到進程退出。而儘管進程狀態有好幾種,但是進程狀態的變遷卻只有兩個方向——從TASK_RUNNING狀態變爲非TASK_RUNNING狀態、或者從非TASK_RUNNING狀態變爲TASK_RUNNING狀態
    (1)也就是說,如果給一個TASK_INTERRUPTIBLE狀態的進程發送SIGKILL信號,這個進程將先被喚醒(進入TASK_RUNNING狀態),然後再響應SIGKILL信號而退出(變爲TASK_DEAD狀態),並不會從TASK_INTERRUPTIBLE狀態直接退出。進程從非TASK_RUNNING狀態變爲TASK_RUNNING狀態,是由別的進程(也可能是中斷處理程序)執行喚醒操作來實現的。執行喚醒的進程設置被喚醒進程的狀態爲TASK_RUNNING,然後將其task_struct結構加入到某個CPU的可執行隊列中。於是被喚醒的進程將有機會被調度執行。
    (2)而進程從TASK_RUNNING狀態變爲非TASK_RUNNING狀態,則有兩種途徑:
           a. 響應信號而進入TASK_STOPED狀態、或TASK_DEAD狀態;
           b. 執行系統調用主動進入TASK_INTERRUPTIBLE狀態 (如nanosleep系統調用)、或TASK_DEAD狀態(如exit系統調用);或由於執行系統調用需要的資源得不到滿足,而進入TASK_INTERRUPTIBLE狀態或TASK_UNINTERRUPTIBLE狀態(如select系統調用)。顯然,這兩種情況都只能發生在進程正在CPU上執行的情況下。

  • 留意SIGSTOP、SIGCONT、SIGKILL信號對於進程狀態的影響。另外,像pause()(等待信號的出現才被喚醒)、wait()(等待子進程退出)等函數都會使得當前進程進入可中斷睡眠狀態。

第二部分 Fork()詳解

       在Linux中手動創建一個新進程的唯一方法是使用fork()函數。
       fork()函數用於從已存在的進程中創建一個新進程。新進程成爲子進程,原進程成爲父進程。子進程是父進程的一個複製品,它從父進程繼承了進程的整個地址空間,包括進程上下文、代碼段、進程堆棧、內存信息、打開的文件描述符、信號控制設定、進程優先級、進程組號、當前工作目錄、根目錄、資源限制和控制終端等。fork()出來的子進程除了進程標識符pid和父進程不一樣以外,其他的都和父進程相同。下面這張圖形象的描述了進程的地址空間。
在這裏插入圖片描述

關於fork()需要注意以下幾點
  • 父進程中的返回值是子進程的進程號, 而在子進程中返回0,因此可以通過返回值來判定當前進程是父進程還是子進程。當一個父進程創建多個子進程時(注意此時fork()一定要保證在父進程運行狀態下被調用),在代碼的不同調用位置可以拿到不同的子進程pid,可參見以下代碼,注意留意父進程創建多個子進程的fork()調用位置。
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
int main(){
    pid_t pid;
    pid = fork();
    if(pid < 0){
        printf("Fork1 error!\n");
        exit(1);
    }
    if(pid > 0){
        printf("the father(1) is running, pid is %d, ret value is %d\n", getpid(), pid);
        pid_t pid2 = fork();
        if(pid2 < 0){
            printf("Fork2 error!\n");
            exit(1);
        }
        if(pid2 > 0){
            printf("the father(2) is running, pid is %d, ret value is %d\n", getpid(), pid2);
            exit(0);
        }else if(pid2 == 0){
            printf("the second son is running, pid is %d, ret value is %d\n", getpid(), pid2);
            exit(0);
        }

    }else if(pid == 0){
        printf("the first son is running, pid is %d, ret value is %d\n", getpid(), pid);
        exit(0);
    }
    return 0;
}

       運行效果
在這裏插入圖片描述

第三部分 wait()/waitpid()詳解

(1)wait()和waitpid()區別
  • wait()用於使父進程阻塞等待,直到一個子進程結束或該進程接到了一個信號爲止。當該父進程沒有子進程或者是子進程結束時會立即返回。
  • waitpid()的作用與wait()相似,但是不一定要等待第一個結束的子進程,它有若干選項,比wait()更加靈活,比如提供一個非阻塞等待的版本。
(2)wait()函數
函數原型 pid_t wait(int *status);
所需頭文件 #include<sys/types.h>和#include<sys/wait.h>
成功 返回已結束運行的子進程的進程號
失敗 返回-1
參數說明
  • 參數status如果不是一個空指針,則終止進程的終止狀態就存放在statloc所指向的單元;
  • 參數status如果是一個空指針,則表示父進程不關心子進程的終止狀態。
(3) waitpid()函數
函數原型 pid_t waitpid(pid_t pid, int *status, int options);
所需頭文件 #include<sys/types.h>和#include<sys/wait.h>
成功 返回已結束運行的子進程的進程號
options爲WNOHANG時 沒有子進程退出則立即返回0
失敗 返回-1
參數說明
  • pid
pid>0 只等待進程ID等於pid的子進程,不管其它已經有多少子進程運行結束退出了,只要指定的子進程還沒有結束,waitpid就會一直等下去。
pid=-1 等待任何一個子進程退出,沒有任何限制,此時waitpid和wait的作用一模一樣。
pid=0 等待同一個進程組中的任何子進程,如果子進程已經加入了別的進程組,waitpid不會對它做任何理睬。
pid<-1 等待一個指定進程組中的任何子進程,這個進程組的ID等於pid的絕對值。
  • options
WNOHANG 若由pid指定的子進程未發生狀態改變(沒有結束),則waitpid()不阻塞,立即返回0
WUNTRACED 返回終止子進程信息和因信號停止的子進程信息
WCONTINUED 返回收到SIGCONT信號而恢復執行的已停止子進程狀態信息
0 這個時候跟wait()一樣阻塞等待
  • status
    同wait()
用法
  • 一個常見的用法是waitpid(pid, NULL, 0),這樣就可以阻塞等待進程號爲pid的子進程退出了。注意這個用法和wait()是不一樣的,後者只能等待任意一個子進程退出,而waitpid()可以指定等待具體哪個進程。

第四部分 關於返回值pid_t的補充說明

  • pid_t在頭文件types.h(sys/types.h)中定義。
  • pid_t是一個typedef定義類型。
    用它來表示進程id類型。
//sys/types.h:
typedef short pid_t;       /* used for process ids */

由此看出,上述定義的pid_t就是一個short類型變量,實際表示的是內核中的進程表的進程號索引。

  • 使用pid_t的一個好處——可移植性更好
    比如對於不同的平臺,可以typedef int pid_t,也可以typedef long pid_t
  • 事實上Linux內核源碼的數據類型基本都把基本類型進行了一次typedef的封裝。

參考文獻

https://blog.csdn.net/csdn_kou/article/details/81091191
《嵌入式Linux應用開發標準教程》 華清遠見嵌入式培訓中心 編著
《深入理解計算機系統》 Randal E.Bryant, David R.O’Hallaron編著

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章