【Linux系统编程】第二十三弹---理解进程等待:为何、何时以及如何等待

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】

目录

1、进程等待

1.1、进程等待必要性

1.2、进程等待的方法

1.3、获取子进程status

3.4、阻塞等待 

3.5、非阻塞等待


1、进程等待


1.1、进程等待必要性

  • 之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
  • 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
  • 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

1.2、进程等待的方法


wait方法 

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
    成功返回被等待进程pid,失败返回-1。
参数:
    输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

 代码演示

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

void RunChild()
{
  int cnt = 5;
  while(cnt)
  {
    printf("I am child process,pid:%d,ppid:%d\n",getpid(),getppid());
    sleep(1);
    cnt--;
  }
}
int main()
{
  printf("I am a father process,pid:%d,ppid:%d\n",getpid(),getppid());
  pid_t id = fork();
  if(id == 0)
  {
    RunChild();
    printf("child quit...\n");
    exit(123);
  }
  sleep(6);
  // father
  pid_t rid = wait(NULL);
  sleep(2);
  printf("father quit...\n");
  return 0;
}

 运行结果

注意:wait等待的是任意一个进程。 

 waitpid方法

pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
    当正常返回的时候waitpid返回收集到的子进程的进程ID;
    如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
    如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
    pid:
        Pid=-1,等待任一个子进程。与wait等效。
        Pid>0.等待其进程ID与pid相等的子进程。
    status:
        WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
        WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
    options:
        WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。

代码演示一

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

void RunChild()
{
  int cnt = 5;
  while(cnt)
  {
    printf("I am child process,pid:%d,ppid:%d\n",getpid(),getppid());
    sleep(1);
    cnt--;
  }
}
int main()
{
  printf("I am a father process,pid:%d,ppid:%d\n",getpid(),getppid());
  pid_t id = fork();
  if(id == 0)
  {
    RunChild();
    printf("child quit...\n");
    exit(123);
  }
  sleep(6);
  // father
  pid_t rid = waitpid(-1,NULL,0);
  sleep(2);
  printf("father quit...\n");
  return 0;
}

运行结果

当waitpid函数以 waitpid(-1,NULL,0) 进行传参,效果跟wait是一样的,都是对任意一个进程等待。

代码演示二

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

void RunChild()
{
  int cnt = 5;
  int* p = NULL;
  while(cnt)
  {
    printf("I am child process,pid:%d,ppid:%d\n",getpid(),getppid());
    sleep(1);
    cnt--;
    *p = 100;
  }
}
int main()
{
  printf("I am a father process,pid:%d,ppid:%d\n",getpid(),getppid());
  pid_t id = fork();
  if(id == 0)
  {
    RunChild();
    printf("child quit...\n");
    exit(123);
  }
  sleep(6);
  // father
  // 等待指定id(子进程)
  pid_t rid = waitpid(id,NULL,0);
  // rid > 0则等待成功,否则等待失败
  if(rid > 0)
  {
    printf("wait sucess,pid:%d\n",rid);
  }
  else
  {
    printf("wait failed!\n");
  }
  sleep(2);
  printf("father quit...\n");
  return 0;
}

运行结果 

将上面的等待代码函数 id 参数改为id + 1如下:

pid_t rid = waitpid(id + 1,NULL,0);

运行结果 

  • 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
  • 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
  • 如果不存在该子进程,则立即出错返回。

1.3、获取子进程status

  • wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息。
  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  • status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位): 

退出码和退出信号获取如下图:

代码演示(正常退出)

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

void RunChild()
{
  int cnt = 5;
  while(cnt)
  {
    printf("I am child process,pid:%d,ppid:%d\n",getpid(),getppid());
    sleep(1);
    cnt--;
  }
}
int main()
{
  printf("I am a father process,pid:%d,ppid:%d\n",getpid(),getppid());
  pid_t id = fork();
  if(id == 0)
  {
    RunChild();
    printf("child quit...\n");
    exit(123);
  }
  sleep(6);
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("wait sucess,pid:%d\n",rid);
  }
  else
  {
    printf("wait failed!\n");
  }
  sleep(2);
  printf("father quit...,child quit code :%d,child signal code:%d\n",(status>>8)&0xFF,status & 0x7F);
  return 0;
}

运行结果 

代码演示(异常退出)

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

void RunChild()
{
  int cnt = 5;
  int* p = NULL;
  while(cnt)
  {
    printf("I am child process,pid:%d,ppid:%d\n",getpid(),getppid());
    sleep(1);
    cnt--;
    *p = 100;
  }
}
int main()
{
  printf("I am a father process,pid:%d,ppid:%d\n",getpid(),getppid());
  pid_t id = fork();
  if(id == 0)
  {
    RunChild();
    printf("child quit...\n");
    exit(123);
  }
  sleep(6);
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("wait sucess,pid:%d\n",rid);
  }
  else
  {
    printf("wait failed!\n");
  }
  sleep(2);
  printf("father quit...,child quit code :%d,child signal code:%d\n",(status>>8)&0xFF,status & 0x7F);
  return 0;
}

运行结果 

3.4、阻塞等待 

前面我们写的都是阻塞等待的代码,但是都是我们自己手动判断结果,此处还有两个宏,下面使用宏来演示阻塞等待的代码。

WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

代码演示

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

void RunChild()
{
  int cnt = 5;
  while(cnt)
  {
    printf("I am child process,pid:%d,ppid:%d\n",getpid(),getppid());
    sleep(1);
    cnt--;
  }
}

int main()
{
  printf("I am father process,pid:%d,ppid:%d\n",getpid(),getppid());
  pid_t id = fork();
  if(id == 0)
  {
    // child 
    RunChild();  
    printf("child quit...\n");
    exit(123);
  }
  sleep(6);
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    if(WIFEXITED(status))
      printf("child quit success,child exit code:%d\n",WEXITSTATUS(status));
    else 
      printf("child quit unnormal!\n");
    printf("wait success\n");
  }
  else 
  {
    printf("wait failed!\n");
  }
  sleep(3);
  printf("father quit, status: %d, child quit code : %d, child quit signal: %d\n", status, (status>>8)&0xFF, status & 0x7F);
  return 0;
}

运行结果 

3.5、非阻塞等待

如果子进程没有退出,而父进程在执行waitpid进行等待(等待某种发生,此处是等待子进程退出),此时为阻塞等待,即父进程没有做任何事,那么父进程能否在等待的过程中做其他的事呢?答案是可以的,此时需要在选项的参数传WNOHANG ,称为非阻塞等待。

options:
        WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。

task.h

#pragma once 
#include <stdio.h>

void PrintLog();
void DownLoad();
void MysqlDataSync();

task.c

#include "task.h"

void PrintLog()
{
  printf("Begin PrintLog...\n");
}

void DownLoad()
{
  printf("Begin DownLoad...\n");
}

void MysqlDataSync()
{
  printf("Begin MysqlDataSync...\n");
}

myprocess.c

#include "task.h"

typedef void(*func_t)();// 重命名函数指针

#define N 3
func_t tasks[N] = {NULL};

void LoadTask()
{
  tasks[0] = PrintLog;
  tasks[1] = DownLoad;
  tasks[2] = MysqlDataSync;
}

void HandlerTask()
{
  for(int i=0;i<N;i++)
  {
    tasks[i]();
  }
}

void DoOtherThing()
{
  HandlerTask();
}

void RunChild()
{
  int cnt = 5;
  //int* p = NULL;
  while(cnt)
  {
    printf("I am child process,pid:%d,ppid:%d\n",getpid(),getppid());
    sleep(1);
    cnt--;
    //*p = 100;
  }
}

int main()
{
  printf("I am father process,pid:%d,ppid:%d\n",getpid(),getppid());
  pid_t id = fork();
  if(id == 0)
  {
    // child 
    RunChild();  
    printf("child quit...\n");
    exit(123);
  }
  
  // father
  LoadTask();
  while(1)
  {
    int status = 0;
    pid_t rid = waitpid(id,&status,WNOHANG); // non block
    // 子进程还没退出
    if(rid == 0)
    {
      usleep(100000);
      printf("child is running,father check out next\n");
      DoOtherThing();
    }
    // 子进程退出且等待成功
    else if(rid > 0)
    {
      if(WIFEXITED(status))
      {
         printf("child quit success, child exit code : %d\n", WEXITSTATUS(status));
      }
      else 
      {
         printf("child quit unnormal!\n");
      }
      break;
    }
    // 等待失败
    else 
    {
      printf("wait failed!\n");
      break;
    }
  }
  return 0;
}

运行结果

waitpid() 函数返回值

  • pid_t > 0 : 等待成功,子进程退出,并且父进程回收成功。
  • pid_t = 0 : 检测成功,但是子进程没有退出,需要下一次进行重复等待。
  • pid_t < 0 : 等待失败。
评论 118
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小林熬夜学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值