1. wait 函数资源回收
1.1 wait() 函数
父进程要负责阻塞(睡眠)进程,给子进程 回收资源,防止僵尸进程产生.
pid_t wait(int *status);
功能:父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:
① 阻塞等待子进程退出
② 回收子进程残留资源
③ 获取子进程结束状态(退出原因)。
返回值:pid_t 成功会返回子进程的ID的号, 失败是-1
子进程的状态通过status返回,但是其值需要解析是否正常退出,以及退出时的值是多少.
1.2 宏:WIFEXITED和WEXITSTATUS
可以理解为wait函数的传入参数status,对于它的解析有两个宏,WIFEXITED只是个有分类范畴的宏,只判断真假正常与否.另一个宏WEXITSTATUS,就是解析出正常退出的值是多少.
WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。
WEXITSTATUS(status) 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status)就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。看下例子中的注释就明白此点的意思了.
如何更加容易的记住这些宏,IFEXITED意思就是if exited?是否正常退出.是正常退出的话,那么EXITSTATUS意思就是exit时的status值,正常退出时的状态值,W只是个标识前缀罢了.
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
pid_t pid = fork();
//int pid = 100;
if(pid < 0){
perror("fork");
return -1;
}
else if(pid > 0){ //父进程
//父进程要通过wait函数来对子进程进行回收
int status;
printf("父进程:pid = %d,ppid = %d\n",getpid(), getppid());
pid_t pid = fork();
if(pid < 0){
perror("fork");
return -1;
}
else if(pid > 0){
//while(1) //①打开之后等待所有子进程
//{
int ret = wait(&status); //如果该进程没有子进程,wait会立即返回失败
//回收子进程wait函数是否会引起阻塞???wait会阻塞,直到子进程执行完毕,返回。
if(ret < 0){
printf("wait 失败\n");
return -1;
}
printf("父进程已wait到子进程%d, status = %d\n",ret, status);
if(WIFEXITED(status)){//WIFEXITED(status) 子进程正常返回,则为真
printf("子进程%d已正常退出,退出的值:status = %d\n", ret,WEXITSTATUS(status));//解析status获取子进程退出的各种状态
}
else{
printf("非正常退出\n");
}
//}
}
else if(0 == pid){
sleep(2);
printf("子进程2:pid = %d, ppid = %d\n", getpid(), getppid());
exit(2);
}
//while(1);
}
else if(0 == pid) {//子进程
sleep(3);
printf("子进程1:pid = %d, ppid = %d\n", getpid(), getppid());
exit(10);
}
return 0;
}
运行结果:
通过以上结果,说明的问题只有一个,就是wait只能等待一个子进程的退出,不能等待两个.
将上述代码的注释 ① 处while(1)打开,会等待所有进程,最终的程序运行结果是:
最后wati失败,是因为所有子进程正常退出后,如果该进程没有子进程,wait会立即返回失败.
注意:如果一个父进程,一个子进程,子进程没有正常退出exit,父进程没有fork判断pid>0,但是此时WIFEXITED(status)还是会正常非0.因此,用这东西,就要子进程正常exit,然后父进程wait回收.
2. waitpid()函数资源回收
2.1 waitpid()函数
pid_t waitpid(pid_t pid, int *status, int options);
参数pid:父进程监听的子进程的进程号
- Pid = -1,等待任一个子进程。与wait等效。
- Pid > 0, 等待其进程ID与pid相等的子进程。
参数status:子进程的退出状态
参数options: 0 阻塞 / WNOHANG不阻塞
wait (&status) == waitpid (-1, &status , 0) ;
返回值:-1, 0, 大于0
- 若waitpid等到了正常终止的子进程,则返回该子进程的ID(即为大于0 的情况);
- 若设置了WNOHANG的值,而等不到正常退出的子进程,即返回0;
- 若设置了阻塞模式没有子进程,调用waitpid函数出错,则返回-1;
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//pid_t process1;
int main(int argc, char const *argv[])
{
pid_t pid1 = fork();
if(pid1 < 0){
perror("fork");
return -1;
}
else if(0 == pid1){//子进程
sleep(5);
printf("子进程1:pid = %d, ppid = %d\n",getpid(), getppid());
exit(10);
}
else if(pid1 > 0){//父进程
printf("父进程:pid = %d, ppid = %d\n",getpid(), getppid());
int stat = 0;
pid_t pid2 = fork();
if(pid2 < 0) {
perror("fork");
return -1;
}
else if(0 == pid2){
sleep(3);
printf("子进程2:pid = %d, ppid = %d\n",getpid(), getppid());
exit(15);
}
else if(pid2 > 0){
int ret = waitpid(-1, &stat, 0); //等同于wait ①
//int ret = waitpid(pid1, &stat, 0); //等待特定pid进程 ②
//int ret = waitpid(pid1, &stat, WNOHANG); //无阻塞回收,特定pid进程 ③
if(ret < 0){
perror("waitpid ");
return -1;
}
printf("父进程: ret = %d\n", ret);
if(WIFEXITED(stat)){
printf("正常退出,退出的值:status = %d\n", WEXITSTATUS(stat));
}
else{
printf("非正常退出\n");
}
sleep(5);
printf("完结\n");
}
}
return 0;
}
运行结果如下:
注释②代码打开,关掉①,运行结果如下:
注释③代码打开,关掉①②,运行结果如下:
2.2 将waitpid设置成WNOHANG非阻塞,父进程一定要回收资源的用法如下:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pid_t pid, wpid;
int stat;
wpid = waitpid(-1, &stat, WNOHANG);
printf("first-pid: %d\n", wpid);
pid=fork();
if(pid < 0)
printf("error occurred!\n");
if(pid == 0){
printf("child\n");
sleep(5);
exit(0);
}
printf("parent\n");
wpid = waitpid(-1, &stat, WNOHANG);
printf("second-pid: %d\n", wpid);
sleep(7);
wpid = waitpid(-1, &stat, WNOHANG);
printf("third-pid: %d\n", wpid);
exit(0);
}
运行结果如下:
第一次调用waitpid时:此时尚未有子进程,所以waitpid出错,返回-1;
第二次调用waitpid时:此时有子进程,但子进程尚未结束,由于waitpid设置为非阻塞的,所以waitpid返回0;
第三次调用waitpid时:此时有子进程,所以waitpid返回子进程id;
虽然waitpid可以设置成非阻塞的状态,但是如果父进程只调用一个waitpid,此时子进程没有结束,因此父进程就会直接略过不回收子进程的资源,因为waitpid只是返回了0,程序跑过去了,并没有回头.因此,如果非要用waitpid()或者wait()函数来回收子进程资源的话,势必会造成父进程的阻塞,waitpid非阻塞模式要么需要多调用很多次,并且sleep稍微等待一下,才能检测到子进程结束.但这样是不合理的,所以,个人感觉非阻塞模式,一般很少用.
虽然waitpid提供了wait所没有的三个特性:
1 waitpid使我们可以等待指定的进程,等待所有的进程.(但也是等第一个结束的子进程,不等待所有)
2 waitpid提供了一个无阻塞的wait
3 waitpid支持工作控制
综合所述,只需要记住waitpid阻塞回收特定进程的功能即可,其他无所谓.