Linux进程等待

进程等待是什么

父进程fork()出子进程之后,需要调用wait()或者waitpid()等待子进程退出。

进程等待的原因

  • 子进程退出的时候进入僵尸状态,会造成内存泄漏的问题,需要通过父进程等待去释放子进程的资源
  • 通过获取子进程退出的状态,得知子进程的执行结果
  • 在一定程度上保证了父进程后于子进程退出

进程等待的方法

1.wait()

1.1函数原型

  pid_t wait(int *status);

1.2函数参数

其中的参数status我们在接下来的waitpid中介绍,此处我们可以暂时设置他为NULL。

1.3函数返回值

函数的返回值如果 >0 则为等待子进程的pid, <0 则表示等待失败。

1.4代码实现

#include <stdio.h>
#include <unistd.h>  //fork
#include <stdlib.h>  //exit
#include <sys/wait.h>  //wait
#include <sys/types.h> 

int main()
{
  pid_t id = fork();//创建子进程
  if(id < 0){
 	//fork出错
    perror("fork error\n");
    exit(1);
  }else if(id == 0){
    //child
    int time = 5;
    while(time > 0)
    { //每隔一秒打印一次  五秒后子进程退出
      printf("child[%d] will exit %d\n",getpid(),time);//
      time--;
      sleep(1);
    }
    exit(0);
  }


  //parent  先等待7秒 也就是说子进程退出后再过2秒父进程执行
  sleep(7);
  pid_t ret = wait(NULL);  //等待子进程
  if(ret < 0){
    perror("wait error\n");//等待失败
    exit(2);
  }
  //wait success
  printf("father wait child[%d] success!\n",ret);
  sleep(10);
  return 0;
}

1.5运行结果

在这里插入图片描述
另外开一个脚本观察子进程运行状态:
在这里插入图片描述

可见在父进程等待成功之后,子进程才从僵尸状态成功释放资源退出。

2.waitpid()

2.1函数原型

pid_t waitpid(pid_t pid, int *status, int options);

2.2函数参数

pid: 有两种选择
(1) pid = -1:表示等待任意一个子进程(与wait功能相同)
(2) pid = id:此处id表示fork之后的子进程id,父进程这时候专门等待此进程

status:是一个输出型参数,有一个int空间的地址(32位),用于向指定空间中存放子进程的退出返回值
(1) 如果不关心子进程的返回值,可以设置为NULL(与wait功能相同)
(2) 高16位暂不关心,低16位中的高8位表示进程的退出码,低7位表示异常退出码,第8位coredump标志位也暂不关心

在这里插入图片描述

利用status判断进程是否正常退出:只需要取出status的低7位即可,为0表示正常退出,否则则为异常退出。 (status & 0x7f )即可取出低7位。
只有异常信号量为0(正常退出),那么进程的退出码才有意义想要取出退出码只需要右移8位然后和0xff相与即可 (status >> 8) & 0xff.

如果嫌相与太麻烦,那么可以用接下来的两个宏取代上面的步骤:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

options:有两种选择
(1) 0:表示阻塞等待。
(2) WNOHANG:表示非阻塞等待。
给大家举个简单的例子,帮助大家理解阻塞与非阻塞:
阻塞等待就像期末考试的时候考场上你没有做完卷子老师什么都不做,等你交卷子。 非阻塞等待就像平时给你卷子,你如果没有写完,老师会每隔一小时或一节课来问你写完没写完,如果你没写完,他不会一直等你,而是去收别人的卷子或者去干别的事。老师的状态就是父进程的状态,这下相信大家理解了吧。

2.3函数返回值

  • 正常返回的时候waitpid返回等待到的子进程的pid;
  • 如果设置了WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,返回0;
  • 如果调用中出错,则返回-1;

2.4代码实现

版本1: 实现阻塞等待任意子进程
#include <stdio.h>
#include <unistd.h>  //fork
#include <stdlib.h>  //exit
#include <sys/wait.h>  //wait
#include <sys/types.h> 

int main()
{

  pid_t id = fork();
  if(id < 0){
    perror("fork error\n");
    exit(1);
  }else if(id == 0){
    //child
    int time = 5;
    while(time > 0)
    {
      printf("child[%d] will exit %d\n",getpid(),time);
      time--;
      sleep(1);
    }
    exit(0);
  }


  //parent
  sleep(7);
  pid_t ret = waitpid(-1,NULL,0); //这时waitpid和wait作用相同
  if(ret < 0){
    perror("wait error\n");
    exit(2);
  }
  //wait success
  printf("father wait child[%d] success!\n",ret);
  sleep(10);
  return 0;
  
}

运行结果:除了进程pid外其余全部一样
在这里插入图片描述

版本2:阻塞等待特定子进程并且获取子进程退出状态
#include <stdio.h>
#include <unistd.h>  //fork
#include <stdlib.h>  //exit
#include <sys/wait.h>  //wait
#include <sys/types.h> 

int main()
{

  pid_t id = fork();
  if(id < 0){
    perror("fork error\n");
    exit(1);
  }else if(id == 0){
    //child
    int time = 5;
    while(time > 0)
    {
      printf("child[%d] will exit %d\n",getpid(),time);
      time--;
      sleep(1);
    }
    exit(0);
  }


  //parent
  sleep(7);
  int status = 0;
  pid_t ret = waitpid(id,&status,0);
  if(ret < 0){
    perror("wait error\n");
    exit(2);
  }else if(ret > 0){
    //等待到子进程
    if((status & 0x7f) ==0 ){
      //正常退出
      printf("father wait child[%d] success!\n",ret);
      //获取退出码
      printf("child[%d] exit code: %d\n",ret,(status>>8 & 0xff));
    }else{
      //异常退出
      printf("进程异常退出,获得一个信号\n");
    }
  }
  sleep(10);
  return 0;
  
}

运行结果:子进程中exit设置的为0所以退出码为0
在这里插入图片描述

脚本显示:
在这里插入图片描述

注意:运行结果显示可能每次都差不多,大家可以忽略结果图,理解代码变化即可。
另外,因为代码中每次父进程执行完所有代码后会sleep10秒 脚本没有截全,为了避免大家误解在此解释一下。 子进程退出后状态变化如下所示:
在这里插入图片描述

最终版本 非阻塞等待子进程并且获取子进程退出码

#include <stdio.h>
#include <unistd.h>  //fork
#include <stdlib.h>  //exit
#include <sys/wait.h>  //wait
#include <sys/types.h> 

int main()
{

  pid_t id = fork();
  if(id < 0){
    perror("fork error\n");
    exit(1);
  }else if(id == 0){
    //child
    int time = 5;
    while(time > 0)
    {
      printf("child[%d] will exit %d\n",getpid(),time);
      time--;
      sleep(1);
    }
    exit(6);
  }


  //parent
  int status = 0;
  while(1){
    pid_t ret = waitpid(id,&status,WNOHANG);
    if(ret == 0){
      //设置了WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,返回0
      printf("father can do other thing...\n");
    }else if(ret > 0 && WIFEXITED(status)){
      //WIFEXITED  若为正常终止子进程返回的状态,则为真
      printf("fahter wait child[%d] success, status exit code: %d\n", ret,WEXITSTATUS(status));
      break; 
    }else if(ret > 0){
      printf("进程异常退出,退出信号为:%d\n",(status&0x7f));
    }
    else if(ret < 0){
      perror("wait error\n");
      exit(2);
   }
    sleep(2);
  }
  sleep(3);
  return 0;
}

运行结果:
在这里插入图片描述
在这里插入图片描述

小插曲:我们一直写的代码是正常退出的,现在我们实现个异常退出的情况

手动利用kill结束掉子进程 ,此时走(ret > 0)的循环 打印一条消息获取退出信号,但是没有break,所以会再进入循环,但此时子进程已经被kill掉 ,所以父进程等待失败 (ret < 0) 打印 wait error 然后break,父进程结束。 在这里插入图片描述

关于进程等待常见的情况就介绍完了。

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值