Linux 殭屍進程詳解
轉載:http://www.51testing.com/?uid-225738-action-viewspace-itemid-206225
1.殭屍進程概念:
殭屍進程(Zombie Process):就是已經結束了的進程,但是沒有從進程表中刪除。太多了會導致進程表裡麵條目滿了,進而導致系統崩潰,倒是不佔用其他系統資源。
在 Linux 進程的狀態中,殭屍進程是非常特殊的一種,它已經放棄了幾乎所有記憶體空間,沒有任何可執行代碼,也不能被調度,僅僅在進程列表中保留一個位置,記載該進程的 退出狀態等資訊供其他進程收集,除此之外,殭屍進程不再佔有任何記憶體空間。它需要它的父進程來為它收屍,如果他的父進程沒安裝 SIGCHLD 訊號處理函數 調用 wait 或 waitpid() 等待子進程結束,又沒有顯式忽略該訊號,那麼它就一直保持殭屍狀態,如果這時父進程結束了,那麼 init 進程自動會接手 這個子進程,為它收屍,它還是能被清除的。但是如果如果父進程是一個迴圈,不會結束,那麼子進程就會一直保持殭屍狀態,這就是為什麼系統中有時會有很多的 殭屍進程。
2.殭屍進程產生的原因:
每個 Linux 進程在進程表裡都有一個進入點(entry),核心程式執行該進程時使用到的一切資訊都儲存在進入點。當用 ps 命令察看系統中的進程資訊時,看到的就是進程表中的相關資料。當以 fork() 系統調用建立一個新的進程後,核心進程就會在進程表中給這個新進程分配一個進入點,然後將相關資訊儲存在該進入點所對應的進程表內。這些資訊中有一項是其父進程的識別碼。當這個進程走完了自己的生命週期後,它會執行 exit() 系統調用,此時原來進程表中的數 據會被該進程的退出碼(exit code)、執行時所用的CPU時間等資料所取代,這些資料會一直保留到系統將它傳遞給它的父進程為止。由此可見,defunct 進程的出現時間是在子進 程終止後,但是父進程尚未讀取這些資料之前。
3.殭屍進程的查看:
用top命令,可以看到:
Tasks: 123 total, 1 running, 122 sleeping, 0 stopped, 0 zombie
zombie 前面的數量就是殭屍進程到數量;
ps -ef
出現:
root 13028 12956 0 10:51 pts/2 00:00:00 [ls] <defunct>
最後有 defunct 的標記,就表明是殭屍進程。
4.殭屍進程解決辦法:
4.1 改寫父進程,在子進程死後要為它收屍。具體做法是接管 SIGCHLD 訊號。子進程死後,會發送 SIGCHLD 訊號給父進程,父進程收到此訊號後,執行 waitpid() 函數為子進程收屍。這是基於這樣的原理:就算父進程沒有調用 wait ,核心也會向它發送 SIGCHLD 訊息,儘管對的預設處理是忽略,如果想響應這個訊息,可以設定一個處理函數。
4.2 把父進程殺掉。父進程死後,殭屍進程成為"孤兒進程",過繼給1號進程 init ,init 始終會負責清理殭屍進程.它產生的所有殭屍進程也跟著消失。
kill -9 `ps -ef | grep "Process Name" | awk '{ print $3 }'`
其中,“Process Name”為處於 zombie 狀態的進程名。
4.3 殺父進程不行的話,就嘗試用 skill -t TTY 關閉相應終端,TTY 是進程相應的 tty 號(終端號)。但是,ps 可能會查不到特定進程的 tty 號,這時就需要自己判斷了。
4.4 實在不行,重啟系統吧,這也是最常用到方法之一。
5.殭屍進程執行個體:
1 #include "sys/types.h"
2 #include "sys/wait.h"
3 #include "stdio.h"
4 #include "unistd.h"
5
6 int main(int argc, char* argv[])
7 {
8 while(1)
9 {
10 pid_t chi = fork();
11 if(chi == 0)
12 {
13 execl("/bin/bash","bash","-c","ls",NULL);
14 }
15 sleep(2);
16 }
17 }
會不停地產生僵死進程ls;
1 #include <stdio.h>
2 #include<sys/types.h>
3
4 main()
5 {
6 if(!fork())
7 {
8 printf("child pid=%d\n", getpid());
9 exit(0);
10 }
11
12
13 sleep(60);
14 printf("parent pid=%d \n", getpid());
15 exit(0);
16 }
60s 內會不斷產生殭屍進程,直到父進程 exit(0) ,如果在調用 wait/waitpid 來為子進程收屍,就不會產生殭屍進程了。
PS:運行例子,先 gcc zombie1.c -o zombie 編譯,然後運行 zombie ,然後可以可用 ps -ef 來查看是否產生了殭屍進程。
其他知識:
execl:進程進入了 shell 環境執行 執行完進程結束
system=fork+exec+waitpid:執行完進程仍然存在,只是用它的子進程執行了操作。