1.什么是僵尸进程
子进程先于父进程结束。子进程结束后父进程此时并不一定立即就能帮子进程“收尸”,在这一段(子进程已经结束且父进程尚未帮其收尸)子进程就被成为僵尸进程。
因此我们每个进程都需要一个帮助它收尸的人,这个人就是这个进程的父进程。
父进程可以使用 wait 或 waitpid 以显式回收子进程的剩余待回收内存资源并且获取子进程退出状态。
2.wait和waitpid
wait() :
pid_t wait (int * status);
返回值:如果执行成功则返回子进程识别码(PID), 如果有错误发生则返回-1. 失败原因存于errno 中.
函数说明:wait()会暂时停止目前进程的执行, 直到有信号来到或子进程结束. 如果在调用wait()时子进程已经结束, 则wait()会立即返回子进程结束状态值. 子进程的结束状态值会由参数status 返回, 而子进程的进程识别码也会一快返回. 如果不在意结束状态值, 则参数 status 可以设成NULL. 子进程的结束状态值请参考waitpid().
waitpid() :
wait和waitpid的区别:
1. 功能基本一样,都是用来回收子进程
2. waitpid可以回收指定PID的子进程
3. waitpid可以阻塞式或非阻塞式两种工作模式
wait函数要靠下面这些宏来解析退出码
WIFEXITED、WIFSIGNALED、WEXITSTATUS这几个宏用来获取子进程的退出状态。
WIFEXITED宏用来判断子进程是否正常终止(return、exit、_exit退出)
WIFSIGNALED宏用来判断子进程是否非正常终止(被信号所终止)
WEXITSTATUS宏用来得到正常终止情况下的进程返回值的。
wait函数示例代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(void)
{
pid_t pid;
pid_t ret;
int status = 10;
pid = fork(); //fork创建子进程,返回值就是子进程ID
if (pid > 0) //父进程
{
printf("parent.\n");
ret = wait(&status);
//ret = waitpid(-1, &status, 0); 0表示阻塞式的,这里和上面wait效果相同
//ret = waitpid(pid, &status, 0); 表示阻塞方式指定回收该子进程
//ret = waitpid(pid, &status, WNOHANG); 表示用非阻塞方式指定回收该子进程
printf("子进程已经被回收,子进程pid = %d.\n", ret);
printf("子进程是否正常退出:%d\n", WIFEXITED(status));
printf("子进程是否非正常退出:%d\n", WIFSIGNALED(status));
printf("正常终止的终止值是:%d.\n", WEXITSTATUS(status));
}
else if (pid == 0)
{
// 子进程
printf("child pid = %d.\n", getpid());
return 51;
//exit(0);
}
else
{
perror("fork");
return -1;
}
return 0;
}
以上代码中: ret = waitpid(pid, &status, WNOHANG);
这种表示父进程要非阻塞式的回收子进程。此时如果父进程执行waitpid时子进程已经先结束等待回收则waitpid直接回收成功,返回值是回收的子进程的PID;如果父进程waitpid时子进程尚未结束则父进程立刻返回(非阻塞),但是返回值为0(表示回收不成功)。
输出结果:
parent.
child pid = 3016.
子进程已经被回收,子进程pid = 3016.
子进程是否正常退出:1
子进程是否非正常退出:0
正常终止的终止值是:51.
总结:
wait函数:父进程等待子进程退出 并收集子进程的退出状态
若子进程退出状态不被收集,则会变成僵死进程(僵尸进程)