僵尸进程形成方式:
1. 父进程先结束,子进程后结束,系统会处理子进程,不会形成僵尸进程。
2. 父进程后结束,子进程先结束,但是父进程在fork前,显式调用了signal(SIGCHLD, SIG_IGN),告诉系统不关心子进程退出状态,则系统会回收子进程,不会形成僵尸进程。
3. 父进程后结束,子进程先结束,父进程调用waitpid()回收子进程,不会形成僵尸进程。
4. 父进程后结束,子进程先结束,父进程既没有显式告诉系统忽略SIGCHLD信号,又没有调用waitpid()主动回收子进程,在父进程退出先,子进程形成僵尸进程。
测试代码:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#define dlog(format, ...) printf("[qmd %d %6d %s %d %s] "format, (int)time(NULL), (int)getpid()/*pthread_self()*/, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
int main(int argc, char **argv)
{
pid_t pid;
int status = 0;
if(argc == 3){
dlog("set SIGCHLD SIG_IGN\n");
signal(SIGCHLD, SIG_IGN);
}
pid=fork();
if(pid < 0){
dlog("fork error [%s]\n", strerror(errno));
return 0;
}else if(pid == 0){
dlog("child1 pid=[%d]\n", (int)getpid());
//execl("/bin/sh", "sh", "-c", "./cmd.sh", (char *)0);
dlog("child sleep 10. pid=[%d]\n", (int)getpid());
sleep(10);
dlog("child exit. pid=[%d]\n", (int)getpid());
return 0;
}else if(pid > 0){
dlog("parent pid=[%d] child pid=[%d]\n", (int)getpid(), pid);
/*while(waitpid(pid, &status, 0) < 0){
dlog("waitpid error [%s]\n", strerror(errno));
if(errno != EINTR){
break;
}
}*/
}
dlog("parent sleep 30\n");
while(1)sleep(30);
dlog("parent1 exit\n");
return 0;
}
#define system(s) qmd_system(s);
static int qmd_system(const char * cmdstring)
{
pid_t pid;
int status;
if(cmdstring == NULL){
return (1);
}
if((pid = fork())<0){
status = -1;
}else if(pid == 0){
dlog("1. system(\"%s\") child process pid=[%d]\n", cmdstring, (int)getpid());
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
exit(127);
}else{
dlog("2. system(\"%s\") parent process pid=[%d] child[%d]\n", cmdstring, (int)getpid(), pid);
while(waitpid(pid, &status, 0) < 0){
dlog("3. waitpid error [%s]. system(\"%s\") parent process pid=[%d] child[%d]\n", strerror(errno), cmdstring, (int)getpid(), pid);
if(errno != EINTR){
status = -1;
break;
}
}
}
dlog("4. return [%d]. system(\"%s\") parent process pid=[%d] child[%d]\n", status, cmdstring, (int)getpid(), pid);
return status;
}