1.僵尸进程怎么产生?
如果一个进程在其终止的时候,自己就回收所有分配给它的资源,系统就不会产生所谓的僵尸进程了。那么我们说一个进程终止之后,还保留哪些信息?为什么终止之后还需要保留这些信息呢?
一个进程终止的方法很多,进程终止后有些信息对于父进程和内核还是很有用的,例如进程的ID号、进程的退出状态、进程运行的CPU时间等。因此进程在终止时,回收所有内核分配给它的内存、关闭它打开的所有文件等等,但是还会保留以上极少的信息,以供父进程使用。父进程可以使用 wait/waitpid 等系统调用来为子进程收拾,做一些收尾工作。
因此,一个僵尸进程产生的过程是:父进程调用fork创建子进程后,子进程运行直至其终止,它立即从内存中移除,但进程描述符仍然保留在内存中(进程描述符占有极少的内存空间)。子进程的状态变成EXIT_ZOMBIE,并且向父进程发送SIGCHLD 信号,父进程此时应该调用 wait() 系统调用来获取子进程的退出状态以及其它的信息。在 wait 调用之后,僵尸进程就完全从内存中移除。因此一个僵尸存在于其终止到父进程调用 wait 等函数这个时间的间隙,一般很快就消失,但如果编程不合理,父进程从不调用 wait 等系统调用来收集僵尸进程,那么这些进程会一直存在内存中。
2.僵尸进程存在的意义
上面也有说到进程终止后有些信息对于父进程和内核还是很有用的,例如进程的ID号、进程的退出状态、进程运行的CPU时间等。
那究竟是怎样的有用?
进程的退出状态对父进程极其的有用,如果工作进程是因为内存不足才停止运行的话,父进程却不知道这一信息,继续的建立运行其他的进程,显然会影响之后的工作。
但是父进程处理死掉的进程的工作不可能总是及时,因此操作系统只好就先收起进程的大部分资源,剩下的资源就等待父进程来处理。
3.怎么查看系统中的僵尸进程
(1) ps aux | grep -w ‘Z’
其中 ps aux输出格式为:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
格式说明:
USER: 行程拥有者
PID: pid
%CPU: 占用的 CPU 使用率
%MEM: 占用的记忆体使用率
VSZ: 占用的虚拟记忆体大小
RSS: 占用的记忆体大小
TTY: 终端的次要装置号码 (minor device number of tty)
STAT: 该行程的状态,linux的进程有5种状态:
D 不可中断 uninterruptible sleep (usually IO)
R 运行 runnable (on run queue)
S 中断 sleeping
T 停止 traced or stopped
Z 僵死 a defunct (”zombie”) process
注: 其它状态还包括W(无驻留页), <(高优先级进程), N(低优先级进程), L(内存锁页).
START: 行程开始时间
TIME: 执行的时间
COMMAND:所执行的指令
(2)ps -A -o stat,ppid,pid,cmd | grep -e ‘^[Zz]’
-A 参数列出所有进程
-o 自定义输出字段
我们设定显示字段为 stat(状态), ppid(进程父id), pid(进程id),cmd(命令)这四个参数
因为状态为 z或者Z的进程为僵尸进程,所以我们使用grep抓取stat状态为zZ进程
运行结果参考如下
Z 12334 12339 /path/cmd
我们可以用 kill -HUP 12339来杀掉这个僵尸进程
4.避免僵尸进程的出现
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
sig_atomic_t child_exit_status;
void clean_up_child_process(int signal_num)
{
/* clean up child process */
int status;
wait (&status);
/* store its exit status in a global variable */
child_exit_status = status;
}
int main()
{
/* handle SIGCHLD by calling clean_up_child_process */
struct sigaction sigchild_action;
memset(&sigchild_action, 0, sizeof(sigchild_action));
sigchild_action.sa_handler = &clean_up_child_process;
sigaction(SIGCHLD, &sigchild_action, NULL);
/* fork a child, and let the child process dies before parent */
pid_t c_pid;
c_pid = fork();
if (c_pid > 0)
{
printf("in parent process, and sleep for on mininute...zZ...\n");
sleep(60);
}
else if(c_pid == 0)
{
printf("in child process, and exit now\n");
exit(0);
}
else
{
printf("fork failed!\n");
}
return 0;
}