UNIXC002 程式資源的回收、孤兒程式和殭屍程式

xueHui_發表於2020-12-31

程式的資源回收

  • 程式終止以後,如果父程式不來回收子程式的資源,相當於子程式的使用者態已經結束了,但是核心態沒有,子程式的PCB還在佔用著資源。這個PCD有時被稱為程式的殭屍(白白佔用系統的記憶體)。所以在子程式結束以後要及時的回收子程式的殭屍。

1. wait

在這裡插入圖片描述

1.1 可以使用以下 巨集檢測程式終止的原因

在這裡插入圖片描述

$ man 2 wait
....
       WIFEXITED(status)
              returns true if the child terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main().

       WEXITSTATUS(status)
              returns  the exit status of the child.  This consists of the least significant 8 bits of the status argument that the child specified
              in a call to exit(3) or _exit(2) or as the argument for a return statement in main().  This macro should be employed only  if  WIFEX‐
              ITED returned true.

       WIFSIGNALED(status)
              returns true if the child process was terminated by a signal.

       WTERMSIG(status)
              returns  the  number  of  the  signal  that caused the child process to terminate.  This macro should be employed only if WIFSIGNALED
              returned true.
      
....

1.2 程式碼示例

wait.c

#include <stdlib.h>
#include "t_stdio.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void){
    int s;
    //建立子程式
    pid_t pid = fork();
    if(pid==-1)E_MSG("fork", -1);
    if(pid == 0){
        //子程式執行程式碼
        printf("child process ... %d\n", getpid());
        getchar();
        // 終止子程式
        exit(-1);
    } else {
        // 父程式執行程式碼
        // wait 阻塞等待子程式結束, 回收子程式資源, 所以子程式執行完以後才會執行下面的printf的程式碼, 這樣就從之前的非同步,變成了現在的同步
        // s用來儲存子程式的退出狀態碼
        wait(&s);
        if(WIFEXITED(s)){   // WIFEXITED(s) 為真說明子程式正常退出,WEXITSTATUS(s)可以獲取子程式退出狀態碼
            printf("exit: %d\n", WEXITSTATUS(s));
        }
        if(WIFSIGNALED(s)){ // WIFSIGNALED(s) 為真說明程式是被訊號打斷的,  WTERMSIG(s) 可以獲取打斷程式訊號的編號
            printf("signum: %d\n", WTERMSIG(s));
        }
        printf("father process ...\n");
    }
    return 0;
}
  • 子程式正常退出
    在這裡插入圖片描述
  • 子程式被訊號打斷
    在這裡插入圖片描述

    在這裡插入圖片描述

2. waitpid

在這裡插入圖片描述

  • wait 是等待任意子程式,只要是子程式終止那麼就把它給回收
  • waitpid 可以指定某一個子程式的pid,也可以指定某一個程式組的pid

2.1 pid 引數取值說明

在這裡插入圖片描述

  • pid == -1, option==0waitpid 完全等同於 wait
  • 程式組包含多個程式,每個程式組裡都有leader程式,leader程式的pid就是這個程式組的pid
  • 一般情況下父程式呼叫fork建立子程式以後,不呼叫setpgid,那麼子程式和父程式都是同組的,如果呼叫了setpgid, 那麼子程式將會被設定為另一個程式組的程式。

waitpid.c

#include <stdlib.h>
#include "t_stdio.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void){
    int s;
    //建立子程式
    pid_t pid = fork();
    if(pid==-1)E_MSG("fork", -1);
    if(pid == 0){
        //子程式執行程式碼
        printf("child process ... %d\n", getpid());
        getchar();
        // 終止子程式
        exit(-1);
    } else {
        // 父程式執行程式碼
        // waitpid(-1, &s, 0); 這是非阻塞模式收子程式的資源. 阻塞: 如果要回收的子程式還沒有結束, 等著結束回收資源以後再去執行下面的
        // waitpid(-1, &s, WNOHANG); 這是非阻塞模式收子程式的資源。非阻塞: 如果要回收的子程式還沒有結束,waitpid直接返回0, 不等子程式了
        int w=waitpid(-1, &s, WNOHANG);
        if(w==0){// 沒有子程式的資源回收,直接結束
            printf("parent exit ...\n");
            return 0;
        }
        // 返回值不是0,說明有子程式資源的回收, 再根據返回的狀態碼來判斷子程式是怎麼終止的
        if(WIFEXITED(s)){   // WIFEXITED(s) 為真說明子程式正常退出,WEXITSTATUS(s)可以獲取子程式退出狀態碼
            printf("exit: %d\n", WEXITSTATUS(s));
        }
        if(WIFSIGNALED(s)){ // WIFSIGNALED(s) 為真說明程式是被訊號打斷的,  WTERMSIG(s) 可以獲取打斷程式訊號的編號
            printf("signum: %d\n", WTERMSIG(s));
        }
        printf("father process ...\n");
    }
    return 0;
}
# 父程式沒有等子程式直接終止
$ ./a.out 
parent exit ...
child process ... 30610

孤兒程式和殭屍程式

在這裡插入圖片描述

  • 上圖的init 改為 upstart

1. 孤兒程式程式碼示例

#include "t_stdio.h"
#include <unistd.h>
#include <sys/types.h>

int main(void){
    pid_t pid = fork();
    
    if(pid == -1)E_MSG("fork", -1);
    if(pid == 0){
        // 在子程式中執行的程式碼
        printf("父程式還沒終止時的pid: %d", getppid());
        printf("子程式的pid: %d\n", getpid()); 
        sleep(2);
        printf("父程式終止以後,但是子程式沒有終止情況下,父程式的pid: %d", getppid());  
        
    } else {
        // 在父程式中執行的程式碼
        sleep(1);
        printf("parent process ...\n");
    }

    return 0;
}
$ ./a.out 
父程式還沒終止時的pid: 27663子程式的pid: 27664
parent process ...
$ 父程式終止以後,但是子程式沒有終止情況下,父程式的pid: 2524
$ ps -aux| grep 2524
moonx     2524  0.0  0.0  48216  2512 ?        Ss   11月23   0:19 /sbin/upstart --user
moonx    28021  0.0  0.0  15968  1080 pts/21   S+   19:23   0:00 grep --color=auto 2524

2. 殭屍程式程式碼示例

#include <stdlib.h>
#include "t_stdio.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void){
    int s;
    //建立子程式
    pid_t pid = fork();
    if(pid==-1)E_MSG("fork", -1);
    if(pid == 0){
        //子程式執行程式碼
        exit(-1);
    } else {
        // 父程式執行程式碼
        getchar();
        wait(NULL);
        
    }
    return 0;
}
$ ./a.out

## 再開啟一個shell,子程式 [a.out] <defunct> 處於殭屍狀態 Z+
$ ps -aux| grep a.out
moonx    32381  0.0  0.0   4356   600 pts/21   S+   19:30   0:00 ./a.out
moonx    32382  0.0  0.0      0     0 pts/21   Z+   19:30   0:00 [a.out] <defunct>
moonx    32410  0.0  0.0  15964   980 pts/31   S+   19:30   0:00 grep --color=auto a.out

相關文章