一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。
父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
例如:一个进程的退出状态(比如:return 0;)可以在Shell中用特殊变量$?查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态的同时彻底清除掉这个进程。
如果一个进程已经终止,但是它的父进程尚未调用wait或waitpid对它进行清理,这时的进程状态称为僵尸(Zombie)进程,可以用ps u查看进程状态。
父进程调用wait或waitpid时可能会:
1.阻塞(如果它的所有子进程都还在运行)。ps:Shell就是阻塞等着子进程运行完毕。
2.带子进程的终止信息立即返回(如果一个子进程已终止,正等待父进程读取其终止信息)。
3.出错立即返回(如果它没有任何子进程)。
wait和waitpid区别:
如果父进程的所有子进程都还在运行,调用wait将使父进程阻塞,而在调用waitpid时如果在options参数中指定WNOHANG可以使父进程不阻塞而立即返回0。wait等待第一个终止的子进程,而waitpid可以通过pid参数指定等待哪一个子进程。
用waitpid函数获取子进程死亡原因:
#include "./common/head.h"
/*功能:
*用waitpid函数获取子进程死亡原因。
*/
int main()
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(1);
}
if(pid == 0){ //子进程
int n = 5;
while(n--){ //子进程5秒后结束
printf("this is child process\n");
sleep(1);
}
exit(3); //父进程中打印正常退出代码为3
}else{ //父进程
int stat_val;
//等待子进程退出
wait(pid, &stat_val, 0); //pid为-1时代表等待所有子进程结束,子进程结束的信息存储在stat_val中,可以用以下的宏来进行判断
if(WIFEXITED(stat_val)){ //子进程正常退出
//打印正常退出代码,即上面返回的3
printf("Child exited with code %d\n", WEXITSTATUS(stat_val));
}else if(WIFSIGNALED(stat_val)){ //子进程是被信号终止的
//打印信号
printf("Child terminated abnormally, signal %d\n", WTERMSIG(stat_val));
}
}
return 0;
}