天天看點

Linux 系統中僵屍程序

  linux 系統中僵屍程序和現實中僵屍(雖然我也沒見過)類似,雖然已經死了,但是由于沒人給它們收屍,還能四處走動。僵屍程序指的是那些雖然已經終止的程序,但仍然保留一些資訊,等待其父程序為其收屍。

<a target="_blank"></a>

  如果一個程序在其終止的時候,自己就回收所有配置設定給它的資源,系統就不會産生所謂的僵屍程序了。那麼我們說一個程序終止之後,還保留哪些資訊?為什麼終止之後還需要保留這些資訊呢?

  一個程序終止的方法很多,程序終止後有些資訊對于父程序和核心還是很有用的,例如程序的id号、程序的退出狀态、程序運作的cpu時間等。是以程序 在終止時,回收所有核心配置設定給它的記憶體、關閉它打開的所有檔案等等,但是還會保留以上極少的資訊,以供父程序使用。父程序可以使用 wait/waitpid 等系統調用來為子程序收拾,做一些收尾工作。

  是以,一個僵屍程序産生的過程是:父程序調用fork建立子程序後,子程序運作直至其終止,它立即從記憶體中移除,但程序描述符仍然保留在記憶體中(程序描述符占有極少的記憶體空間)。子程序的狀态變成exit_zombie,并且向父程序發送sigchld 信号,父程序此時應該調用 wait() 系 統調用來擷取子程序的退出狀态以及其它的資訊。在 wait 調用之後,僵屍程序就完全從記憶體中移除。是以一個僵屍存在于其終止到父程序調用 wait 等函數這個時間的間隙,一般很快就消失,但如果程式設計不合理,父程序從不調用 wait 等系統調用來收集僵屍程序,那麼這些程序會一直存在記憶體中。

  在 linux 下,我們可以使用 ps 等指令檢視系統中僵屍程序,僵屍程序的狀态标記為‘z’:

Linux 系統中僵屍程式

https://dn-linuxcn.qbox.me/data/attachment/album/201404/09/145023nsfw6cj420uo6ofo.png

  根據上面的描述,我們很容易去寫一個程式來産生僵屍程序,如下代碼:

#include  #include &lt;sys/types.h&gt; int main() {     //fork a child process     pid_t pid = fork();     if (pid &gt; 0)   //parent process     {         printf("in parent process, sleep for one miniute...zz...\n");         sleep(60);         printf("after sleeping, and exit!\n");     }     else if (pid == 0)           //child process exit, and to be a zombie process         printf("in child process, and exit!\n");         exit(0);     return 0; }

  父程序并沒有寫 wait 等系統調用函數,是以在子程序退出之後變成僵屍程序,父程序并沒有為其去收屍。我們使用下面指令編譯運作該程序,然後檢視系統中程序狀态:

guohailin@guohailin:~/documents$ gcc zombie.c -o zombie guohailin@guohailin:~/documents$ ./zombie  in parent process, sleep for one miniute...zz... in child process, and exit! # 打開另一個終端: guohailin@guohailin:~$ ps aux | grep -w 'z' 1000      2211  1.2  0.0      0     0 ?        z    13:24   6:53 [chromium-browse]  1000      4400  0.0  0.0      0     0 ?        z    10月16   0:00 [fcitx]  1000     10871  0.0  0.0      0     0 pts/4    z+   22:32   0:00 [zombie]

  從上面可以看出,系統中多了一個僵屍程序。但如果等父程序睡眠醒來退出之後,我們再次檢視系統程序資訊,發現剛才的僵屍程序不見了。

after sleeping, and exit! guohailin@guohailin:~/documents$ ps aux | grep -w 'z' 1000      4400  0.0  0.0      0     0 ?        z    10月16   0:00 [fcitx]

  這是為什麼呢?父程序到死都也沒有為其子程序收屍呀,怎麼父程序退出之後,那個僵屍程序就消失了呢?難道父程序在退出時會為子程序收拾嗎?其實不 然....真正的原因是:父程序死掉之後,其所有子程序過繼給 init 程序,init 程序成為該僵屍程序的新程序,init 程序會周期性地去調用 wait 系統調用來清除它的僵屍孩子。是以,你會發現上面例子中父程序死掉之後,僵屍程序也跟着消失,其實是 init 程序為其收屍的!

  不能使用 kill 後接 sigkill 信号這樣的指令像殺死普通程序一樣殺死僵屍程序,因為僵屍程序是已經死掉的程序,它不能再接收任何信号。事實上,如果系統中僵屍程序并不多的話,我們也無需去消除它們,少數的僵屍程序并不會對系統的性能有什麼影響。

  那麼在程式設計時,如果能避免系統中大量産生僵屍程序呢?根據上面描述的,子程序在終止時會向父程序發 sigchld 信号,linux 預設是忽略該信号的,我們可以顯示安裝該信号,在信号處理函數中調用 wait 等函數來為其收屍,這樣就能避免僵屍程序長期存在于系統中了。示例代碼如下:

#include &lt;sys/wait.h&gt; sig_atomic_t child_exit_status; void clean_up_child_process(int signal_num)     /* clean up child process */     int status;     wait (&amp;status);     /* store its exit status in a global variable */     child_exit_status = status;     /* handle sigchld by calling clean_up_child_process  */     struct sigaction sigchild_action;     memset(&amp;sigchild_action, 0, sizeof(sigchild_action));     sigchild_action.sa_handler = &amp;clean_up_child_process;     sigaction(sigchld, &amp;sigchild_action, null);     /* fork a child, and let the child process dies before parent */     pid_t c_pid;     c_pid = fork();     if (c_pid &gt; 0)         printf("in parent process, and sleep for on mininute...zz...\n");     else if(c_pid == 0)         printf("in child process, and exit now\n");     else         printf("fork failed!\n"); }   

<a href="http://en.wikipedia.org/wiki/zombie_process" target="_blank">http://en.wikipedia.org/wiki/zombie_process</a>

<a href="http://simplestcodings.blogspot.com/2010/10/story-of-zombie-process.html" target="_blank">http://simplestcodings.blogspot.com/2010/10/story-of-zombie-process.html</a>

<a href="http://www.howtogeek.com/119815/" target="_blank">http://www.howtogeek.com/119815/</a>

  原文釋出時間為:2013-10-20

本文來自雲栖社群合作夥伴“linux中國”

繼續閱讀