前边文章提到fork与vfork可以产生一个新进程,当子进程使命结束时会调用exit函数。但是调用exit并不会使子进程完全消失,而是转为一个僵
尸进程(zombie)。僵尸进程已将原进程占用的绝大部分内存空间释放,也几乎不占用CPU,它不错任何事情,只是等待父进成来获取原子进程的结束信
息。僵尸进程的存在有其实际意义,它可以为程序员提供许多重要信息,如子进程结束时的状态(正常结束还是异常结束),子进程占用的CPU时间等等,下边的
代码将演示如何产生一个zombie
/***zombie.c***/
#include
#include
#include
#include
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
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
中第二列(即进程状态)为Z(zombie)
zombie不占用内存也不占用CPU,表面上我们可以不用在乎它们的存在,然而事实上UNIX系统限制了某一时刻能同时存在的进程的最大数目。如果程序不及时清理系统中的zombie,最终会导致进程数过多,当再次需要产生新进程时就会出错。
鉴于上边的原因,我们需要在子进程调用exit后在父进成中调用wait或waipid
#include
#include
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 返回错误(如果不存在子进程)
两个函数的不同在于wait会令调用者阻塞直至某个子进程终止而waitpid则可以通过设置一个选项来设置为非阻塞,另外waitpid并不是等待第一个结束的进程而是等待参数中pid指定的进程。
两个函数中的变量statloc是一个指向int型数据的指针。如果此变量不是NULL,则结束的进程的termination
status会被保存在statiloc所指向的内存的区域;如果我们不关心termination
status,则可以把statloc置为NULL。
传统的实现中这两个函数返回的整数中特定的比特位被赋予了特定的含义。POSIX.1指定了一些包含在头文件 宏来查看这些termination status
Macro Description
WIFEXITED(status)
如果status是由一个正常结束的进程产生的则值为真,
此时我们可以继续使用宏WEXITSTATUS(status)来
获取exit或_exit的参数
WIFSIGNALED(status)
如果status是由一个异常结束(接受到一个信号)的进
程产生的则值为真,此时使用宏WTERMSIG(status)来
获取信号数。
WIFSTOPPED(status)
如果status是由一个接受到信号目前终止的进程产生的
则值为真,此时可以继续调用宏WSTOPSIG(status)来
查看是哪个信号导致进程终止。
waitpid的option常量
WNOHANG waitpid将不阻塞如果指定的pid并未结束
WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。
waitpid中pid的含义依据其具体值而变
pid==-1 等待任何一个子进程,此时waitpid的作用与wait相同
pid >0 等待进程ID与pid值相同的子进程
pid==0 等待与调用者进程组ID相同的任意子进程
pid<-1 等待进程组ID与pid绝对值相等的任意子进程
waitpid提供了wait所没有的三个特性:
1 waitpid使我们可以等待指定的进程
2 waitpid提供了一个无阻塞的wait
3 waitpid支持工作控制
以下代码来自APUE page202
Exp:
#include
#include
#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
上述代码通过两次fork防止zombie的产生
阅读(667) | 评论(0) | 转发(0) |