说在前面

今天给大家带来操作系统中进程等待的概念,我们学习的操作系统是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. 什么是离散化?C++实现方法

    简介 离散化本质上可以看成是一种 哈希 ,其保证数据在哈希以后仍然保持原来的全/偏序关系. 通俗地讲,就是当我们只关心数据的大小关系时,用排名代替原数据进行处理的一种预处理方法.离散化本质上是一种哈希 ...

  2. 题解 [HDU6746] Civilization(贪心+模拟)

    来源:2020 年百度之星·程序设计大赛 - 初赛一 一道贪心 + 细节模拟题 题意很简单,这里不详细写了 观察题目,\(n\) 只有 500 ,可以 \(n \times n\) 枚举每个位置作为起 ...

  3. 技术分享 | 不同格式标准SBOM清单横评:SPDX、CDX和DSDX

    为了保证安全性.降低开发.采购及维护的相关成本,复杂动态的现代软件供应链对软件资产透明度提出了更高的要求.使用清晰的软件物料清单(SBOM)收集和共享信息,并在此基础上进行漏洞.许可证和授权管理等,可 ...

  4. 理解 docker volume

    1. docker volume 简介 文章 介绍了 docker image,它由一系列只读层构成,通过 docker image 可以提高镜像构建,存储和分发的效率,节省时间和存储空间.然而 do ...

  5. 基于python的药店药品信息管理系统-毕业设计-课程设计

    基于python+django+vue.js开发的药店信息管理系统 功能介绍 平台采用B/S结构,后端采用主流的Python语言进行开发,前端采用主流的Vue.js进行开发. 功能包括:药品管理.分类 ...

  6. Spring Cloud 系列:Seata 中TCC模式具体实现

    概述 https://seata.io/zh-cn/docs/dev/mode/tcc-mode https://seata.io/zh-cn/docs/user/mode/tcc TCC模式与AT模 ...

  7. 08-避免Latch的产生

    1.Latch简介 Latch就是锁存器,是一种在异步电路系统中,对输入信号电平敏感的单元,用来存储信息 锁存器在数据未锁存时,输出端的信号随输入信号变化,就像信号通过一个缓冲器,一旦锁存信号有效,数 ...

  8. 有了Composition API后,有些场景或许你不需要pinia了

    前言 日常开发时有些业务场景功能很复杂,如果将所有代码都写在一个vue组件中,那个vue文件的代码量可能就几千行了,维护极其困难.这时我们就需要将其拆分为多个组件,拆完组件后就需要在不同组件间共享数据 ...

  9. [转帖]tcplife的使用

    https://www.rutron.net/posts/2203/bcc-tcplife-usage/ 这篇文档主要演示了 tcplife(Linux eBPF/bcc) 工具的使用. 示例 tcp ...

  10. Python学习之十二_tkinter的学习与使用

    Python学习之十二_tkinter的学习与使用 摘要 本来想说会用QT5进行界面编程 但是发现比较繁琐 还是先学习使用 tkinter的方式进行界面化的编写和学习了 基础知识 tkinter是一个 ...