Linux zombie process is a process that has died long ago, but still occupies a position in the process table; if the parent process does not have wait() when the child process dies, you can usually see it displayed as "
", thus generating zombie processes; if a large number of zombie processes are generated, the system will not be able to generate new processes because there are no available process numbers, so zombie processes should be avoided.
The operating environment of this tutorial: linux5.9.8 system, Dell G3 computer.
The generation and avoidance of zombie processes (Defunct processes) under Linux
In the UNIX system, a process ends, but its parent The process does not wait (call wait/waitpid) for him, then he will become a zombie process. When you use the ps command to observe the execution status of the process, you will see that the status bar of these processes is defunct. A zombie process is a process that has died long ago, but still occupies a slot in the processes table.
But if the parent process of the process has ended first, then the process will not become a zombie process. Because when each process ends, the system will scan all processes running in the current system to see if any process is a child process of the process that just ended. If so, the Init process will take over it and become it. The parent process, thus ensuring that each process will have a parent process. The Init process will automatically wait for its child processes, so all processes taken over by Init will not become zombie processes.
Each Unix process has an entry point (entry) in the process table, Core processUsed when executing the process All information received is stored at the entry point. When you use the ps command to view process information in the system, what you see is the relevant data in the process table. When a new process is created with the fork() system call, the core process will assign an entry point to the new process in the process table, and then store relevant information in the process table corresponding to the entry point. One of these pieces of information is the identification number of its parent process.
The end of the child process and the running of the parent process are an asynchronous process, that is, the parent process can never predict when the child process will end. So will the status information when the child process ends be lost because the parent process is too busy to wait for the child process, or because it does not know when the child process will end?
Won't. Because UNIX provides a mechanism to ensure that as long as the parent process wants to know the status information when the child process ends, it can get it. This mechanism is: when the child process completes its life cycle, it will execute the exit() system call, and the kernel releases all the resources of the process, including open files, occupied memory, etc. However, certain information is still retained for it (including the process ID, the exit code, the termination status of the process, the amount of CPU time taken by the process, etc.), and these data will be retained until the system Pass it to its parent process and not release it until the parent process fetch it via wait/waitpid.
In other words, when a process dies, it does not completely disappear. The process terminates, it is no longer running, but there is still some residual data waiting for the parent process to reclaim it. When a parent process forks() a child process, it must use wait() (or waitpid()) to wait for the child process to exit. It is this wait() action that makes the residual data of the child process disappear.
If the parent process does not call wait/waitpid, the retained information will not be released, and its process number will always be occupied, but the system The capacity of the process table is limited, and the process numbers that can be used are also limited. If a large number of zombie processes are generated, the system will not be able to generate new processes because there are no available process numbers.
So, defunct processes not only occupy the system's memory resources and affect the performance of the system, but also cause system paralysis if there are too many of them. Moreover, since the scheduler cannot select the Defunct process, the kill command cannot be used to delete the Defunct process. The only way is to restart the system.
If the parent process does not have wait() when the child process dies, you can usually use ps to see that it is displayed as "
It can be seen that the defunct process appears after the child process terminates, but before the parent process has read the data. Taking advantage of this, we can use the following program to create a defunct process:
#include <stdio.h> #include<sys/types.h> main() { if(!fork()) { printf(“child pid=%d\n”, getpid()); exit(0); } sleep(20); printf(“parent pid=%d \n”, getpid()); exit(0); }
当上述程序以后台的方式执行时,第17行强迫程序睡眠20秒,让用户有时间输入ps -e指令,观察进程的状态,我们看到进程表中出现了defunct进程。当父进程执行终止后,再用ps -e命令观察时,我们会发现defunct进程也随之消失。这是因为父进程终止后,init 进程会接管父进程留下的这些“孤儿进程”(orphan process),而这些“孤儿进程”执行完后,它在进程表中的进入点将被删除。如果一个程序设计上有缺陷,就可能导致某个进程的父进程一直处于睡眠状态或是陷入死循环,父进程没有wait子进程,也没有终止以使Init接管,该子进程执行结束后就变成了defunct进程,这个defunct 进程可能会一直留在系统中直到系统重新启动。
在看一个产生僵尸进程的例子。
子进程要执行的程序test_prog
//test.c #include <stdio.h> int main() { int i = 0; for (i = 0 ; i < 10; i++) { printf ("child time %d\n", i+1); sleep (1); } return 0; }
父进程father的代码father.c
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main() { int pid = fork (); if (pid == 0) { system ("./test_prog"); _exit (0); }else { int i = 0; /* int status = 0; while (!waitpid(pid, &status, WNOHANG)) { printf ("father waiting%d\n", ++i); sleep (1); }*/ while (1) { printf ("father waiting over%d\n", ++i); sleep (1); } return 0; } }
执行./father,当子进程退出后,由于父进程没有对它的退出进行关注,会出现僵尸进程
20786 pts/0 00:00:00 father 20787 pts/0 00:00:00 father <defunct>
总结:子进程成为 defunct 直到父进程 wait(),除非父进程忽略了 SIGCLD 。更进一步,父进程没有 wait() 就消亡(仍假设父进程没有忽略 SIGCLD )的子进程(活动的或者 defunct)成为 init 的子进程,init 着手处理它们。
1、父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。
在上个例子中,如果我们略作修改,在第8行sleep()系统调用前执行wait()或waitpid()系统调用,则子进程在终止后会立即把它在进程表中的数据返回给父进程,此时系统会立即删除该进入点。在这种情形下就不会产生defunct进程。
2. 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。
3. 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号
4. fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。 下面就是Stevens给的采用两次folk避免僵尸进程的示例:
#include "apue.h" #include <sys/wait.h> int main(void) ...{ pid_t pid; if ((pid = fork()) < 0) ...{ err_sys("fork error"); } else if (pid == 0) ...{ /**//* first child */ if ((pid = fork()) < 0) err_sys("fork error"); else if (pid > 0) exit(0); /**//* parent from second fork == first child */ /**//* * We're the second child; our parent becomes init as soon * as our real parent calls exit() in the statement above. * Here's where we'd continue executing, knowing that when * we're done, init will reap our status. */ sleep(2); printf("second child, parent pid = %d ", getppid()); exit(0); } if (waitpid(pid, NULL, 0) != pid) /**//* wait for first child */ err_sys("waitpid error"); /**//* * We're the parent (the original process); we continue executing, * knowing that we're not the parent of the second child. */ exit(0); }
相关推荐:《Linux视频教程》
The above is the detailed content of What is a linux zombie process?. For more information, please follow other related articles on the PHP Chinese website!