说在前面

今天给大家带来操作系统中进程等待的概念,我们学习的操作系统是Linux操作系统。

我们今天主要的目标就是认识wait和waitpid这两个系统调用。

前言

那么这里博主先安利一下一些干货满满的专栏啦!

手撕数据结构https://blog.csdn.net/yu_cblog/category_11490888.html?spm=1001.2014.3001.5482这里包含了博主很多的数据结构学习上的总结,每一篇都是超级用心编写的,有兴趣的伙伴们都支持一下吧!
算法专栏https://blog.csdn.net/yu_cblog/category_11464817.html这里是STL源码剖析专栏,这个专栏将会持续更新STL各种容器的模拟实现。

STL源码剖析https://blog.csdn.net/yu_cblog/category_11983210.html?spm=1001.2014.3001.5482


什么是进程等待

简单来说, 进程等待是进程的一种状态, 是父进程等待子进程退出时的一个过程。

当然这样描述,大家肯定是不明白的,继续向后看,我相信大家就能明白了!

为什么需要进程等待

首先,我们知道,创建一个子进程,肯定是想让子进程去完成一些不同于父进程的东西。那么当子进程执行完之后,如果父进程还没有退出,子进程的状态叫做僵尸

  • 子进程退出,父进程不管子进程,子进程就要处于僵尸状态 --- 导致内存泄漏
  • 父进程创建了子进程,是要让子进程办事儿的,那么子 进程把任务完成的怎么样?父进程需要关心吗?如果需 要,如何得知?如果不需要,该怎么处理?

当子进程结束时,子进程的结束方式有三种:

  1. 子进程代码执行完,结果正确
  2. 子进程代码执行完,结果不正确
  3. 子进程代码没有执行完,程序崩溃

那么,父进程如果要通过判断子进程的执行情况,分别进行不同操作的时候,我们的父进程就要了解子进程的执行,是属于上面三种的哪一种。与此同时,父进程还需要回收子进程。

如何进行进程等待

进程等待有两个系统调用接口:

  1. wait
  2. waitpid

wait

首先,我们先把Makefile,myproc.c准备好

然后,我们在myproc.c里面先构建一个僵尸状态,然后我们再通过进程等待的系统调用接口去解决。

如图所示:通过代码构建一个僵尸状态。

在这份代码中,5秒之后,子进程就正常退出,此时的子进程就是一个Z状态。 

关于wait函数的更详细信息,我们可以通过手册来获取。

 wait接口的用法

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

验证代码和现象如下:

waitpid

第一个参数,表示要等待的进程的子进程的pid。

刚开始我们学习代码的时候,一般只有一个父进程一个子进程的情况。但是继续往后学习,我们接触到多进程的时候,我们就要使用waitpid接口,进行执行的进程的等待。

第二个参数是一个输出型参数,我们传入一个int类型变量status的地址,waitpid会给把等待到的进程的执行情况存储到status中带回来

第三个参数表示进行选择阻塞等待方式/非阻塞等待方式(后面我们会详细讲解和区分)。 

此时我们可以先看一个简单的例子: 

这里我们举例子使用了阻塞式等待,即第三个参数是0。

在代码中,我们让子进程传回一个特殊的数字,表示它的进程退出码,这里我们传的是105

最后打印一下status(代码没有体现,但是现象中是加上了打印status的,很简单,加一句话就行)

我们发现status并不是传回来的105。

这是因为:status并不是按照整数的方式来使用的,它是按照比特位的方式对status进行划分,一个32个,我们只学习低16个比特位置的划分。 

首先,我们所要明确的一个概念:
进程的退出码只有在进程正常结束的时候才有意义!

假设return 0表示正常退出,return 1表示段错误,exit()也一样,返回退出码的前提是,程序正常执行到了这一句。如果程序异常退出了,退出码毫无意义。

那么如何判断程序是否异常退出呢?看所接受到的信号是否为0

如果所接受到的信号为0 -- 表示程序正常退出。

接下来就是一个C语言的问题了 -- 我们如何从status这个32位整数里面得到退出码和信号呢?

这里博主直接给出结果了,如果不明白的伙伴要补习一下C语言了。

  • 退出码:(status>>8) & 0xFF
  • 信号:status & 0x7F

此时,博主再给大家再次强调一下这个重要概念 

两个重点:

  • 程序异常退出,本质是进程异常退出,本质是OS杀掉了这个进程
  • OS怎么杀——发送信号

一些程序异常退出的例子 

讲到这里,我们再回答三个问题:

1. 父进程通过wait/waitpid可以拿到子进程的退出结果,为什么要用waitpid/wait函数呢??直接全局变量不行吗??

当然不行,进程具有独立性,那么数据就要进行写 时拷贝,父进程肯定是拿不到的,更不用说信号了。

2. 既然进程是具有独立性的,进程退出码,不也是子进程的数据吗??父 进程又凭什么拿到呢?? wait/waitpid究竟干了什么呢?

这里我们要从僵尸进程说起
僵尸进程:至少要保留 pcb信息!task_struct里面保留了进程退出时的退 出结果信息!!

因此,wait/waitpid本质是读取了子进程的task_struct结构!

3. 那么现在的问题是:wait/waitpid有这个权利吗???

​​​​​​​别忘了, task_stuct是内核数据结构对象!当然有这个权利啊! 因为它们是系统调用啊! 它们在干活儿不就是OS在干活儿吗?

一个小总结

waitpid/wait 可以在目前的情况下,让进 程退出具有一定的顺序性! 将来可以让父进程进行更多的收尾工作!

一些补充

对于上层使用的人来说,我使用的时候还要对操作系统内核有一定了解,还得知道status的构成,我才能提取信息这样未免太麻烦了!

其实操作系统是给我们提供了宏的,一开始博主不告诉大家,是希望大家理解status的低层构成。

阻塞等待和非阻塞等待

因此,如果我们调用的是阻塞等待,父进程在子进程尚未结束的时候的状态是,挂起! 

重新理解挂起

​​​​​​​ 这种阻塞等待,其实是在系统调用接口(wait系列函数)中阻塞 也就是内核中阻塞

比如以前我们的scanf,cin 如果我们一直不往键盘输入。此时OS发现缓冲区里面资源没有就绪 系统就把我们的进程挂起! 此时,表面上看,就是卡住了!

而在哪里卡住? 由冯诺依曼体系我们知道, 键盘这些输入硬件,到显示器这种输出硬件中,肯定会经过操作系统,所以其实本质上也是在内核中卡住了! scanf,cin必定封装了系统调用!

阻塞的本质,是在内核中阻塞,在内核中把进程挂起!

基于非阻塞调用的轮询检测方案

那么非阻塞调用的时候,直接就返回了,父进程不等。
那我们怎么知道子进程的状态呢? 我们会每隔一段时间去检测一下子进程的状态,如果监测到子进程结束,就释放子进程这个叫做 --- 基于非阻塞调用的轮询检测方案。

总结

相信看到这里,大家对Linux操作系统中的进程等待已经有了一定的了解了。操作系统的学习是必要的,虽然它可能很枯燥。如果大家觉得这篇文章有帮助的话,不要忘记一键三连哦!!

多进程|基于非阻塞调用的轮询检测方案|进程等待|重新理解挂起|Linux OS的更多相关文章

  1. Linux下同步模式、异步模式、阻塞调用、非阻塞调用总结

    转自:http://www.360doc.com/content/13/0117/12/5073814_260691714.shtml 同步和异步:与消息的通知机制有关. 本质区别 现实例子 同步模式 ...

  2. 基于mykernel完成时间片轮询多道进程的简单内核

    基于mykernel完成时间片轮询多道进程的简单内核 原创作品转载请注明出处+中科大孟宁老师的linux操作系统分析:https://github.com/mengning/linuxkernel/ ...

  3. 支持阻塞操作和轮询操作的globalfifo设备驱动代码分析以及测试代码

    #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include ...

  4. 精讲响应式WebClient第2篇-GET请求阻塞与非阻塞调用方法详解

    本文是精讲响应式WebClient第2篇,前篇的blog访问地址如下: 精讲响应式webclient第1篇-响应式非阻塞IO与基础用法 在上一篇文章为大家介绍了响应式IO模型和WebClient的基本 ...

  5. 2017年5月12日15:10:46 rabbitmq不支持非阻塞调用服务器

    就像昨天碰到的问题描述一样,问题不是出在消费者上而是在生产者发送消息出现没有得到返回值时消息通道被挂起,rabbitmq发送的消息是阻塞调用即当发生阻塞时,继续发送的消息都堆在后面.在网上看到有两个方 ...

  6. php fsockopen()方法,简化,异步非阻塞调用

    介绍在项目中遇到一个问题,就是php是同步的读取下来的,如果一个方法请求的时间长了一点, 那么整个程序走下去将会遇到阻塞,现在我想触发这个方法,但是又不影响我下下面的程序正常的走下去.查了一上午的方法 ...

  7. rabbiitmq非阻塞调用

    https://blog.csdn.net/panxianzhan/article/details/50755409 https://blog.csdn.net/u013946356/article/ ...

  8. python_非阻塞套接字及I/O流

    http://www.cnblogs.com/lixy-88428977/p/9638949.html 首先,我们要明确2个问题: 普通套接字实现的服务端有什么缺陷吗? 有,一次只能服务一个客户端! ...

  9. 网络IO-阻塞、非阻塞、IO复用、异步

    网络socket输入操作分为两个阶段:等待网络数据到达和将到达内核的数据复制到应用进程缓冲区.对这两个阶段不同的处理方式将网络IO分为不同的模型:IO阻塞模型.非阻塞模型.多路复用和异步IO. 一 阻 ...

  10. 非阻塞IO与异步IO

    一.非阻塞IO的轮询读写 ---如果当前进程有多个输入终端和多个输出终端呢?while((n=read(STDIN_FILENO,buf,buf_size))>0){    if(write(S ...

随机推荐

  1. 解决SUM函数返回为NULL

    解决SUM函数返回为NULL SUM函数的作用:计算某一字段中所有行的数值和, 使用SUM函数进行对符合条件的结果行数进行求和. 问题产生: sum 求和时会对 null 进行过滤,不计算,但如果没有 ...

  2. 5G“乍到”,图扑带你了解室内定位可视化的实现与新突破

    前言 现代工业化的推进在极大加速现代化进程的同时也带来的相应的安全隐患,在传统的可视化监控领域,一般都是基于 Web SCADA 的前端技术来实现 2D 可视化监控,本系统采用 Hightopo 的  ...

  3. 十六、企业级私有仓库harbor

    系列导航 一.docker入门(概念) 二.docker的安装和镜像管理 三.docker容器的常用命令 四.容器的网络访问 五.容器端口转发 六.docker数据卷 七.手动制作docker镜像 八 ...

  4. JVM简单概述

    一.内存模型&分区 Java虚拟机在运行Java程序时,会管理着一块内存区域:运行时数据区 在运行时数据区里,会根据用途进行划分为以下模块: 1.Java虚拟机栈 2.本地方法栈 3.Java ...

  5. spring-transaction源码分析(3)Transactional事务失效原因

    问题概述 在Transactional方法中使用this方式调用另一个Transactional方法时,拦截器无法拦截到被调用方法,严重时会使事务失效. 类似以下代码: @Transactional ...

  6. mvn 编译异常:Fatal error compiling: 无效的标记: -parameters

    错误信息: [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin::compile (defaul ...

  7. 供应链投毒预警 | 恶意Py包仿冒tensorflow AI框架实施后门投毒攻击

    概述 本周(2024年01月15号),悬镜供应链安全实验室在Pypi官方仓库(https://pypi.org/)中捕获1起Py包投毒事件,投毒者利用包名错误拼写(typo-squatting)的攻击 ...

  8. Makeflie脚本使用

    1.目标 2.Makefile的作用 自动化编译仿真 文件有引用层级关系,Tb会引用RTL顶层,RTL顶层也会引用一些其他的小的模块,编译的时候被引用的文件需要先进行编译. 脚本有两种模式,debug ...

  9. springboot入参下划线转驼峰出参驼峰转下划线

    springboot入参出参下划线转驼峰 前言 因为历史原因前端入参和出参都为下划线,下划线对有亿点强迫症的我来说是不可接受的.因此就有了下面这篇. 本篇基于之前的一篇springboot封装统一返回 ...

  10. [转帖]redis-benchmark的使用总结

    redis-benchmark的使用总结 Redis简介: 测试需求: 测试环境架构 测试工具Redis-benchmark 1 redis-benchmark使用方法 参数的作用 2 测试查看 测试 ...