子进程是父进程调用fork生成的,如果父进程在子进程之前结束,那么对于对应的父进程都已经结束的所有子进程,它们的父进程都会变成init进程,也就是这些子进程被init进程领养;在一个进程结束时,内核会逐个检查所有的活动进程,来判断它是否是正要结束的子进程,如果是,就会将该进程的父进程ID改为1,也就是init这个进程,如此就保证了每个进程都有一个父进程
如果子进程在父进程之前结束,子进程会将其结束状态返回给父进程;内核为每个结束的子进程保存了一定的信息,所以当子进程的父进程在调用wait或者waitpid的时候,是可以得到这些状态信息的,比如进程ID,结束状态等
进程终止后,内核会释放该进程所使用的所有存储区,关闭所有打开的文件,而一个已经终止但是其父进程尚未对其进行善后处理,也就是没有获取终止子进程的有关信息,释放它仍占用的资源,这样的一个已经终止的进程就是僵尸进程
前面的例子,父进程打印1次,而子进程打印3次,只需要将他们打印执行的次数交换一下,即父进程打印3次,子进程打印1次,并且不进行任何wait,在执行这个程序过程中,不停通过ps来查看进程,就会看到僵尸进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(){
pid_t pid;
char *message;
int n;
if ((pid = fork()) < 0) {
printf("fork failed...\n");
exit(1);
} else if (pid == 0){
message = "child now...";
n = 1;
} else {
message = "parent now...";
n = 3;
}
for(; n > 0; n--){
puts(message);
sleep(1);
}
return 0;
}
编译运行,与此同时执行ps命令:
$ ./a.exe
parent now...
child now...
$ ps aux
PID PPID PGID WINPID TTY UID STIME COMMAND
8244 3964 8244 4704 pty1 1000 14:09:50 /usr/bin/ps
3964 4604 3964 5092 pty1 1000 12:03:03 /usr/bin/bash
5048 1 5048 5048 ? 1000 11:42:52 /usr/bin/mintty
6960 2216 2216 6960 pty0 1000 14:09:49 /home/lihui/a <defunct>
2216 4372 2216 6432 pty0 1000 14:09:49 /home/lihui/a
4372 5048 4372 4228 pty0 1000 11:42:52 /usr/bin/bash
4604 1 4604 4604 ? 1000 12:03:03 /usr/bin/mintty
由于父进程并没有wait等函数,因此子进程结束之后变成了僵尸进程,在父进程还没有完全打印完的时候,可以看到一个defunct的进程,父进程此刻没有为其善后,可是过了一会儿,再用ps命令查看,僵尸进程没了,想要看更直观更明显效果的话,可以直接父进程里面来一个sleep N秒,这样僵尸进程就会一直存在,直到父进程苏醒
$ ./a.exe
parent now...
child now...
parent now...
parent now...
$ ps aux
PID PPID PGID WINPID TTY UID STIME COMMAND
3964 4604 3964 5092 pty1 1000 12:03:03 /usr/bin/bash
5048 1 5048 5048 ? 1000 11:42:52 /usr/bin/mintty
I 4372 5048 4372 4228 pty0 1000 11:42:52 /usr/bin/bash
7000 3964 7000 4772 pty1 1000 14:37:54 /usr/bin/ps
4604 1 4604 4604 ? 1000 12:03:03 /usr/bin/mintty
但是由于父进程并没有添加wait和waitpid,因此父进程不会主动去回收子进程资源善后,我觉得是父进程结束之后,子进程被init收养,最终结束的~!
