1.概念
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程
2.产生
实例1:子进程、父进程、孤儿进程
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#define child_debug(_fmt_, ...) printf("[ child] l:%d,"_fmt_"\r\n",__LINE__,##__VA_ARGS__)
#define parent_debug(_fmt_, ...) printf("[parent] l:%d,"_fmt_"\r\n",__LINE__,##__VA_ARGS__)
int main()
{
pid_t pid;
//创建一个进程
printf("#####fork test#######\n");
pid = fork();
printf("fork() pid=%d\n",pid);
//创建失败
if (pid < 0)
{
perror("fork error:");
exit(1);
}
//子进程点击打开链接
if (pid == 0)
{
child_debug(" child process");
//输出进程ID和父进程ID
child_debug(" pid:%d\tppid:%d",getpid(),getppid());
child_debug(" sleep five seconds");
//睡眠5s,保证父进程先退出
sleep(5);
//子进程成孤儿进程,被init进程收养
child_debug("pid:%d\tppid:%d",getpid(),getppid());
child_debug(" child exited");
}
//父进程
else
{
parent_debug(" child processid=%d",pid);
//父进程睡眠1s,保证子进程输出进程id
sleep(1);
parent_debug("#########commod ps##########");
system("ps");
parent_debug("parent exited");
}
return 0;
}
实例2:僵尸进程
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#define child_debug(_fmt_, ...) printf("[ child] l:%d,"_fmt_"\r\n",__LINE__,##__VA_ARGS__)
#define parent_debug(_fmt_, ...) printf("[parent] l:%d,"_fmt_"\r\n",__LINE__,##__VA_ARGS__)
int main()
{
pid_t pid;
//创建一个进程
printf("#####fork test#######\n");
pid = fork();
printf("fork() pid=%d\n",pid);
//创建失败
if (pid < 0)
{
perror("fork error:");
exit(1);
}
//子进程
if (pid == 0)
{
child_debug(" child process");
//输出进程ID和父进程ID
child_debug(" pid:%d\tppid:%d",getpid(),getppid());
//保证子进程先退出,而父进程没有wait/waitpid获取子进程状态,将成为僵尸进程
parent_debug("#########commod ps##########");
system("ps");
child_debug("pid:%d\tppid:%d",getpid(),getppid());
child_debug(" child exited");
}
//父进程
else
{
parent_debug(" child processid=%d",pid);
//父进程睡眠10s,保证子进程先退出
sleep(10);
parent_debug("#########commod ps##########");
system("ps -o pid,ppid,state,tty,command");
parent_debug("parent exited");
}
return 0;
}
实例3:避免僵尸进程方法
1、wait/waitpid,但是会使父进程阻塞;(可考虑子进程创建线程,在线程中wait/waitpid)
2、fork twice, 用孙子进程去完成子进程的任务(注意这种方法的使用情景)
3、signal(SIGCHLD,SIG_IGN), 并不是所有系统都兼容 --(备注:忽略子进程退出信号)
4、sigaction + SA_NOCLDWAIT, 并不是所有系统都兼容--(没使用过)
5、推荐方法:在signal handler中调用 waitpid (下面的例子能说明用 waitpid 而不用 wait的原因),这样父进程不用阻塞
注意:每一种方法都有它适用的场合,比如方法 5 适用于 one-request-one-process 的网络服务器程序。
方法1:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#define child_debug(_fmt_, ...) printf("[ child] l:%d,"_fmt_"\r\n",__LINE__,##__VA_ARGS__)
#define parent_debug(_fmt_, ...) printf("[parent] l:%d,"_fmt_"\r\n",__LINE__,##__VA_ARGS__)
int main()
{
pid_t pid;
//创建一个进程
printf("#####fork test#######\n");
pid = fork();
printf("fork() pid=%d\n",pid);
//创建失败
if (pid < 0)
{
perror("fork error:");
exit(1);
}
//子进程
if (pid == 0)
{
child_debug(" child process");
//输出进程ID和父进程ID
child_debug(" pid:%d\tppid:%d",getpid(),getppid());
child_debug(" sleep five seconds");
parent_debug("#########commod ps##########");
system("ps");
child_debug("pid:%d\tppid:%d",getpid(),getppid());
child_debug(" child exited");
}
//父进程
else
{
parent_debug(" child processid=%d",pid);
int status=0;
int pid_ret=waitpid(pid,&status,0);
//根据status状态值,确认子进程退出是否正常?coredump?还是SIGPIPE?等问题
parent_debug(" child pid=%d,pid_ret=%d",pid,pid_ret);
//WIFEXITED return non-zero if child exited normally
parent_debug("WIFEXITED(status) == %d",WIFEXITED(status));
//WEXITSTATUS get the return code
parent_debug(" The return code WEXITSTATUS(status) == %d",WEXITSTATUS(status));
//returns true if the child process was terminated by a signal
parent_debug(" WIFSIGNALED(status) == %d",WIFSIGNALED(status));
//returns the number of the signal that caused the child process to terminate.
//This macro should only be employedif WIFSIGNALED returned true.
parent_debug(" WTERMSIG(status) == %d", WTERMSIG(status));
//returns true if the child produced a core dump. This macro should only be employed if WIFSIGNALED returned true.
//This macro is not specified in POSIX.1-2001 and is not available on some Unix implementations (e.g., AIX, SunOS).
// Only use this enclosed in #ifdef WCOREDUMP ... #endif.
parent_debug(" WCOREDUMP(status) == %d", WCOREDUMP(status));
//returns true if the child process was stopped by delivery of a signal; this is only possible if the call was done
//using WUNTRACED or when the child is being traced (see ptrace(2)).
parent_debug(" WIFSTOPPED(status) == %d",WIFSTOPPED(status));
//returns the number of the signal which caused the child to stop. This macro should only be employed if WIFSTOPPED
// returned true
parent_debug(" WSTOPSIG(status) == %d",WSTOPSIG(status));
// (since Linux 2.6.10) returns true if the child process was resumed by delivery of SIGCONT
parent_debug(" WIFCONTINUED(status) == %d",WIFCONTINUED(status));
parent_debug("parent exited");
}
return 0;
}
参考资料:
避免产生僵尸进程的N种方法:http://blog.csdn.net/duyiwuer2009/article/details/7964795
孤儿进程与僵尸进程[总结]:https://www.cnblogs.com/Anker/p/3271773.html