【Linux系统编程】特殊进程之僵尸进程
00. 目錄
文章目錄
- 00. 目錄
- 01. 僵尸進程概述
- 02. 僵尸進程案例
- 03. 避免僵尸進程
- 04. 附錄
01. 僵尸進程概述
進程已運行結束,但進程的占用的資源未被回收,這樣的進程稱為僵尸進程。
在每個進程退出的時候,內核釋放該進程所有的資源、包括打開的文件、占用的內存等。 但是仍然為其保留一定的信息,這些信息主要主要指進程控制塊的信息(包括進程號、退出狀態、運行時間等)。直到父進程通過 wait() 或 waitpid() 來獲取其狀態并釋放。這樣就會導致一個問題,如果進程不調用wait() 或 waitpid() 的話, 那么保留的那段信息就不會釋放,其進程號就會一直被占用,但是系統所能使用的進程號是有限的,如果大量的產生僵尸進程,將因為沒有可用的進程號而導致系統不能產生新的進程.此即為僵尸進程的危害,應當避免。
子進程已運行結束,父進程未調用 wait() 或 waitpid() 函數回收子進程的資源是子進程變為僵尸進程的原因。
02. 僵尸進程案例
測試代碼:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h>int main(void) {pid_t pid = -1;pid = fork();if (-1 == pid){perror("fork"); goto err0;}else if (0 == pid){printf("I am child process %d\n", getpid());exit(0);}sleep(200);return 0; err0:return 1; }測試結果:
我們另啟一個終端,查看進程的狀態,有哪些是僵尸進程:
或者
ps -ef | grep defunct ,后面尖括號里是 defunct 的都是僵尸進程。
03. 避免僵尸進程
3.1 避免僵尸進程的方法
-
最簡單的方法,父進程通過 wait() 和 waitpid() 等函數等待子進程結束,但是,這會導致父進程掛起。
-
如果父進程要處理的事情很多,不能夠掛起,通過 signal() 函數人為處理信號 SIGCHLD , 只要有子進程退出自動調用指定好的回調函數,因為子進程結束后, 父進程會收到該信號 SIGCHLD ,可以在其回調函數里調用 wait() 或 waitpid() 回收。關于信號的更詳細用法,請看《信號中斷處理》。
測試代碼:
#include <stdio.h> #include <unistd.h> #include <errno.h> #include <stdlib.h> #include <signal.h> #include <sys/wait.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; }測試結果:
- 如果父進程不關心子進程什么時候結束,那么可以用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; }測試結果:
- 還有一些技巧,就是 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; }測試結果:
04. 附錄
4.1 參考博客:【Linux系統編程】特殊進程之僵尸進程
4.2 參考網址:百度百科
總結
以上是生活随笔為你收集整理的【Linux系统编程】特殊进程之僵尸进程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux系统编程】vfork() 函
- 下一篇: 【Linux系统编程】特殊进程之孤儿进程