【Linux】进程等待


文章目录

  • tips
  • 一、进程等待是什么?
  • 二、为什么要有进程等待?
  • 三、怎么做到进程等待?
    • 先看看什么是进程等待
    • wait和waitpid
      • status参数
      • options参数
        • 非阻塞轮询
    • 进程等待的原理
  • 总结



tips

下面的代码可以循环检测进程。

while :; do ps ajx | head -1; ps ajx | grep testwait | grep -v grep; sleep 1; echo "-------------------------------------"; done

一、进程等待是什么?

通过系统调用wait/waitpid,来对子进程进行状态检测和回收。

二、为什么要有进程等待?

  • 1.僵尸进程无法被杀死,需要通过进程等待来杀掉它,从而解决内存泄露的问题。 ——这是必须解决的
  • 2.需要通过进程等待,来获得子进程的退出情况。也就是我要知道布置给它的任务做的怎么样了。——这是可选做的

进程等待的重点就是为了解决上述两个问题。

三、怎么做到进程等待?

先看看什么是进程等待

  7 int main()8 {9 10      pid_t id = fork();11      if(id < 0)12      {13          perror("fork");14          return 0;15      }16      else if(id == 0)17      {18          //child19          int cnt = 5;20          while(cnt)21          {22              printf("I am child,pid : %d , ppid : %d\n",getpid(),getppid());23              sleep(1);24              cnt--;25          }26 27          exit(0);28      }29      else30      {                                                                                                                                                                                     31          //parent32          int cnt = 10;33          while(cnt)34          {35              printf("I am parent,pid : %d , ppid : %d\n",getpid(),getppid());36              sleep(1);37              cnt--;38          }39 40          pid_t ret = wait(NULL);41          if(ret == id)42          {43              printf("wait success,ret = %d\n",ret);44          }45 46          sleep(5);47      }48 }

上面代码的意思是:
先创建一个子进程,父子进程同时跑5s,前5s都处在阻塞状态,即S状态。
中间5秒子进程退出,等待父进程来回收,此时子进程变成僵尸状态,即Z状态。
后5秒父进程通过wait等待子进程退出后,回收子进程,此时只有父进程还在运行,
随后父进程也退出。

在这里插入图片描述

下面是循环创建10个子进程:

  7 void Runchild()8 {9     int cnt = 5;10     while(cnt)11     {12         printf("i am child, pid : %d,ppid : %d\n",getpid(),getppid());13         sleep(1);14         cnt--;15     }16 }17 18 int main()19 {20     int i = 0;21     //创建10个子进程22     for(;i<10;i++)23     {24         pid_t id = fork();25         if(id == 0)26         {27             Runchild();28             exit(0);29         }30     }31 32     sleep(10);33 34     //目前来说,进程等待是必须的35     for(i=0;i<10;i++)                                                                                                                                                                      36     {37         pid_t ret = wait(NULL);38         printf("wait success: %d\n",ret);39     }40 41     sleep(5);42     return 0;43 }

前5秒执行代码,创建10个子进程,父子进程同时运行。
中间5秒,10个子进程退出,变成僵尸状态,等待父进程回收
后5秒,父进程调用10次wait系统调用,对10个僵尸进程进行回收。
回收5秒后,父进程退出。

总结:目前来说,进程等待是必须的,这样才能杀掉僵尸状态,回收资源,防止内存泄露。


问题1:
如果父进程一直在等待子进程,而子进程一直不退出呢?

如果父进程等不到子进程退出,父进程就会一直等,wait不返回结果,父进程就会一直处于阻塞状态。

wait和waitpid

在这里插入图片描述

status参数

wait函数只需要传入一个参数:status,代表进程的退出状态,一般传入的时候是一个整数,整数是32个比特位,一般只考虑低16位。

在这里插入图片描述

其中,退出core dump后面会讲,进程的退出信号可以查询:

通过 kill -l命令查询到到有64个退出信号,由status变量的0~7个比特位表示。

在这里插入图片描述

进程的退出码由8~15个比特位表示。
在这里插入图片描述

其中0就表是进程正常运行,结果正确。


问题:能不能自己创建一个全局变量status,子进程退出时将status变量设置成不同的退出码,代表不同的退出状态返回给父进程呢?

答案是不可能的。**因为父子进程之间具有独立性。**当子进程要修改status时,会发生写时拷贝,父子进程的资源互相独立,子进程返回的status父进程是拿不到的,所以只能通过系统调用让操作系统帮父进程从子进程中拿资源。

通过父进程对子进程进行等待,将status参数传入waitpid中,获取子进程的退出信息和退出码。

if(id > 0)
{int status=0; // 低16位有效pid_t ret = waitpid(id,&status,0);if(ret == id){//低七位是退出信号,次低八位是退出码printf("wait success! id = %d , exit sig = %d, exit code = %d\n",ret,status&0x7F,(status>>8)&0xFF);}
}

核心代码如上:

status&0x7F,即可获取到子进程的退出信号
在这里插入图片描述
通过status的次低8位,获取到子进程的退出码。
(status>>8)&0xFF

其实,库里面还提供了两个宏,来代替上面的stauts&0x7F, 和(status>>8)&0xFF

  • 1.WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出) ——相当于把status&0x7F再加上一个逻辑取反。

  • 2.WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)——相当于(status>>8)&0xFF

 if(ret == id){//低七位是退出信号,次低八位是退出码//printf("wait success! id = %d , exit sig = %d, exit code = %d\n",ret,status&0x7F,(status>>8)&0xFF);if(WIFEXITED(status)){printf("父进程等待它的子进程成功,退出码为:%d\n",WEXITSTATUS(status));}}else{printf("父进程等待它的子进程失败!\n");}

核心代码如上,进入if说明父进程等待的是它的子进程成功。
进入else,说明父进程等待的不是它自己的子进程。

所以,父进程只有等待它自己创建的子进程时才会成功。


总结:

  • 1.只要通过wait/waitpid函数的status参数低7位判断是否为0,就能知道进程是否跑完。
  • 2.只要通过8~15位判断进程的退出码是多少,就知道进程运行结果如何。

这就对应了进程的退出情况无非只有三种:

  • 1.进程正常退出,结果正确
  • 2.进程正常退出,结果不正确
  • 3.进程异常退出

从此,就能通过waitpid,等待对应的进程,进而获取到对应进程的退出情况。

options参数

options参数的意思就是,让父进程选择什么样的方式等待子进程退出!

下面提供了两种等待方式,阻塞等待和非阻塞等待!

非阻塞轮询

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

WNOHANG是一个宏,就表示非阻塞等待。

先讲讲什么是轮询:

将近期末考试,小吴是一个努力型学霸,小邓是一个摆烂型学渣,老师说:明天要考c语言了!
小邓一听,顿时慌了,下课后马上打电话给小吴,对小吴说:“小吴,你的笔记和你这个人可以借我用一下吗?”
小吴一听就知道了,这是小邓想要我教他呢!
小吴说:“好,你等等,我在复习,等会就下去找你。”
小邓听完就挂电话了,过了10秒中,小邓又打电话给小吴:“你好了没。”
小吴说:“还没。” 啪的一声,小邓挂断了电话。又过了十秒,又打电话给小吴:“你好了没。” “还没。” 就这样,打了20几个电话,小吴说:“好了,我现在下去找你”。

这个每隔一段时间小邓就打电话给小吴询问情况的过程就叫做轮询!!!

而什么叫做非阻塞轮询呢?

在小邓不断打电话给小吴询问情况的过程中,小邓学聪明了,傻等这也不是办法,所以小邓拿着一本c语言的教材在看,边看边等。意味着小邓在等待的过程中,能够做自己的事情!!!

这就意味着非阻塞!!!

所以,两者结合起来,就是非阻塞轮询!!!

小吴就相当于操作系统,小邓就相当于父进程,父进程在不断向操作系统询问等待返回结果的过程,就是轮询的过程。
而在轮询时父进程可以做自己的事情,就叫做非阻塞轮询!!!

总结:WNOHANG等待方式就是非阻塞等待方式,如果等待的子进程没有结束,就返回0,如果等待成功,返回子进程的id。

int main()
{pid_t id = fork();if(id < 0){perror("fork");exit(0);}else if(id == 0){//childint cnt = 10;// int a = 10;// a/=0;while(cnt--){printf("i am a child, pid %d, ppid %d \n",getpid(),getppid());sleep(1);}exit(11);}else {//parentint cnt = 5;while(cnt--){printf("i am a parent, pid %d, ppid %d \n",getpid(),getppid());sleep(1);}while(1)  //轮询{int status=0; // 低16位有效pid_t ret = waitpid(id,&status,WNOHANG); //非阻塞等待//pid_t ret = waitpid(id,&status,0); //阻塞等待//阻塞等待,父进程什么都做不了,只能在这里死等着子进程if(ret < 0) //等待失败{   printf("等待子进程失败!\n");break;}//等待成功else if(ret > 0){//低七位是退出信号,次低八位是退出码//printf("wait success! id = %d , exit sig = %d, exit code = %d\n",ret,status&0x7F,(status>>8)&0xFF);if(WIFEXITED(status)){printf("父进程等待它的子进程成功,退出码为:%d\n",WEXITSTATUS(status));}break;}else{printf("子进程还未退出,再等等....\n");//等的过程父进程可以做自己的事情int a = 10;int b = 20;int c = a+b;printf("父进程在进行相加运算,结果为:%d\n",c);}//每隔1秒等待1次sleep(1);}}return 0;
}
pid_t ret = waitpid(id,&status,WNOHANG); 

这段代码,就是父进程在等待子进程时采用的是非阻塞等待(WNOHANG)
不断执行while循环就是轮询。

pid_t ret = waitpid(id,&status,0); 

这段代码,就是阻塞等待,如果子进程未退出,父进程就会在这里傻傻地等待子进程退出。自己什么事情都做不了。

进程等待的原理

父进程通过调用操作系统提供的系统调用waitpid,然后由操作系统接收到父进程的申请,去查询父进程对应的子进程的状态。
如果子进程处于s状态,则操作系统会将子进程的状态结果返回给父进程。
如果子进程处于z状态,则操作系统会将子进程的退出码exit_code,退出信号exit_signal拿出来返回给父进程。父进程收到信息后就知道该如何做下一步动作。

在这里插入图片描述


问题:为什么父进程不直接去子进程中获取子进程的运行状况呢?

因为操作系统不相信任何人!!!

这就比如说:A学校的有一个网络空间安全方面的学生特别厉害,B学校的校长想邀请A学校的那个学生帮助B学校参加一项网络空间安全的比赛,B校长直接找到A学校的那名学生叫他去参加比赛。
这是不能这样做的,因为B校长想要找A学校的学生,还得问过A校长才行!!!

操作系统对父进程也是如此,父进程不能直接获取子进程的数据,必须通过操作系统这一媒介!!!

因为要是父进程修改了子进程的数据了呢???

所以,操作系统不相信任何人!!!

总结

这篇文章详细介绍了进程等待的各种细节。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/191552.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git推送本地代码到远程仓库

Git推送本地代码到远程仓库 1、首先需要安装Git&#xff0c;如果已经安装&#xff0c;请跳过。下载地址&#xff1a;https://git-for-windows.github.io/ 2、安装好git服务器后。首先找到你项目的文件夹&#xff0c;比如项目名称为Item&#xff0c;进入到这个文件夹&#xff0…

v-bind和v-model

目录 前言 v-bind 作用 语法格式 编译原理 简写 v-model 作用 使用方法 v-bind和v-model的区别和联系 前言 本文我们来了解一下模板语法之指令语法中的v-bind和v-model v-bind 作用 v-bind可以让html标签的某个属性的值产生动态的效果 语法格式 <html标签 v-bin…

企业级真实应用利用Mybatis-Plus进行分页查询处理

怎么导入依赖我在之前的文章里边有说过不理解的可以看看 你应该懂点Mybatis-plus&#xff0c;真的好用 1&#xff1a;了解Page<T>类的使用 首先我们需要使用到Page类 &#xff0c;建立一个Page类&#xff0c;泛式类型中放入我们需要输出的类&#xff0c;是列表的话就…

2023年亚太杯数学建模思路 - 案例:FPTree-频繁模式树算法

文章目录 赛题思路算法介绍FP树表示法构建FP树实现代码 建模资料 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模式树算法&#…

任意注册漏洞

目录 一漏洞介绍 二实战演示 三漏洞修复 本文由掌控安全学院 - 小博 投稿 一漏洞介绍 1.未验证邮箱/手机号 情景&#xff1a;应用为了方便用户记录用户名&#xff0c;使用邮箱和手机号作为用户名&#xff08;因此很多应用在注册的时候就要求用户填写&#xff0c;多数时候…

安卓:打包apk时出现Execution failed for task ‘:app:lintVitalRelease

Execution failed for task :lintVitalRelease 程序可以正常运行&#xff0c;但是打包apk的时候报Execution failed for task ‘:app:lintVitalRelease导致打包失败&#xff0c;原因是执行lintVitalRelease失败了&#xff0c;存在错误。解决办法&#xff1a;在app模块的build.…

万宾科技内涝积水监测仪效果,预警城市积水

当城市之中出现强降雨或者大暴雨&#xff0c;可能会导致雨水不断堆积到城市排水管网之中&#xff0c;可能还会淹没城市的排水系统时&#xff0c;这种现象被称为城市之中的内涝&#xff0c;并且在许多城市之中内涝问题日益引起人们的关注。 内涝积水监测仪的出现成为了希望的灯塔…

Android Studio的代码笔记--JSON解析学习2

JSON学习2 生成JSON解析JSON java解析json字符串和合成json字符串 json字符串 {"type":"getConfig","ip":"192.168.1.100"}使用 String ss groupJS("Config","192.168.1.100"); splitJS(ss);回显 I/lxh: group…

arcgis--NoData数据处理

方法一&#xff1a;利用【栅格计算器】可以对NoData的值进行修改。【Spatial Analyst工具】-【地图代数】-【栅格计算器】&#xff0c;将NoData修改为某一个值。 方法二&#xff1a;先对原始数据进行重分类&#xff0c;分成1类&#xff0c;将NoData赋值为2,。然后&#xff0c;将…

2023.11.11通过html内置“required-star“添加一个红色的星号来表示必填项

2023.11.11通过html内置"required-star"添加一个红色的星号来表示必填项 在HTML中&#xff0c;可以使用标签来为元素添加说明。同时可以通过添加一个红色的星号来表示必填项。 <!DOCTYPE html> <html lang"en"> <head><meta charse…

GoLong的学习之路,进阶,语法之并发(并发错误处理)补充并发三部曲

这篇文章主要讲的是如何去处理并发的错误。 在Go语言中十分便捷地开启goroutine去并发地执行任务&#xff0c;但是如何有效的处理并发过程中的错误则是一个很棘手的问题。 文章目录 recovererrgroup recover 哦对&#xff0c;似乎没写错误处理的文章。后面补上。 首先&…

一文懂得电源模块过温保护测试方法 ate测试软件助力测试

过温保护测试是电源模块保护功能测试项目之一&#xff0c;也是电源模块测试的重要测试指标&#xff0c;以保证电源模块过温保护功能正常&#xff0c;确保电源模块不受损坏。用ate测试软件测试电源模块过温保护&#xff0c;不仅可以保证测试结果的准确性&#xff0c;还可以多维度…

[Linux] dns域名解析服务

一、DNS 1.1 DNS简介 域名解析&#xff1a;&#xff08;英文&#xff1a;Domain Name System&#xff0c;缩写&#xff1a;DNS&#xff09;是互联网的一项服务。它作为将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便地访问互联网。DNS使用udp53和tcp53…

SQLite3 数据库学习(一):数据库和 SQLite 基础

参考引用 SQL 必知必会SQLite 权威指南&#xff08;第二版&#xff09;关系型数据库概述 1. 数据库基础 1.1 什么是数据库 数据库&#xff08;database&#xff09;&#xff1a;保存有组织的数据的容器&#xff08;通常是一个文件或一组文件&#xff09; 可以将其想象为一个文…

C语言仅凭自学能到什么高度?

今日话题&#xff0c;C语言仅凭自学能到什么高度&#xff1f;学习C语言的决定我确实非常推荐&#xff0c;毕竟它是编程领域的“通用工具”&#xff0c;初学者可以尝试并在发现编程的乐趣后制定长期学习计划。至于能够达到何种高度&#xff0c;这实在无法准确回答。即使是经验丰…

NtripShare Mos地铁自动化监测终端盒子硬件设计

自动化监测产品到目前为止做了接近一年&#xff0c;在软件层面上&#xff0c;控制终端软件、平台软件、网平差算法都已解决&#xff0c;硬件盒子始终是心里过不去的坎&#xff0c;最终还是没有耐住性子自己做了一把。 选型如下&#xff1a; 1、主板:瑞芯微RK3568主板。 2、外…

主流接口测试框架对比,究竟哪个更好用

公司计划系统的开展接口自动化测试&#xff0c;需要我这边调研一下主流的接口测试框架给后端测试&#xff08;主要测试接口&#xff09;的同事介绍一下每个框架的特定和使用方式。后端同事根据他们接口的特点提出一下需求&#xff0c;看哪个框架更适合我们。 需求 1、接口编写…

Windows系统下使用docker部署redis

使用虚拟机部署redis&#xff0c;虚拟机很占用电脑资源&#xff0c;所以选择使用docker对redis进行部署。 一、安装docker 安装链接&#xff1a;https://docker.p2hp.com/ 二、配置redis.conf文件 下载配置文件&#xff1a;https://download.redis.io/redis-stable/redis.con…

rabbitMq创建交换机,以及路由键绑定队列教程

创建交换机&#xff1a; 创建队列&#xff1a; 创建路由&#xff0c;绑定到交换机&#xff1a; 补充&#xff1a; 创建新用户后&#xff0c;记得点进用户中&#xff0c;那两个set都点击一下&#xff1b; 还有配置代码连接的时候&#xff0c;连的端口为5672&#xff0c;可不…

【JavaEE】Servlet(创建Maven、引入依赖、创建目录、编写及打包、部署和验证、smart Tomcat)

一、什么是Servlet&#xff1f; Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app 1.1 Servlet能干什么&#xff1f; &#x1f695;允许程序猿注册一个类, 在 Tomcat 收到某个特定的 HTTP 请求的时候, 执行这个类…