使用fork创建进程非常实用,但如果对子进程的情况不了解,也可能会产生些副作用。当子进程终止时,它与父进程之间的关联还会继续保持,直到父进程结束或者调用wait。这样一来,进程表中代表子进程的表项就不会立刻释放。虽然子进程已经结束,但是它仍然在系统中存在着,这是因为父进程还需要在调用wait时使用子进程的退出码。此时,子进程就成为了僵尸(zombie)进程。
举一个会产生僵尸进程的例子:
#include
#include
#include
#include
int main()
{
pid_t pid;
char* message;
int n;
printf("fork program starting\n");
pid = fork();
switch(pid) {
case -1:
perror("fork failed\n");
exit(1);
case 0:
message = "This is the child";
n = 3;
break;
default:
message = "This is the parent";
n = 60;
break;
}
for(; n>0; n--) {
puts(message);
sleep(1);
}
exit(0);
}
如上所示,子进程输出消息的次数只有3次,3次过后,子进程就完成了自己的任务,该结束了。但是由于父进程还没有完成自己的任务,不能退出,此时的子进程就成为了僵尸进程。
如果父进程异常终止,那么子进程就会被init进程(pid=1)接管,它的父进程就会变为init进程。僵尸进程将一直保存在进程表中,直到被init进程发现并释放。进程表越大,这一过程就越慢。应该尽量避免僵尸进程的产生,因为在被释放之前,它们会一直占用系统资源。
如何避免僵尸进程的产生呢?可以使用waitpid函数。先来看看该函数的声明:
#include
#include
pid_t waitpid(pid_t pid, int* stat_loc, int options);
pid:需要等待的子进程的pid;如果为-1,表示waitpid将返回任一子进程的信息。
stat_loc:接收子进程的状态信息。
options:控制waitpid行为的选项。常用的一个值为WNOHANG,它可以防止waitpid将调用者挂起。可以用这个选项来判断是否有子进程已经结束,如果没有,程序将继续运行。
如果子进程没有结束或意外终止,该函数返回0,否则返回子进程pid。如果返回-1表示waitpid调用失败并设置errno。
举个例子:
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
char* message;
int n;
int exit_code;
printf("fork program starting\n");
pid = fork();
switch(pid) {
case -1:
perror("fork failed\n");
exit(-1);
case 0:
message = "This is the child";
n = 20;
exit_code = 37;
break;
default:
message = "This is the parent";
n = 3;
exit_code = 0;
break;
}
for(; n>0; n--) {
puts(message);
sleep(1);
}
if(pid != 0) {
pid_t child_pid;
int stat_val;
while(1) {
child_pid = waitpid(-1, &stat_val, WNOHANG);
if(child_pid) {
printf("Child process (%d) has finished", child_pid);
if(WIFEXITED(stat_val)) {
printf(", exit code %d.\n", WEXITSTATUS(stat_val));
}
else {
printf(" abnormally.\n");
}
break;
}
printf("Child process is running\n");
sleep(1);
}
}
exit(exit_code);
}
这里在一个while(1)循环中不停地检测子进程是否终止,如果停止则退出循环。
阅读(2563) | 评论(2) | 转发(0) |