Linux中孤兒進程,殭屍進程,進程回收wait、waitpid函數



簡 述: 這篇繼承上一篇,先要自己梳理清楚一下進程相關的知識, 上上一篇的虛擬地址空間和進程控制塊 PCB ,以及上一篇的進程相關知識,帶着思考來學習 孤兒進程、殭屍進程、 以及進程回收 wait()waitpid()相關的概念。


編程環境:

💻: uos20 📎 gcc/g++ 8.3 📎 gdb8.0

💻: MacOS 10.14 📎 gcc/g++ 9.2 📎 gdb8.3


孤兒進程:

  • 父進程創建子進程

  • 父進程死了,子進程還活着,該進程叫孤兒進程(Orphan Process)

  • 在傳統的 Linux 系統中,孤兒進程被 init 進程領養,init 進程成爲孤兒進程的父親

    • init 進程這樣做,主要是爲了釋放子進程佔用的系統資源。
    • 因爲進程結束之後,能夠釋放用戶控空間;但是釋放不了系統空間的 PCB,這塊資源必須由父進程釋放。
  • 寫一個代碼例子:

    #include <stdio.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[])
    {
        pid_t pid = fork();  //創建子進程
    
        if (pid == 0) { //存活時間大於 1s(此時父進程已經執行完成銷燬), 爲孤兒子進程
            sleep(1);
            printf("this is a child process, pid = %d  ppid = %d\n", getpid(), getppid());  
        } else if (pid > 0) {  //父進程
            printf("this is a parent process, pid = %d  ppid = %d\n", getpid(), getppid());  //執行完後,父進程就結束了
        }
    
        return 0;
    }
    
  • 代碼分析:

    當父進程創建子進程之後,很快的就執行完後就結束銷燬自己(小於 1s 內);因爲子進程的生存時間大於 1s,故此時它爲孤兒進程(下圖可證明:此時其父進程號是 1 而非 17441),然後被系統的 init 進程領養,幫助銷燬子進程的 PCB;

  • 運行結果:


殭屍進程:

  • 父進程創建子進程

  • 子進程死了,父進程還活着,且不去釋放子進程的 PCB,該子進程就變成了殭屍進程(Zombie Process)。

  • 殭屍進程是一個已經死掉的進程。

  • 寫一個代碼例子:

    #include <stdio.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[])
    {
        pid_t pid = fork();  //創建子進程
    
        if (pid == 0) { //(此時子進程已經執行完成銷燬), 而父進程沒有時間去銷燬這個子進程的 pcb,故爲殭屍進程
            printf("this is a child process, pid = %d  ppid = %d\n", getpid(), getppid());  
        } else if (pid > 0) {  //父進程
            while (true)
            {
                sleep(1);
               printf("this is a parent process, pid = %d  ppid = %d\n", getpid(), getppid());  //執行完後,父進程就結束了
            }
        }
    
        return 0;
    }
    
  • 代碼分析:

    父進程創建子進程之後,子進程在極短的時間內執行完所有代碼後,其用戶區的資源被釋放,其系統區的 pcb 等待其父進程來釋放;但是父進程一直在忙其它的時間,沒有做處理,釋放自己成的 PCB,在下圖左側還沒有運行 ctrl c 的時候,使用 ps 在管道里面查詢該子進程的號 5563,可以發現其已經被標註爲死亡的進程(其 Z+ 中的是單詞殭屍 Zombie 的首字母)。

  • 運行結果:


進程回收:

wait():

pid_t wait(int *stat_loc);
  • 作用:

    • 阻塞並且等待子進程退出
    • 回收子進程殘留的資源
    • 獲取子進程結束的狀態(退出原因)
    • 每次只能夠回收一個進程,調用一次 wait() 也只能回收一個進程
  • 參數:

    • WIFEXITED(status): 爲非 0 --> 進程正常退出
      • WEXITSTATUS(status): 如果這個宏爲真,使用此宏 --> 獲取進程退出狀態(exit / return) 的參數
    • WIFSIGNALED(status): 爲非 0 --> 進程異常終止
      • WTERMSIG(status): 如果這個宏爲真,使用此宏 --> 獲取使進程退出的那個信號的編號。
  • 返回值:

    • 成功:清理掉的子進程 ID
    • 失敗:-1(沒有子進程)
  • 寫一個代碼例子驗證:

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    int main(int argc, char *argv[])
    {
        printf("-----開始-----\n");
    
        pid_t pid = fork();  //創建子進程
    
        if (pid == 0) { //(此時子進程已經執行完成銷燬), 而父進程沒有時間去銷燬這個子進程的 pcb,故爲殭屍進程
            while (true) {
                sleep(2);
                printf("this is a child process, pid = %d  ppid = %d\n", getpid(), getppid());  
            }
        } else if (pid > 0) {  //父進程
            //sleep(1);
            
            int status = 0;
            pid_t wpid = wait(&status);  //回收子進程
    
            if (WIFEXITED(status))   //判斷是否正常退出
                printf("退出值是 val = %d\n", WEXITSTATUS(status));
    
            if (WIFSIGNALED(status))   //判斷是被信號殺死
                printf("通過信號退出 val = %d\n", WTERMSIG(status));
    
            printf("this is a parent process, pid = %d  ppid =  %d\n", getpid(), getppid());  //執行完後,父進程就結束了
        }
    
        printf("+++++結束+++++\n");
    
        return 9;  //若是註釋掉 12 和 15 行代碼,會顯示父父進程的 退出值是 9
    }
    
    • 代碼分析:

      代碼是在 MacOS(Unix) 系統上面運行的;

      若是註釋掉 12 和 15 行代碼,則會運行第一個宏,可以看到進程結束的返回值是 9 (人爲隨機指定的一個數值,return 9);運行結果見下面第一個圖。

      而不註釋掉 12 和 15 行代碼;運行程序後,執行 kill 進程號 殺死子進程, 則會執行第二個宏,顯示是被信號(15)所殺「若果是 Linux 運行,這裏會顯示信號是 9」;運行結果見下面第二個圖。

    • 運行截圖:


waitpid():

pid_t waitpid(pid_t pid, int *stat_loc, int options);
  • 作用:

    同 wait(),但是可以指定要回收的進程 pid 號,且能夠指定是阻塞還是非阻塞模式。

  • 參數:

    • pid:

      pid == -1;回收所有的子進程。與 wait 等效

      pid > 0; 回收指定 pid 的進程

      pid == 0; 回收當前組的所有子進程

      pid < -1; 回收進程的 pid 取反(加減號)

    • status:

      子進程的退出狀態,用法同 wait()

    • options:

      設置爲 WNOHANG,函數非阻塞,設置爲 0,函數阻塞

  • 返回值:

    > 0  //返回清理掉的子進程 ID
    -1   //無子進程
    =0   //參數 3 爲 WHOHANG,且子進程正在運行
    

下載地址:

10_orphan_zombie_process

歡迎 star 和 fork 這個系列的 linux 學習,附學習由淺入深的目錄。

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