物聯網教程Linux系統程式設計——特殊程式之殭屍程式

千鋒教育官方發表於2019-08-28

 

  Linux系統程式設計——特殊程式之殭屍程式


  殭屍程式(Zombie Process)


  程式已執行結束,但程式的佔用的資源未被回收,這樣的程式稱為殭屍程式。


  在每個程式退出的時候,核心釋放該程式所有的資源、包括開啟的檔案、佔用的記憶體等。但是仍然為其保留一定的資訊,這些資訊主要主要指程式控制塊的資訊(包括程式號、退出狀態、執行時間等)。直到父程式透過wait()或waitpid()來獲取其狀態並釋放(具體用法,請看《等待程式結束》)。這樣就會導致一個問題,如果程式不呼叫wait()或waitpid()的話,那麼保留的那段資訊就不會釋放,其程式號就會一直被佔用,但是系統所能使用的程式號是有限的,如果大量的產生僵死程式,將因為沒有可用的程式號而導致系統不能產生新的程式.此即為殭屍程式的危害,應當避免。


  子程式已執行結束,父程式未呼叫wait()或waitpid()函式回收子程式的資源是子程式變為殭屍程式的原因。


  殭屍程式測試程式如下:


  #include<stdio.h>


  #include<unistd.h>


  #include<errno.h>


  #include<stdlib.h>


  int main(int argc,char*argv[])


  {


  pid_t pid;


  pid=fork();//建立程式


  if(pid<0){//出錯


  perror("fork error:");


  exit(1);


  }else if(0==pid){//子程式


  printf("I am child process.I am exiting.\n");


  printf("[son id]:%d\n",getpid());


  exit(0);


  }else if(pid>0){//父程式


  //父程式沒有呼叫wati()或watipid()


  sleep(1);//保證子程式先執行


  printf("I am father process.I will sleep two seconds\n");


  printf("[father id]:%d\n",getpid());


  while(1);//不讓父程式退出


  }


  return 0;


  }



  如何避免殭屍程式?


  1)最簡單的方法,父程式透過wait()和waitpid()等函式等待子程式結束,但是,這會導致父程式掛起。具體用法,請看《程式的控制:結束程式、等待程式結束》。


  2)如果父程式要處理的事情很多,不能夠掛起,透過signal()函式人為處理訊號SIGCHLD,只要有子程式退出自動呼叫指定好的回撥函式,因為子程式結束後,父程式會收到該訊號SIGCHLD,可以在其回撥函式里呼叫wait()或waitpid()回收。關於訊號的更詳細用法,請看《訊號中斷處理》。


  測試程式碼如下:


  #include<stdio.h>


  #include<unistd.h>


  #include<errno.h>


  #include<stdlib.h>


  #include<signal.h>


  void sig_child(int signo)


  {


  pid_t pid;


  //處理殭屍程式,-1代表等待任意一個子程式,WNOHANG代表不阻塞


  while((pid=waitpid(-1,NULL,WNOHANG))>0){


  printf("child%d terminated.\n",pid);


  }


  }


  int main()


  {


  pid_t pid;


  //建立捕捉子程式退出訊號


  //只要子程式退出,觸發SIGCHLD,自動呼叫sig_child()


  signal(SIGCHLD,sig_child);


  pid=fork();//建立程式


  if(pid<0){//出錯


  perror("fork error:");


  exit(1);


  }else if(pid==0){//子程式


  printf("I am child process,pid id%d.I am exiting.\n",getpid());


  exit(0);


  }else if(pid>0){//父程式


  sleep(2);//保證子程式先執行


  printf("I am father,i am exited\n\n");


  system("ps-ef|grep defunct");//檢視有沒有殭屍程式


  }


  return 0;


  }


  執行結果:


  3)如果父程式不關心子程式什麼時候結束,那麼可以用signal(SIGCHLD,SIG_IGN)通知核心,自己對子程式的結束不感興趣,父程式忽略此訊號,那麼子程式結束後,核心會回收,並不再給父程式傳送訊號。關於訊號的更詳細用法,請看《訊號中斷處理》。


  #include<stdio.h>


  #include<unistd.h>


  #include<errno.h>


  #include<stdlib.h>


  #include<signal.h>


  int main()


  {


  pid_t pid;


  //忽略子程式退出訊號的訊號


  //那麼子程式結束後,核心會回收,並不再給父程式傳送訊號


  signal(SIGCHLD,SIG_IGN);


  pid=fork();//建立程式


  if(pid<0){//出錯


  perror("fork error:");


  exit(1);


  }else if(pid==0){//子程式


  printf("I am child process,pid id%d.I am exiting.\n",getpid());


  exit(0);


  }else if(pid>0){//父程式


  sleep(2);//保證子程式先執行


  printf("I am father,i am exited\n\n");


  system("ps-ef|grep defunct");//檢視有沒有殭屍程式


  }


  return 0;


  }


  執行結果:


  4)還有一些技巧,就是fork()兩次,父程式fork()一個子程式,然後繼續工作,子程式fork()一個孫程式後退出,那麼孫程式被init接管,孫程式結束後,init(1號程式)會回收。不過子程式的回收還要自己做。《UNIX環境高階程式設計》8.6節說的非常詳細。原理是將子程式成為孤兒程式,從而其的父程式變為init程式(1號程式),透過init程式(1號程式)可以處理殭屍程式。更多詳情,請看《特殊程式之孤兒程式》。


  原始碼如下:


  #include<stdio.h>


  #include<stdlib.h>


  #include<unistd.h>


  #include<errno.h>


  int main()


  {


  pid_t pid;


  //建立第一個子程式


  pid=fork();


  if(pid<0){//出錯


  perror("fork error:");


  exit(1);


  }else if(pid==0){//子程式


  //子程式再建立子程式


  printf("I am the first child process.pid:%d\tppid:%d\n",getpid(),getppid());


  pid=fork();


  if(pid<0){


  perror("fork error:");


  exit(1);


  }else if(pid==0){//子程式


  //睡眠3s保證下面的父程式退出,這樣當前子程式的父親就是init程式


  sleep(3);


  printf("I am the second child process.pid:%d\tppid:%d\n",getpid(),getppid());


  exit(0);


  }else if(pid>0){//父程式退出


  printf("first procee is exited.\n");


  exit(0);


  }


  }else if(pid>0){//父程式


  //父程式處理第一個子程式退出,回收其資源


  if(waitpid(pid,NULL,0)!=pid){


  perror("waitepid error:");


  exit(1);


  }


  exit(0);


  }


  return 0;


  }


  執行結果:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69914734/viewspace-2655313/,如需轉載,請註明出處,否則將追究法律責任。

相關文章