linux c 殭屍進程的產生和清除

linuxc殭屍進程的產生和清除

什麼是殭屍進程

  殭屍進程是指它的父進程已經退出(父進程沒有等待(調用wait/waitpid)它),而該進程dead以後沒有進程接受,就成爲殭屍進程,也就是(zombie)進程。linux

殭屍進程是怎麼樣產生

  一個進程在調用exit命令結束本身的生命的時候,其實它並無真正的被銷燬,而是留下一個稱爲殭屍進程(Zombie)的數據結構(系統調用exit,它的做用是使進程退出,但也僅僅限於將一個正常的進程變成一個殭屍進程,並不能將其徹底銷燬)。編程

  在Linux進程的狀態中,殭屍進程是很是特殊的一種,它已經放棄了幾乎全部內存空間,沒有任何可執行代碼,也不能被調度,僅僅在進程列表中保留一個位置,記載該進程的退出狀態等信息供其餘進程收集,除此以外,殭屍進程再也不佔有任何內存空間。它須要它的父進程來爲它收屍。網絡

  若是他的父進程沒安裝SIGCHLD信號處理函數調用wait或waitpid()等待子進程結束,又沒有顯式忽略該信號,那麼它就一直保持殭屍狀態,若是這時父進程結束了,那麼init進程自動會接手這個子進程,爲它收屍,它仍是能被清除的。數據結構

  可是若是父進程是一個循環,不會結束,那麼子進程就會一直保持殭屍狀態,這就是爲何系統中有時會有不少的殭屍進程。系統所能使用的進程號是有限的,若是大量的產生僵死進程,將由於沒有可用的進程號而致使系統不能產生新的進程. 異步

以下代碼,子進程先於父進程退出,在父進程執行的5s內,子進程將爲殭屍:函數

#include <stdio.h>    
#include <stdlib.h>    
#include <signal.h>    
#include <unistd.h>    
    
int main() {   
    //子進程的pid    
    int c_pid;   
    int pid;   
    
    if ((pid = fork())) {   
        //父進程    
        c_pid = pid;   
        printf("The child process is %d\n", c_pid);   
        sleep(5);   
        exit(0);   
    } else {   
        //子進程    
        printf("I 'm a child.\n");   
        exit(0);   
    }   
}

如何快速找出殭屍程序

打開終端並輸入下面命令:unix

ps aux | grep Zcode

會列出進程表中全部殭屍進程的詳細內容。繼承

怎麼幹掉這些殭屍程序

用 SIGCHLD信號來殺死殭屍進程

正常狀況下咱們能夠用 SIGKILL 信號來殺死進程,可是殭屍進程已經死了, 你不能殺死已經死掉的東西。 所以你須要輸入的命令應該是:進程

ps -ef | grep pid 首先查看殭屍進程的父進程pid,輸出結果的第三列ppid就是父進程的pid

kill -s SIGCHLD ppid

將這裏的 pid 替換成父進程的進程 id,這樣父進程就會刪除全部以及完成並死掉的子進程了。

程序中避免殭屍進程的產生

  1. 父進程調用wait/waitpid。
  2. 父進程先於子進程退出,子進程自動託管到init進程。
  3. fork()兩次,調用孫子進程。
  4. 綁定SIGCHLD信號處理函數,在信號處理函數中調用wait。
  5. 綁定SIGCHLD信號處理函數SIG_IGN。(不推薦)

父進程調用wait/waitpid

父進程調用waitpid()等函數來接收子進程退出狀態。

#include <stdio.h>    
#include <stdlib.h>    
#include <signal.h>    
#include <unistd.h>    
#include <sys/wait.h>    
    
int main() {   
    //子進程的pid    
    int c_pid;   
    int pid;   
    
    if ((pid = fork())) {   
        //父進程    
        c_pid = pid;   
        printf("The child process is %d\n", c_pid);   
        //阻塞等待子進程    
        int status;   
        if ((pid = wait(&status)) != -1 && pid == c_pid) {   
            //成功回收子進程    
            printf("The child exit with %d\n", WEXITSTATUS(status));   
            fflush(stdin);   
        }  
        printf("Now , The child has been exit , and I will sleep.\n");   
        sleep(20);   
        exit(0);   
    } else {   
        //子進程    
        printf("I 'm a child.\n");   
        sleep(5);   
        exit(0);   
    }   
}

父進程先exit,子進程託管到init進程

父進程先結束,子進程則自動託管到Init進程(pid = 1)。

#include <stdio.h>    
#include <stdlib.h>    
#include <signal.h>    
#include <unistd.h>    
#include <sys/wait.h>    
    
int main() {   
    //子進程的pid    
    int c_pid;   
    int pid;   
    
    if ((pid = fork())) {   
        //父進程      
        printf("I'm father id = %d.The child process is %d\n", getpid(), pid);   
        exit(0);   
    } else {   
        //子進程    
        printf("I 'm a child.\n");   
        sleep(5);   
        exit(0);   
    }   
}

fork()兩次,調用孫子進程

父進程一次fork()後產生一個子進程隨後當即執行waitpid(子進程pid, NULL, 0)來等待子進程結束,而後子進程fork()後產生孫子進程隨後當即exit(0)。這樣子進程順利終止(父進程僅僅給子進程收屍,並不須要子進程的返回值),而後父進程繼續執行。這時的孫子進程因爲失去了它的父進程(便是父進程的子進程),將被轉交給Init進程託管。因而父進程與孫子進程無繼承關係了,它們的父進程均爲Init,Init進程在其子進程結束時會自動收屍,這樣也就不會產生殭屍進程了。

#include <stdio.h>    
#include <stdlib.h>    
#include <signal.h>    
#include <unistd.h>    
#include <sys/wait.h>    
    
int main() {   
    //子進程的pid    
    int c_pid;   
    int pid;   
    
    if ((pid = fork())) {   
        //父進程    
        c_pid = pid;   
        printf("I'm father id = %d.The child process is %d\n", getpid(), c_pid);   
        //阻塞等待子進程    
        int status;   
        if ((pid = wait(&status)) != -1 && pid == c_pid) {   
            //成功回收子進程    
            printf("The child exit with %d\n", WEXITSTATUS(status));   
            fflush(stdin);   
        } 
        exit(0);   
    } else {
        if ((pid = fork())) {
            //子進程 
            printf("I 'm a child pid = %d. my child pid = %d. exit.\n", getpid(), pid);
            exit(0);
        } else {
            //孫子進程
            sleep(20);
            printf("I 'm a grandson pid = %d. exit\n", pid);
            exit(0);
        }
           
    }   
}

綁定SIGCHLD信號處理函數,在信號處理函數中調用wait

阻塞等待,那麼父進程就沒法作其餘事;

父進程退出,在建立守護進程時候會有,可是咱們並不想讓父進程退出;

fork()兩次,孤兒進程不易由咱們程序來管理;

man wait,查看NOTES章節,能夠找到:子進程退出的時候,會發送SIGCHLD信號,默認的POSIX不響應

因此設置處理SIGCHLD信號的函數,能夠用於異步回收fork子進程。

注意:信號產生觸發信號處理函數時,會中斷某些函數,例如poll、select、epoll等。

​ 詳情見《unix網絡編程》5.8小節,POSIX信號處理

#include <stdio.h>    
#include <stdlib.h>    
#include <signal.h>    
#include <unistd.h>    
#include <sys/wait.h>    
    
void sig_chld(int num) {    
    pid_t pid;
    int stat;
    while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) // 循環處理完全部的信號
        printf("child %d terminated\n", pid);
    return;
}   
    
int main() {   
    //子進程的pid    
    int c_pid;   
    int pid;   
    
    signal(SIGCHLD, sig_chld);   
    
    if ((pid = fork())) {   
        //父進程    
        c_pid = pid;   
        printf("The child process is %d\n", c_pid);   
    
        //父進程不用等待,作本身的事情吧~    
        for (int i = 0; i < 10; i++) {   
            printf("Do parent things.\n");   
            sleep(1);   
        }   
    
        exit(0);   
    } else {   
        //子進程    
        printf("I 'm a child.\n");   
        sleep(2);   
        exit(0);   
    }   
}

綁定SIGCHLD信號處理函數SIG_IGN

不推薦使用,由於POSIX標準中未規定SIG_IGN,會破壞程序的可移植性。

#include <stdio.h>    
#include <stdlib.h>    
#include <signal.h>    
#include <unistd.h>    
#include <sys/wait.h>     
    
int main() {   
    //子進程的pid    
    int c_pid;   
    int pid;   
    
    signal(SIGCHLD,SIG_IGN); 
    
    if ((pid = fork())) {   
        //父進程    
        c_pid = pid;   
        printf("The child process is %d\n", c_pid);   
    
        //父進程不用等待,作本身的事情吧~    
        for (int i = 0; i < 10; i++) {   
            printf("Do parent things.\n");   
            sleep(1);   
        }   
    
        exit(0);   
    } else {   
        //子進程    
        printf("I 'm a child.\n");   
        sleep(2);   
        exit(0);   
    }   
}
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息