您的位置:首页 > 产品设计 > UI/UE

APUE之fork两次与僵尸进程的问题

2016-01-18 11:31 453 查看
初读此书,一点不明,最终的所有的无父进程的子进程都要变为init进程的子进程,那为什么,还要fork两次解决这个僵尸进程的问题,这其中的本质原因,还是没有弄清楚,为此研究一下。

如下是书中一段源码:

/*
* Avoid zombie processes by calling fork twice.
* APUE-2e 程序清单8-5
*/
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
pid_t pid;
if( (pid = fork()) < 0 )
{
printf("fork error.\n");
exit(-1);
}
else if(pid == 0)   /* first child */
{
if( (pid = fork()) < 0 )
{
printf("fork error.\n");
exit(-1);
}
else if(pid > 0)
{
exit(0);
}

/* We're the second child; our parent becomes init as soon as our real parent exits. */
printf("second child, parent pid = %d\n", getppid());
/* ---------------handle tasks--------------- */
exit(0);
}

if(waitpid(pid, NULL, 0) != pid)    /* wait for first child */
{
printf("waitpid error.\n");
exit(1);
}
printf("parent, first child pid = %d\n", pid);
/* ---------------handle tasks--------------- */

exit(0);
}


再次重新认识这里面的几个概念。

zombie processes

在wiki中

On Unix and Unix-like computer operating systems, a zombie process or defunct process is a process that has completed execution (via the exit system call) but still has an entry in the process table: it is a process in the “Terminated state”.

意思大概是,在unix和类unix的计算机操作系统中,如果一个进程已经结束了(通过执行系统调用exit函数),但是仍然有一个实体存在于进程表中,那么这就是一个僵尸进程,僵尸进程实际是一种“终止状态”进程。

This occurs for child processes, where the entry is still needed to allow the parent process to read its child’s exit status: once the exit status is read via the wait system call, the zombie’s entry is removed from the process table and it is said to be “reaped”.

在子进程中通常发生,当子进程结束后,其实体需要被父进程通过系统调用wait去读取,一旦使用wait系统调用读取后,子进程的实体就被从进程表里面去除了。

A child process always first becomes a zombie before being removed from the resource table. In most cases, under normal system operation zombies are immediately waited on by their parent and then reaped by the system – processes that stay zombies for a long time are generally an error and cause a resource leak.

一个子进程通常在被从资源表中移除之前,首先成为一个僵尸进程,在大多数情况下,处于常规操作系统中,父进程将会立即等待其僵尸子进程,然后清除子进程结束状态,因为如果一个僵尸进程长时间的驻留在进程表中,会导致错误并引起资源泄露。

由上面的内容可知,僵尸进程是不可避免的,但是可以消除,即将这个僵尸进程时间窗口变小。利用wait系统调用,由父进程为子进程收尸。

our parent becomes init as soon as our real parent exits

这是需要明白的第二个概念,在最上面的程序中,当第二个子进程fork出另外一个子进程后,调用exit()结束了自己的生命,他的子进程的父进程变成了init。

还是在wiki中寻找答案:

Zombie processes should not be confused with orphan processes: an orphan process is a process that is still executing, but whose parent has died. These do not remain as zombie processes; instead, (like all orphaned processes) they are adopted by init (process ID 1), which waits on its children. The result is that a process that is both a zombie and an orphan will be reaped automatically

僵尸进程和孤立进程是有区别的,孤立进程是仍然执行的进程而其父进程已经结束。这些孤立进程不是僵尸进程,他们被init进程(进程号为1)接管,init进程将一直等待其接管的子进程。如果一个子进程结束了,但是其父进程没有使用wait系统调用来清除其结束状态并且也结束了,那么这个子进程将会变成僵尸进程和孤立进程,他将会自动清除。

按照上面的说法,孤立进程不可怕可怕的是僵尸进程。只有孤立进程会被init进程重新收养,为了避免僵尸进程,在使用wait系统调用是可以解决的,但是这是一种被动等待。如果把潜在的可能成为僵尸进程变成孤立进程是不是可以呢。

在原书中的进程fork情况是这样的:



所以将进程c变成了孤立进程,那么由init进程收养。这个问题解决了

网友对此有一段看法,

这个代码并不是通用场景的避免僵尸进程的办法,它的场景是这样的:

一个进程要创建一个进程,两个进程同时处理任务,谁也不耽误谁。如果直接用子进程充当第二个进程的角色,那么问题是这样的:如果父进程处理时间长,子进程处理时间短,那么如果父进程不 wait() 处理的话,子进程就会成为僵尸进程,但如果父进程 wait() 子进程的话,父进程就会阻塞,所有有个方法就是让自己尽快推出,任务让子进程的子进程来处理。

这个场景的 APUE 的原文描述:If we want to write a process so that it forks a child but we don’t want to wait for the child to complete and we don’t want the child to become a zombie until we terminate, the trick is to call fork twice.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: