Linux进程控制(二)---进程等待

目录

什么是进程等待

为什么要进行进程等待?

wait()

waitpid()

status的使用★

options★

问题:既然进程具有独立性,进程退出码不也是子进程数据吗,父进程凭什么拿到呢?wait/waitpid究竟做了什么呢?

什么是进程等待

进程等待指父进程等待其子进程终止的一种机制。在多进程系统中,父进程创建子进程并且可能需要等待子进程执行完毕,以便获取子进程的退出状态或执行其他操作。

需要注意的是:父进程等待子进程终止是一个阻塞操作,即父进程会暂停自己的执行,直到子进程终止。

为什么要进行进程等待?

1.回收僵尸进程

2.获取子进程退出状态.

这是对上面两句话详细说明:

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

接下来我们要制造一种僵尸状态.

输入以下代码:

1 #include<stdio.h>2 #include<stdlib.h>3 #include<unistd.h>4 #include<sys/types.h>5 int main()6 {7   pid_t id = fork();8   if(id < 0)9   {10     perror("fork");11     exit(1);//标识进程运行完毕,结果不正确12   }13   else if(id == 0)14   {15     //子进程16     int cnt = 5;17     while(cnt)18     {19       printf("cnt: %d, 我是子进程,pid: %d,ppid: %d\n",cnt,getpid(),getppid());20       sleep(1);21       cnt--;22     }23     exit(0);24 25   }26   else27   {28     while(1)29     {30       printf("我是父进程,pid: %d,ppid: %d\n",getpid(),getppid());31       sleep(1);32     }33   }34 35   return 0;36 }     

这样子进程循环5次后退出,而父进程一直在循环,无法退出,造成了子进程无法被回收的情况.

然后退出vim,make编译并执行,同时再创建一个窗口,检测该进程的状态,在新窗口输入:

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

 

5秒后子进程退出,只有父进程在运行了. 

 此时观察进程状态也知,5秒后子进程的状态变成了Z+,僵尸状态.

所以此时我们如何回收僵尸进程呢,便用到了wait接口.

wait()

我们man 2 wait查看一下其用法.

它的作用是等待一个进程,直到其状态发生变化. 

这个参数status是用来获取子进程结果的,这个在后面waitpid会讲的,都是一样的.目前写NULL即可.

然后看一下返回值:

 陈工,返回子进程的pid,否则返回-1.

此时我们使用它,把父进程模块里的代码做如下改动:

先输出一次,然后再等待子进程,等待成功的话,会输出“等待子进程成功”.

退出vim,make编译.同时还是右边窗口监视.

这样父进程就一直会等子进程从运行直到死亡,状态发生变化时,将其回收.

 我们发现,此时子进程在5秒后没有产生僵尸状态,而是直接没有了,只剩下父进程在继续运行。

这个时候,子进程就是被父进程回收了,wait等到了状态变化.

那么waitpid和这个wait又有什么区别呢?

waitpid()

同样地我们man 2 waitpid查看用法.

第一个参数pid, 是用来等待特定pid的子进程。

有如下两个选择:

        1.Pid=-1,等待任一个子进程。与wait等效。
        2.Pid>0.等待其进程ID与pid相等的子进程。

第三个参数options 默认为0,表示阻塞等待.

第二个参数status,是一个指针类型,它是一个输出型参数.

比如我们在函数外定义一个int status = 0,

然后把这个status传入到waitpid中的第二个参数中。

然后 函数结束的时候,会把子进程的退出结果自动填充到这个 status里面去,这便是输出型参数.

所以来说,waitpid(-1,NULL,0)等价于wait(NULL).

我们把父进程中的wait改成waitpid:

 make编译.并运行

 结果是同样的.

status的使用★

那我们来使用一下status吧.

我们手动的在子进程模块添加一个退出码69,然后父进程模块中先定义好status变量,然后再将其传入到waitpid中,最后再将其输出.

退出来,make编译并运行.

 我们发现,退出码不是69,而是这么大一个数,这是什么情况呢?

其实status并不是按照整数来整体使用的,而是按照比特位的方式,将32个比特位进行划分.

我们只学习低16位.这个低16位就基本上满足我们需求了.

其中,这次低8位标识子进程的退出码.相当于第8-15位.我们想拿到它,首先得先将其右移8位,然后&0xFF,0xFF是只有后8位是1,其余位都是0,而任何&1是它本身,&0都是0,这样就拿到了这8位.

 

 此时我们便拿到了我们想要的结果69了.

每次这么写感觉很麻烦,那么有没有更快一点的方法呢?

C标准库提供了一些宏:

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

WEXITSTATUS(status):若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

我们具体看一下如何用:

 运行:

 可以看到此时子进程正常结束,并获得了我们返回的退出码

如果此时还是在子进程加上一个除0错误,然后再次输出.

可以看到子进程异常退出,而 WIFEXITED这个返回了一个值0.

相对来说也是比较简单的.

终止信号

再来说进程异常退出或者崩溃,本质是操作系统杀掉了进程.

那OS是如何杀掉的呢?本质是通过发送信号的方式杀掉进程.

这是Linux下的所有信号.

 所以那个最低7个比特位,即终止信号那里,表示的是进程收到的信号.

其中code dump在后面讲解信号的时候会说明。

我们同样的输出一下最后7个比特位的值.

这个时候需要获取最后7个比特位,那就要&上0111 1111,这个十六进制表示0x7 F.

 运行:

 子进程最后收到的信号是0,说明是正常跑完的.

退出码是在正常跑完的基础上,用来判断结果正确还是不正确的.

那我们此时写一个除0错误,让子进程异常结束.

运行:

 

 我们发现子进程收到的信号是8,信号8是SIGFPE,代表浮点数计算错误。

此时由于程序都没有正常跑完,退出码便没有了意义。因为程序都没有跑完,此时再判断结果正确已经没意义了.

而且程序异常,不只是内部代码有问题,也有可能是外部原因(如信号等).

options★

我们之前写的waitpid,父进程一直在阻塞等待直至子进程结束,也就是说子进程结束之前父进程就一直在等待,什么事情也做不了,这好像有点怪,那么我们能不能让父进程非阻塞等待呢?

这里就用到了waitpid的第三个参数.

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

这个其实是个宏,内部其实是#define WNOHANG 1,也就是说你第3个参数填WNOHANG可以,填1也可以.

下面是waitpid的一段伪代码,当检测到子进程没有退出时,若检测到options是0,则父进程会直接被阻塞,本质是阻塞在系统函数内部等待子进程被唤醒.

而如果是1,即WNOHANG,会直接返回,而不进行阻塞等待.

那么问题来了,当options==0,子进程被唤醒时,是从if后面继续向下执行,还是重新执行waitpid呢?

但是是继续执行if后面的语句,因为寄存器EIP保存的是下一行代码的地址.

 我们换做代码来理解一下它.

 等待成功,而且子进程退出或者等待失败,就退出. 

如果子进程没有退出,那么就一直做检测,相当于轮询检测. 

可以看到,子进程运行期间,父进程每隔一秒都会轮询检测一下,当子进程结束时,waitpid也检测到子进程退出,此时res>0,便输出子进程的退出信息.然后父进程循环同时也结束了,结束进程.

当然可以让父进程具体处理一些任务,比如在函数最前面加上这些代码:

 

在等待期间的这个模块:

 

 然后我们再次执行:

这个时候,父进程在等待子进程推出的同时也可以执行对应的任务了. 

问题:既然进程具有独立性,进程退出码不也是子进程数据吗,父进程凭什么拿到呢?wait/waitpid究竟做了什么呢?

我们需要从僵尸进程谈起:它是一个死亡的进程,但是至少要保留该进程的PCB信息,里面保留了任何进程退出时的退出结果信息!

僵尸进程保留自己退出信息的目的就是让别的进程来读取的!

wait和waitpid本质上是读取子进程的task_struct结构。

我们看一下内核源代码:

 wait/waitpid本质是读取到这两个字段,然后位操作设置到status这个输出型变量里,我们就拿到了.

那么wait和waitpid有权限去读取这个内核数据结构内容吗?

答案是一定有,因为wait和waitpid是系统调用!系统调用就是操作系统在调用。

父进程没有权限读取,但我可以调用wait和waitpid让操作系统帮我去拿.

总结来说:父进程没有权限,但是wait/waitpid有权限,父进程可以调用wait/waitpid获取子进程的退出状态.

wait/waitpid本质上是把读取的子进程的退出状态 通过位操作设置到status这个输出型变量中.

所以到这里,进程的等待就结束了.

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

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

相关文章

Spring Cloud Hystrix简单实用

文章目录 一、简介二、快速开始1、pom依赖2、启动类注解3、服务降级配置HystrixCommand4、配置熔断策略5、测试 三、原理分析四、实际使用 一、简介 Hystrix&#xff0c;英文意思是豪猪&#xff0c;全身是刺&#xff0c;刺是一种保护机制。Hystrix也是Netflflix公司的一款组件。…

Kotlin获取Fragment中的组件

左边和右边分别是两个不同的Fragment&#xff0c;左边的Fragment中右一个Button组件&#xff0c;目标是想要获取这个组件的id&#xff0c;以便进行将右边的Fragment更改成另一个Fragmeent的操作。 left_fragment.xml <?xml version"1.0" encoding"utf-8&qu…

玩玩两个简单的python的web框架 flask、fastapi

IDEA连接远程解释器&#xff0c;本地代码编辑无法代码提示 一、Flask入门使用 官网 其它参考 注意 1.这里使用linux 192.168.72.126上远程解释器,需要/usr/bin/pip3 install flask&#xff0c;host参数不要使用localhost/127.0.0.1,即只监听本地的访问&#xff0c;会导致wind…

手机定屏死机问题操作指南

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、定屏死机问题抓取 Log 要求二、 复现定屏死机问题后做什么三、检查adb是否可连的方法四、连接adb 抓取以下Log五、如果adb不可连&#xff0c;执行下…

IDEA实用设置及插件

一、IDEA实用设置 二、IDEA实用插件 1. aiXcoder是一个基于最先进的深度学习技术的强大的代码完成器和代码搜索引擎。它有可能向您推荐一整行代码&#xff0c;这将帮助您更快地进行编码。AiXcoder还提供了一个代码搜索引擎&#xff0c;帮助您在GitHub上搜索API用例。 2. 阿里…

【iOS】—— 面向对象,Runtime,ARC等问题总结

对于暑假学习大多数是对之前学习的一个复习&#xff0c;在这里只做对之前学习欠缺知识的补充以及这些知识点涉及的一些问题&#xff0c;从问题入手学习。 文章目录 面向对象1.一个NSObject对象占多少内存&#xff1f;2.对象的isa指针指向哪里&#xff1f;3.OC的类信息存放在哪…

HarmonyOS/OpenHarmony应用开发-Stage模型UIAbility组件使用(五)

UIAbility组件间交互&#xff08;设备内&#xff09; UIAbility是系统调度的最小单元。在设备内的功能模块之间跳转时&#xff0c;会涉及到启动特定的UIAbility&#xff0c;该UIAbility可以是应用内的其他UIAbility&#xff0c;也可以是其他应用的UIAbility&#xff08;例如启动…

Dcat-admin使用 Alpine 双向数据绑定

介绍 Alpine.js 这东西真的轻量级&#xff0c;和vue相似&#xff0c;和 livewire 同一个作者&#xff0c;推荐大家使用&#xff0c;可以平替jquery 效果 实现 在 bootstrap.php 引入js Admin::headerJs([https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-y/alpinejs/3.9.0/…

unity 2019 内置渲染管线 光照与Lighting面板 参数详解

文章目录 前言一 Unity的光照 与 烘焙光照1 unity完整的光照组成2 光的亮度与颜色3 全局光照直接光间接光5 间接光≠光照贴图 二 色彩空间与自动烘焙1 unity的色彩空间2 自动烘焙光照 三 烘焙1 什么是烘焙&#xff0c;烘焙的是什么2 如何进行烘焙3 烘焙的优点和缺点4 查看光照贴…

666!一个图形化测绘平台渗透工具

工具介绍 集Fofa、Hunter鹰图、Shodan、360 quake、Zoomeye 钟馗之眼为一体的gui图形界面化工具。注&#xff1a;第一次使用选择保存&#xff0c;之后需要修改api点击修改即可&#xff0c;如果再点击保存将覆盖所有已经配置的API&#xff0c;则需要重新再配置。 关注【Hack分享…

Visual Studio 中的新特性:可视化宏扩展

今天&#xff0c;我们很高兴地宣布在 Visual Studio 17.7 预览版中推出可视化宏扩展功能。这个新功能通过可视化的方式对宏代码进行逐步扩展。 若要开始使用此功能&#xff0c;请确保你的 Visual Studio 版本更新到最新版本的 Visual Studio 预览版。 下面&#xff0c;我们来看…

Java Mybatis02+oracle拓展

0目录 Mybatis 02Oracle 拓展 1.Mybatis 02 创建数据库和表 创建工程 实体类 util工具类 接口方法 Resource Mapper xml文件 配置文件 测试 加入模糊查询&#xff08;根据姓名&#xff09; 测试结果 2.ParameterType语法 实战 参数为对象 参数为…