wait() 与 waitpid() 函数的使用.
因为前段时间博主没有把Windows系统带走所以不能实时更新博客,请见谅。接下来就让我们继续开始进程的世界吧。
前面我们说到了如何创建多个子进程那么现在就让我们来消灭那些僵尸进程吧。
那么我们回顾一下,僵尸进程是不能使用kill命令清除掉的。因为kill命令只是用来终止进程的,而僵尸进程本身已经终止了。那么我们有什么办法来清楚僵尸进程呢?
如何查看僵尸进程:
销毁僵尸进程1. wait函数
父进程调用wait 函数可以回收子进程终止信息。我们的wait函数由三个功能:
1. 阻塞等待子进程退出
2. 回收子进程残留资源
3. 获取子进程结束的状态。
(博主在这里解释一下什么是子进程结束状态。因为在程序运行时,子进程可能是正常结束,但也可能异常结束,可能是调用kill命令直接终止,那么,如果子进程不能保留自己的进程结束状态,那么父进程就不知道紫禁城是如何结束的)。
#include<sys/wait.h> pid_t wait(int *status); --->成功: 返回终止的子进程ID 失败: 返回-1
下面给出电子书中的内容,大家可以记一下下面的结构.()其实不是很完整,博主将在下面给出大家更好的体系,大家可以跟博主一样使用下面的语句)。
上面可以看出图片上如果是正常返回那么我们就能知道正常终止的子进程ID是多少,但是大家有没有想过,万一是异常终止呢???那我么应该怎么去知道是哪一个终止了我们的子进程呢??所以这就引出了我们下面的内容。
为大家补充两个宏(前面两个和书上的是一致的,后面两个宏为补充的)
1.WIFEXITED(status) 为非0 -->进程正常结束
WEXITSTATUS(status) 如上宏为真,使用此宏 --> 获取进程退出状态 (exit的参数)也就是返回你的exit(x) 返回x的数值。
2.WIFSIGNALED(status) 为非0 -->进程异常终止
WTERMSIG(status) 如上宏为真,使用此宏 --> 取得使进程终止的那个信号的编号。就比如说你使用 kill -9 5031(假设5031是子进程ID)那么返回的就是 9 也就是你用9号来终止的子进程。
也就是说在我们使用wait函数之后,我们可以使用下面的代码来检测子进程是否正常终止。
if(WIFEXITED(status)){ puts("Normal termination!"); printf("child pass num :%d,"WEXITSTATUS(status)); }else{ if(WIFSIGNALED(status) ){ puts("error termination!"); printf("child killed num :%d,"WTERMSIG(status)); } } 根据上面的内容编写不仅涵盖了正常情况而且未知情况我们也可以预见,因此更加符合 防御式编程,希望大家能够就记住。
看上上面的介绍大家是不是觉得wait 函数已经很好了,正常情况我们能够知道,就连错误情况我们也能了解到,那为什么还需要waitpid函数呢??
其实是非常有必要的,博主下面给出一个例子,大家就能知道 waitpid函数的重要性了。
因为wait 函数具有阻塞性,当我们调用 wait函数是,如果没有已经终止的子进程,那么我们的程序就阻塞了啊!!!必须等到由子进程终止程序才会继续运行,在编程领域这可是非常不好的事情。所以接下来我们就隆重有请我们的waitpid函数的登场。
销毁僵尸进程2
wait函数会引起程序阻塞,那么我们就可以考虑条用 waitpid函数。这就是我们防止僵尸进程的第二种方法,也是防止阻塞的方法。
#include<sys/wait.h> pid_t witpid(pid_t pid, int *status, int options); 参数: 第一个参数 pid_t pid: > 0 回收指定ID的子进程 -1 回收任意子进程(相当于wait) 0 回收和当前调用waitpid一个组的所有子进程 < -1 回收指定进程组内的任意子进程 第二个参数 int *status. 和wait一样的 第三个参数 int options 传递头文件sys/wait.h 中声明的常量 WNOHANG,表示解释没有终止的子进程也不会进入阻塞状态,而是返回 0 并退出函数。 返回值: 成功:返回终止的子进程ID(或0) 失败:返回-1 waitpid同样可以使用我们的上面介绍的if else模型进行判断。 下面也给出大家waitpid的使用模型吧(以后一般都是使用waitpid函数来回收子进程!) 下面给出电子书上的代码(改进版)。
#include<stdio.h> #include<uistd.h> #include<sys/wait.h> int main(int argc, char *argv[]) { int status; pid_t pid = fork(); //创建子进程 if (pid == -1) { printf("fork() error"); exit(-1); }else if (pid == 0) { //子进程休眠后结束,目的是为了检测我们的 waitpid是否阻塞的小测试 sleep(15); exit(24); } else { //这就是父进程了 while (!waitpid(-1, &status, WNOHANG)) { sleep(3); puts("sleep 3sec"); } if (WIFEXITED(status)) { puts("Normal termination!"); printf("child pass num :%d,"WEXITSTATUS(status)); //如果正常的话我们能看到24 } else { if (WIFSIGNALED(status)) { puts("error termination!"); printf("child killed num :%d, "WTERMSIG(status)); } return 0; } } }
注意:“注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。 ”
好啦这就是waitpid 和 wait函数啦,他们最大的区别就是waitpid能设置为非阻塞(也能设置为阻塞),wait只能阻塞等待。
谢谢大家的观看。