如下程序,即 fork()求质数 改动:
实验1 父进程不睡眠,201个子进程睡眠
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#define LEFT 200
#define RIGHT 250
int main(void)
{
int i,j,mark;
pid_t pid;
for(i = LEFT; i <= RIGHT; i++)
{
pid = fork();
if(pid < 0)
{
fprintf(stderr,"fork() failed!\n");
exit(1);
}
else if(pid == 0)//child
{
mark = 1;
for(j = 2; j < i/2; j++)
{
if(i % j ==0)
{
mark = 0;
break;
}
}
if(mark)
printf("%d is a primer\n",i);
sleep(1000);
exit(0);//!!!
}
}
exit(0);
}
改动:每个子进程结束前 睡眠1000秒,这样的话 父进程一定是先结束,而201个子子进程后结束。这种情况 父进程创建201个子进程后就直接 exit()结束了,而子进程干完活就 sleep了。
ps axf 查看进程状态:
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ ps axf
15800 pts/2 S 0:00 \_ ./a.out
15801 pts/2 S 0:00 \_ ./a.out
15802 pts/2 S 0:00 \_ ./a.out
15803 pts/2 S 0:00 \_ ./a.out
15804 pts/2 S 0:00 \_ ./a.out
15805 pts/2 S 0:00 \_ ./a.out
15806 pts/2 S 0:00 \_ ./a.out
15807 pts/2 S 0:00 \_ ./a.out
15808 pts/2 S 0:00 \_ ./a.out
15809 pts/2 S 0:00 \_ ./a.out
15810 pts/2 S 0:00 \_ ./a.out
15811 pts/2 S 0:00 \_ ./a.out
15812 pts/2 S 0:00 \_ ./a.out
15813 pts/2 S 0:00 \_ ./a.out
15814 pts/2 S 0:00 \_ ./a.out
15815 pts/2 S 0:00 \_ ./a.out
15816 pts/2 S 0:00 \_ ./a.out
15817 pts/2 S 0:00 \_ ./a.out
15818 pts/2 S 0:00 \_ ./a.out
15819 pts/2 S 0:00 \_ ./a.out
15820 pts/2 S 0:00 \_ ./a.out
...
...
...
可以看到 201个子进程全部是顶格写的,故 201进程的父进程是 init进程
对于父进程已经终止的所有进程,它们的父进程都改变为 init 进程,我们称 这些进程由 init进程收养。其操作过程大致是: 在一个进程A终止时,内核会逐个检查所有其他的活动进程 B C D…等等,来判断这些活动进程 是不是终止进程A 的子进程,如果是的话,则活动进程的父进程更改为1(init 进程的ID 为1),这种处理方法保证了每一个进程都有一个父进程。
实验2 父进程睡眠,子进程不睡眠
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#define LEFT 200
#define RIGHT 250
int main(void)
{
int i,j,mark;
pid_t pid;
for(i = LEFT; i <= RIGHT; i++)
{
pid = fork();
if(pid < 0)
{
fprintf(stderr,"fork() failed!\n");
exit(1);
}
else if(pid == 0)//child
{
mark = 1;
for(j = 2; j < i/2; j++)
{
if(i % j ==0)
{
mark = 0;
break;
}
}
if(mark)
printf("%d is a primer\n",i);
exit(0);//!!!
}
}
sleep(1000);
exit(0);
}
mhr@ubuntu:~/Desktop/xitongbiancheng/test$ ps axf
\_ zeitgeist-datahub
2824 ? Sl 0:37 \_ /usr/bin/python /usr/bin/terminator
2836 ? S 0:00 | \_ gnome-pty-helper
2837 pts/0 Ss 0:01 | \_ /bin/bash
16001 pts/0 R+ 0:00 | | \_ ps axf
2858 pts/2 Ss 0:00 | \_ /bin/bash
15944 pts/2 S+ 0:00 | \_ ./a.out
15945 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
15946 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
15947 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
15948 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
15949 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
15950 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
15951 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
15952 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
15953 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
15954 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
15955 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
15956 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
15957 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
15958 pts/2 Z+ 0:00 | \_ [a.out] <defunct>
...
....
....
3692 ? Sl 0:02 \_ /usr/lib/x86_64-linux-gnu/notify-osd
本实验,由于父进程没有退出,故所有的子进程的 父进程依然是 之前的父进程,并没有转交给 init进程。
可以看到 此时的201个子进程 的进程状态为 Z+ 即 zombie 僵尸进程。那么什么是僵尸进程,僵尸进程是如何产生的?
在UNIX术语中,一个已经终止,但是其父进程尚未对其进行善后处理(获取终止子进程的有关信息,释放它仍然占用的资源)的进程成为 僵尸进程。
所以 对于上述例子,父进程fork了201个子进程,除非父进程等待获取子进程的终止状态,否则这些子进程终止后就会变成僵尸进程,如上。关于如果对子进程进行善后处置,用 wait()/waitpid() 处理,下节详细说明。
还有一个注意事项,一个由 init 进程收养的进程终止的时候会发生什么?他会不会变成一个僵尸进程?
答案是否定的,init进程被设计成 无论何时 只要有一个子进程终止, init就会调用一个 wait() 函数对其进行善后处理,获取其终止状态。这样就防止了系统中塞满僵尸进程。