Linux 进程等待

1. 进程等待的必要性

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

2. 进程等待的方法

2.1 wait方法

wait里面的参数在waitpid里面细说,我们更多的使用的都是waitpid,wai等待的是任意一个子进程。

cpp 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include <sys/types.h>
  5 #include <sys/wait.h>
  6 int main()
  7 {
  8         for(int i=0;i<3;i++)
  9         {
 10                 pid_t id=fork();
 11                 if(id<0)
 12                 {
 13                          perror("fork");
 14                          return 1;
 15                 }
 16                  else if(id==0)
 17                  {
 18                          int cnt=5;
 19                          while(cnt)
 20                          {
 21                                  printf("I am child,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);
 22                                  cnt--;
 23                                  sleep(1);
 24                                  exit(0);//结束后退出,不执行其他函数
 25                         }
 26                  }
 27         }
 28         int time=10;
 29         while(time)
 30         {
 31                 printf("I am father,pid:%d,ppid:%d\n",getpid(),getppid());
 32                 sleep(1);
 33                 time--;
 34         }
 35         for(int i=0;i<3;i++)
 36         {
 37                 pid_t rid=wait(NULL);//父进程等待子进程结束,回收
 38                 printf("子进程被回收,pid为:%d\n",rid);
 39         }
 40         return 0;
 41 }
cpp 复制代码
while :; do ps ajx|head -1&&ps ajx|grep wait|grep -v grep ; sleep 1;done 

输入上面的代码,我们来监控父进程和子进程的状态。

我们发现3个创建的子进程的僵尸状态消失了,并且退出。

ps:wait等待的任意的子进程。

2.2 waitpid

如果我们设置waitpid(-1,NULL,0)这样的参数的话就相当于和wait是一个效果的,都是对任意一个进程的等待。

2.2.1 status参数

wait和waitpid,都有一个status参数,该参数是一个输出型参数,我们设置int类型的变量,操作系统会把返回状态填充到变量里面。

如果传递NULL,表示不关心子进程的退出状态信息。

否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。

status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位)

cpp 复制代码
 1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include <sys/types.h>
  5 #include <sys/wait.h>
  6 int main()
  7 {
  8         int num[3];
  9         for(int i=0;i<3;i++)
 10         {
 11                 pid_t id=fork();
 12                 if(id<0)
 13                 {
 14                          perror("fork");
 15                          return 1;
 16                 }
 17                  else if(id==0)
 18                  {
 19                          int cnt=5;
 20                          while(cnt)
 21                          {
 22                                  printf("I am child,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);
 23                                  cnt--;
 24                                  sleep(10);
 25                                  exit(0);//结束后退出,不执行其他函数
 26                         }
 27                  }
 28                 else//父进程
 29                 {
 30                         num[i]=id;//存储子进程的pid
 31                 }
 32         }
 33         int time=10;
 34         while(time)
 35         {
 36                 printf("I am father,pid:%d,ppid:%d\n",getpid(),getppid());
 37                 sleep(1);
 38                 time--;
 39         }
 40 
 41         for(int i=0;i<3;i++)
 42         {
 43                 int statu=0;
 44         //      pid_t rid=wait(NULL);//父进程等待子进程结束,回收
 45                 pid_t rid=waitpid(num[i],&statu,0);
 46                 printf("%d,exit sig:%d,exitcode:%d\n",num[i],statu&0x7F,(statu>>8)&0xFF);
 47         }
 48         return 0;
 49 }

我们设置一个数组来保存子进程的pid,方便后面waitpid回收固定的子进程。

我们可以使用对位图的&操作来获取到子进程的退出状态,每个进程中都有保存自己的退出码和导致进程终止的退出信号,父进程也就是从子进程的PCB上面去获取。

同时我们还可以通过系统给的库函数来获取。

**WIFEXITED(status):**查看进程是否正常退出,为真表示正常退出,返回1,没有收到异常信号。

WEXITSTATUS(status): 如果WIFEXITED(status)非零,提取子进程退出码。

cpp 复制代码
 41         for(int i=0;i<3;i++)
 42         {
 43                 int statu=0;
 44         //      pid_t rid=wait(NULL);//父进程等待子进程结束,回收
 45                 pid_t rid=waitpid(num[i],&statu,0);
 46         //      printf("%d,exit sig:%d,exitcode:%d\n",num[i],statu&0x7F,(statu>>8)&0xFF);
 47                 if(WIFEXITED(statu))
 48                 {
 49                         printf("child wait success,child exit code:%d\n",WEXITSTATUS(statu));
 50                 }
 51                 else
 52                 {
 53                         printf("child wait fail");
 54                 }
 55         
 56         }

测试一下:

如果我们在运行过程中,发送9号信号来杀死一个子进程。

可以明显发现一个子进程等待失败了。

2.3 非阻塞等待

上面我们把waitpid的第三个参数都设为了0,也就是让父进程结果后,阻塞在那边去等待子进程结束,然后把它回收。

等待的时候也可以让父进程做一些自己的事情,设置为非阻塞轮询,也就是父进程在调用waitpid等待子进程时,发现子进程还没有结束,就可以让父进程去做别的事情,下次再过来询问子进程好了吗?没有就再去做自己的事情,当有一次子进程结束了和子进程一起退出。

改造一下代码:

cpp 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include <sys/types.h>
  5 #include <sys/wait.h>
  6 int main()
  7 {
  8         int num[3];
  9         for(int i=0;i<3;i++)
 10         {
 11                 pid_t id=fork();
 12                 if(id<0)
 13                 {
 14                          perror("fork");
 15                          return 1;
 16                 }
 17                  else if(id==0)
 18                  {
 19                          int cnt=5;
 20                          while(cnt)
 21                          {
 22                                  printf("I am child,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);
 23                                  cnt--;
 24                                  sleep(10);
 25                                  exit(0);//结束后退出,不执行其他函数
 26                         }
 27                  }
 28                 else//父进程
 29                 {
 30                         num[i]=id;//存储子进程的pid
 31                 }
 32         }
 33         int time=2;
 34         while(time)
 35         {
 36                 printf("I am father,pid:%d,ppid:%d\n",getpid(),getppid());
 37                 sleep(1);
 38                 time--;
 39         }
 40         
 41         for(int i=0;i<6;i++)//前3次循环中子进程没有结束,父进程做自己的事情,后三次循环,父进程回收子进程
 42         {       
 43                 int statu=0;
 44         //      pid_t rid=wait(NULL);//父进程等待子进程结束,回收
 45                 pid_t rid=waitpid(num[i],&statu,WNOHANG);//设置为非阻塞状态
 46                 if(rid<0)
 47                 {
 48                         printf("wait fail");
 49                         break;
 50                 }
 51                 else if(rid==0)//没有结束的子进程,可以让父进程做别的事情
 52                 {
 53                         printf("子进程还没有结束");
 54                         printf("我去做一些别的事情了\n");
 55                         sleep(3);//模拟父进程做别的事情
 56                 }
 57                 else
 58                 {
 59  //      printf("%d,exit sig:%d,exitcode:%d\n",num[i],statu&0x7F,(statu>>8)&0xFF);
 60                         if(WIFEXITED(statu))
 61                         {
 62                         printf("child wait success,child exit code:%d\n",WEXITSTATUS(statu));
 63                         }
 64                         else
 65                         {
 66                                 printf("child quit fail");
 67                         }
 68                 }
 69 
 70         }
 71         return 0;
 72 }

                                                                                                                  

我们让子进程创建后10秒后再退出,把等待模型设置为非阻塞状态,把for循环设置为6次,前3次由于子进程没有结束,父进程做自己的事情,后三次父进程回收子进程。

运行结果:

相关推荐
稚辉君.MCA_P8_Java1 小时前
DeepSeek Java 多线程打印的19种实现方法
java·linux·jvm·后端·架构
Han.miracle1 小时前
JavaEE-- 网络编程 http请求报头
运维·服务器·网络·网络协议·计算机网络·http
鹿鸣天涯2 小时前
使用VMware Workstation 17虚拟机安装红帽企业版系统RHEL10
linux·运维·服务器
SKYDROID云卓小助手2 小时前
三轴云台之控制协同技术
服务器·网络·图像处理·人工智能·算法
艾莉丝努力练剑2 小时前
【Git:企业级开发模型】Git企业级Git工作流实战:基于Git Flow的分支模型与开发流程
服务器·git·ubuntu·gitee·centos·powershell·企业级开发模型
杰 .2 小时前
Linux yum_and_apt
linux·服务器
南棱笑笑生3 小时前
20251129给荣品RD-RK3588开发板跑Rockchip的原厂Buildroot【linux-6.1】系统时适配AP6275P的蓝牙BLE
linux·运维·服务器·rockchip
Brown.alexis3 小时前
docker安装redis7
运维·docker·容器