前边文章提到fork与vfork可以产生一个新进程,当子进程使命结束时会调用exit函数。但是调用exit并不会使子进程完全消失,而是转为一个僵尸进程(zombie)。僵尸进程已将原进程占用的绝大部分内存空间释放,也几乎不占用CPU,它不错任何事情,只是等待父进成来获取原子进程的结束信息。僵尸进程的存在有其实际意义,它可以为程序员提供许多重要信息,如子进程结束时的状态(正常结束还是异常结束),子进程占用的CPU时间等等,下边的代码将演示如何产生一个zombie
/***zombie.c***/
#include<sys/types.h>
#include<stdio.h>
#include<unistd.h>
#include<wait.h>
int main()
{ pid_t pid;
if((pid=fork())<0)
{printf("fork error");
exit(0);
}
else if(pid==0) /*child process exit ant wait its parent to fetch its exit status*/
exit(0);
else if(pid>0)
sleep(10); /*parten process sleep for 10s so thar we can see the zombie*/
if(wait(NULL)==-1)
printf("wait error");
printf("parent exit");
exit(0);
}
编译,运行
$gcc -o zombie zombie.c
$./zombie &
此时在终端运行ps -l 查看进程信息有
$ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 10460 10139 0 75 0 - 1419 wait pts/0 00:00:00 bash
0 S 0 24484 10460 0 78 0 - 346 - pts/0 00:00:00 zombie
1 Z 0 24485 24484 0 78 0 - 0 exit pts/0 00:00:00 zombie <defunct>
0 R 0 24821 10460 0 76 0 - 1273 - pts/0 00:00:00 ps
可以看到
1 Z 0 24485 24484 0 78 0 - 0 exit pts/0 00:00:00 zombie <defunct>
中第二列(即进程状态)为Z(zombie)
zombie不占用内存也不占用CPU,表面上我们可以不用在乎它们的存在,然而事实上UNIX系统限制了某一时刻能同时存在的进程的最大数目。如果程序不及时清理系统中的zombie,最终会导致进程数过多,当再次需要产生新进程时就会出错。
鉴于上边的原因,我们需要在子进程调用exit后在父进成中调用wait或waipid
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int &statloc);
pid_t waitpid(pid_t pid,int *statloc, int options);
Both return:process ID if OK,-1 on error
它们被父进程调用以获取子进程结束信息、清除zombie。当父进成调用这两个函数时
a 阻塞(如果它的子进程还在运行)
b 立即返回子进程结束信息(如果一个子进程已经结束并等待父进程获取信息)
c 返回错误(如果不存在子进程)
waitpid提供了wait所没有的三个特性:
1 waitpid使我们可以等待指定的进程
2 waitpid提供了一个无阻塞的wait
3 waitpid支持工作控制
以下代码来自APUE page202
Exp:
#include<sys/types.h>
#include<sys/wait.h>
#include "ourhde.h"
int main(void)
{ pid_t pid;
if( (pid=fork() )<0)
err_sys("fork error");
else if(pid==0){
if((pid=fork())<0)
err_sys("fork error");
else if(pid>0)
exit(0); /*parent from second fork == first child */
/* We're the second child;our parent becomes init as soon as our real parent calls exit() in the statement above. Here's where we'd continue executing,knowing that when we're done,init wil reap our status*/
sleep(2);
printf("second child ,parent pid=%d/n",getppid());
exit(0);
}
if(waitpid(pid,NULL,0)!=pid)
err_sys("waitpid error");
/* We're the parent (the original process);we continue executing,knowing that we're not the parent of the second child.*/
exit(0);
}
$a.out
$second child, parent pid = 1