1. 程式人生 > 實用技巧 >回收子程序

回收子程序

1.孤兒程序
父程序先於子程序結束,則子程序成為孤兒程序,子程序的父程序成為init程序,稱為init程序領養孤兒程序
2.殭屍程序
子程序終止,父程序尚未回收,子程序殘留資源(PCB)存放在核心中,變成殭屍(zombie)程序
特別注意:殭屍程序是不能使用kill命令清除掉的。以為kill命令只能終止活著的程序,而殭屍程序已經終止。
3.回收殭屍程序
模擬一個殭屍程序:zombie.c

   #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include 
<sys/wait.h> int main(void) { pid_t pid; pid = fork(); if(pid == 0) { printf("----child, going to sleep,my parent=%d\n", getppid()); sleep(10); printf("----child die----\n"); }
else if(pid > 0) { while(1){ printf("I am parent, pid=%d, myson=%d\n", getpid(), pid); sleep(1); } } else { perror("fork error"); exit(1); }
return 0; }

ps aux 查詢結果如下:
root 3230 0.0 0.0 4216 352 pts/0 S+ 17:36 0:00 ./zombie.out
root 3231 0.0 0.0 0 0 pts/0 Z+ 17:36 0:00 [zombie.out] <defunct>--殭屍程序

wait函式
一個程序在終止時會關閉所有檔案描述符,釋放在使用者空間分配的記憶體,但它的PCB還保留著,核心在其中儲存了一些資訊:
如果是正常終止則儲存著退出狀態,如果是異常終止則儲存著導致該程序終止的訊號是哪個。這個程序的父程序可以呼叫
wait或者waitpid獲取這些資訊,然後徹底清除掉這個程序。我們知道一個程序的退出狀態我們可以在shell中用特殊變數$?
檢視,因為shell是它的父程序,當它終止時shell呼叫wait或waitpid得到它的退出狀態同時徹底清除這個程序。

父程序呼叫wait函式可以回收子程序終止資訊,該函式有三個功能:
1).阻塞等待子程序退出
2).回收子程序殘留資源
3).獲取子程序結束狀態(退出原因)
pid_t wait(int *status);
成功:清理掉子程序ID;
失敗:-1(沒有子程序)

當程序終止時,作業系統的隱式回收機制會做如下操作:
1).關閉所有的檔案描述符
2).釋放使用者空間分配的記憶體
核心的PCB仍存在,其中儲存該程序的退出狀態。(正常終止-->退出值,異常終止-->終止訊號)

可使用wait函式傳出引數status來儲存程序的退出狀態。藉助巨集函式來進一步判斷程序終止的具體原因。
巨集函式可分為三組:
1).WIFEXITED(status) 為非0 -->程序正常結束
WEXITSTATUS(status) 如上巨集為真,使用此巨集-->獲取程序退出狀態(exit的引數)

2).WIFSIGNALED(status) 為非0 -->程序異常終止
WTERMSIG(status) 如上巨集為真,使用此巨集-->取得程序終止的那個訊號的編號(kill的引數)

wait第一版:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    int main(void)
    {
        pid_t pid, wpid;
        pid = fork();
    
        if(pid == 0) {
            printf("----child, going to sleep,my parent=%d\n", getppid());
            sleep(10);
            printf("----child die----\n");
        } else if(pid > 0) {
            wpid = wait(NULL);
            if(wpid == -1){
                perror("wait error");
                exit(1);
            }
            
            while(1){
                printf("I am parent, pid=%d, myson=%d\n", getpid(), pid);
                sleep(1);
            }
        } else {
            perror("fork error");
            exit(1);
        }
    
        return 0;
    }

wait使用巨集版:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    int main(void)
    {
        pid_t pid, wpid;
        int status;
        pid = fork();
    
        if(pid == 0) {            
            printf("----child, going to sleep,my parent=%d\n", getppid());
            sleep(20);
            printf("----child die----\n");
            return 100;
        } else if(pid > 0) {
            wpid = wait(&status);
            if(wpid == -1){
                perror("wait error");
                exit(1);
            }
            if(WIFEXITED(status)) {
                printf("child exit with %d\n",WEXITSTATUS(status));
            }
            if(WIFSIGNALED(status)) {
                printf("child killed by %d\n",WTERMSIG(status));
            }
            
            while(1){
                printf("I am parent, pid=%d, myson=%d\n", getpid(), pid);
                sleep(1);
            }
        } else {
            perror("fork error");
            exit(1);
        }
    
        return 0;
    }

gcc abnor.c -o abnor.out 測試段錯誤和浮點錯誤

    int main(void){
    char *p = "test of wait abnormally\n";
    //p[0] = 'h';//測試段錯誤
    int a = 5/0;//測試浮點錯誤
    return 65;
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    int main(void)
    {
        pid_t pid, wpid;
        int status;
        pid = fork();
    
        if(pid == 0) {        
            execl("abnor.out", "abnor.out", NULL);
            printf("----child, going to sleep,my parent=%d\n", getppid());
            sleep(20);
            printf("----child die----\n");
            return 100;
        } else if(pid > 0) {
            wpid = wait(&status);
            if(wpid == -1){
                perror("wait error");
                exit(1);
            }
            if(WIFEXITED(status)) {
                printf("child exit with %d\n",WEXITSTATUS(status));
            }
            if(WIFSIGNALED(status)) {
                printf("child killed by %d\n",WTERMSIG(status));
            }
            
            while(1){
                printf("I am parent, pid=%d, myson=%d\n", getpid(), pid);
                sleep(1);
            }
        } else {
            perror("fork error");
            exit(1);
        }
    
        return 0;
    }

根據kill -l找到對應的訊號

waitpid函式
可以指定pid程序清理,可以不阻塞。
pid_t waitpid(pid_t pid, int *status, int options);
成功:返回清理掉的子程序ID; 失敗:-1(無子程序結束)
特殊引數和返回值情況:
引數1 pid:
>0 回收指定ID的子程序
-1 回收任意子程序(相當於wait)
0 回收和當前呼叫waitpid一個組的所有子程序
<-1回收指定程序組內的任意子程序
引數2 status

引數3 設定為WNOHANG,非阻塞回收(輪詢)
設定為0,父程序阻塞等待子程序執行完,阻塞回收
返回值: 成功:pid
失敗:-1
返回0:引數3設定為WNOHANG,且子程序正在執行

注意:一次wait或waitpid呼叫只能清理一個子程序,清理多個子程序應使用迴圈。