我们知道一个子进程如果要结束,其内核释放进程的所有资源,但是还保存了一部分资源供父进程使用(是一个被称之为僵尸进程的数据结构,包含有进程号、运行时间、退出状态等,它需要父进程去处理),所以其父进程要调用wait或者waitpid获取子进程的状态信息,然而如果父进程没有调用这个信息会发生什么情况呢?
一个进程如果创建出子进程,如果此时子进程退出,而父进程没有进行善后工作(wait与waitpid获取子进程状态信息),那么此时子进程的进程描述符仍然保存在系统中。
如果一个父进程退出,它的子进程(有一个或者多个)还在,那么子进程将成为孤儿进程,这个时候这些孤儿进程会被1号进程,也就是init进程所收养,并且由init进程完成善后工作(状态收集)。
由于孤儿进程在最后都会被一个一号进程也就是init进程所收养,而init进程会对这个孤儿进程进行善后工作,所以一般情况下我们不需要处理孤儿进程。
但是僵尸进程如果不处理会占用大量资源,所以我们必须要对僵尸进程进行处理,一般的处理方式我们有如下两种:
信号处理是一般的做法,一般子进程退出的时候向父进程发送SIGCHILD信号,所以我们只需要捕捉这个信号,在信号处理函数中进行wait(),对其子进程进行善后工作,将不会产生僵尸进程。
如果父进程处理时间长,子进程处理时间短,如果父进程不wait()处理的话,子进程就会产生僵尸进程,但是如果父进程进行了wait()处理,那么父进程又会产生阻塞,所以解决方法就是让自己尽快退出,任务让子进程的子进程来处理。
fork()两次的做法是在unix环境高级编程一书中所提到的,其方法是在fork第一次进程的里面再用一次fork,然后让第一次fork出来的子进程变为孤儿进程,交给init进程进行处理,自己则用waitpid善后即可。
#include
#include
#include
int main()
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork error");
exit(1);
}
//子进程
if (pid == 0)
{
printf("child\n");
printf("My Pid:%d,My Father pid:%d\n", getpid(), getppid());
//保证父进程退出
sleep(5);
printf("My Pid:%d,My Father pid:%d\n", getpid(), getppid());
}
else
{
printf("I am father\n");
sleep(1);
printf("father exit!\n");
}
return 0;
}
提供了如下两种僵尸进程的方式:
#include
#include
#include
int main()
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork error");
exit(1);
}
//子进程
if (pid == 0)
{
printf("child\n");
printf("My Pid:%d,My Father pid:%d\n", getpid(), getppid());
printf("I exit!\n");
exit(0);
}
printf("I am father\n");
//父进程等待1秒不处理子进程
sleep(1);
//打印进程信息
system("ps -o pid,ppid,state,tty,command");
printf("father exit!\n");
return 0;
}
#include
#include
#include
#include
#include
static void sig_child(int signo);
int main()
{
pid_t pid;
signal(SIGCHLD, sig_child);
pid = fork();
if (pid < 0)
{
perror("Fork error");
exit(1);
}
if (pid == 0)
{
printf("I am child %d!\n", getpid());
exit(1);
}
printf("I an father! \n");
sleep(3);
system("ps -o pid,ppid,state,tty,command");
printf("father exit!\n");
return 0;
}
static void sig_child(int signo)
{
pid_t pid;
int stat;
while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
printf("child %d terminated.\n", pid);
}
#include
#include
#include
#include
int main()
{
pid_t pid = fork();
if (pid < 0)
{
perror("Fork error!\n");
exit(1);
}
if (pid == 0)
{
printf("The first pid:%d,ppid:%d\n", getpid(), getppid());
//此时fork第二次
pid = fork();
if (pid < 0)
{
perror("Fork error!\n");
exit(1);
}
else if (pid > 0)
{
//第一次创建的进程退出
printf("first exit!\n");
exit(0);
}
if (pid == 0)
{
//子进程休息三秒保证第一次fork出的进程退出,此时自己就变为孤儿进程了。
printf("I an second,pid:%d,ppid:%d\n", getpid(), getppid());
sleep(3);
exit(0);
}
}
//这里是处理第一个进程
if (waitpid(pid, NULL, 0) != pid)
{
perror("waitpid error!\n");
exit(1);
}
exit(0);
return 0;
}