【Linux系统编程(进程编程)】进程的退出:父进程等待子进程的退出之僵尸进程与孤儿进程

文章目录

  • 一、进程退出
    • 1.1、进程正常退出方式
    • 1.2、异常退出
  • 二、父进程等待子进程退出(一)
    • 2.1、为什么要等待子进程退出
    • 2.2、(1)父进程等待子进程退出并收集子进程的退出状态
      • 如何等待
      • wstatus空
      • wstatus非空
    • 2.3、(2)子进程退出状态不被收集,变成僵尸进程(僵死进程)。
      • vfork没有使用wait,退出状态不被收集,子进程变成僵尸进程
      • fork没有使用wait,退出状态不被收集,子进程变成僵尸进程
  • 三、父进程等待子进程退出(二)
    • 3.1、相关函数
      • 阻塞等待
      • 非阻塞等待
    • 3.2、孤儿进程(面s)

一、进程退出

vfork保证子进程先运行,当子进程退出后,父进程才运行。

1.1、进程正常退出方式

共5种退出方式

  1. main函数调用return
  2. 进程调用exit(),标准c库
  3. 进程调用_exit()或者_Exit(),属于系统调用

补充

  1. 进程最后一个线程返回
  2. 最后一个线程调用pthread_exit

1.2、异常退出

  1. 主动调用abort(),放弃当前进程
  2. 当进程收到某些信号时,如ctrl+C【ctrl+Z???挂起】
  3. 最后一个进程对取消(cancellation)请求做出相应

不管进程如何终止,最后都会执行内核中同一段代码。这段代码为响应进程关闭素有打开的描述符,释放它所使用的存储器。

对上述任意一种终止情形,我们都希望终止进程能够通知其父进程它是如何终止的。对于三个终止函数(exit、_exit、_Exit),实现这一点的方法是,将其退出状态传递(exit status)作为参数传递给函数。

在异常终止情况下,内核(不是进程本身)产生一个指示其异常终止原因的一个状态(termination status)。在任意一种情况下,该终止进程的父进程都能调用wait或waitpid函数取得其终止状态。

那么收集退出状态无非就是给exit函数传递一个值。
image.png
image.png
建议使用exit,是对_exit或_Exit的封装,会对进程的缓冲区做些处理,而_exit或_Exit系统调用是直接退出。

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
        pid_t pid;
        int cnt = 0;

        pid = vfork();

        if(pid > 0)
        {
                while(1){
                        printf("cnt=%d\n", cnt);
                        printf("this is father process, pid:%d\n",getpid());
                        sleep(1);
                }
        }
        else if(pid == 0)
        {
                while(1){
                        printf("this is chlid process, pid:%d\n",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 3){
                                _Exit(0);//break;
                        }
                }
        }


        return 0;
}

二、父进程等待子进程退出(一)

2.1、为什么要等待子进程退出

问:创建子进程的目的是什么?

创建子进程的目的是为了让子进程干活,那么子进程不一定能把交给它的活顺利都做完。所以我们比较关心子进程干没干完活,干的怎么样。所以我们要等待子进程的退出,并且收集退出的状态。
我不仅要等待子进程的退出,而且还要检测你这个活干的怎么样。 你正常退出,你活干到什么程度,根据exit的退出码的不同进程判断。你没干完是异常退出,你是不想干了还是其它原因ctrl+C了等等。这是父进程为什么要等待子进程的退出的原因。

2.2、(1)父进程等待子进程退出并收集子进程的退出状态

调用wait函数,解析返回状态码。子进程调用exit返回状态码。
调用wait可以防止子进程变成僵尸进程。
image.png


如何等待

使用wait函数
image.png
函数参数是一个地址
wstatus参数:是一个整型数指针
wstatus非空:子进程退出状态放在它所指向的地址中。
wstatus空:不关心退出状态。

wstatus空

父进程中使用了wait函数,就不会出现子进程变成僵尸进程的情况。
image.png

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
        pid_t pid;

        int cnt = 0;

        pid = fork();

        if(pid > 0)
        {
                wait(NULL);
                while(1){
                        printf("cnt=%d\n", cnt);
                        printf("this is father process, pid:%d\n",getpid());
                        sleep(1);
                }
        }
        else if(pid == 0)
        {
                while(1){
                        printf("this is chlid process, pid:%d\n",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 5){
                                exit(0);
                        }
                }
        }


        return 0;
}

wstatus非空

非空:检查退出状态,存在status里面
image.png

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
        pid_t pid;

        int cnt = 0;
        int status = 10;

        pid = fork();

        if(pid > 0)
        {
                wait(&status);
                printf("chlid quit, chlid status:%d\n", WEXITSTATUS(status));
                while(1){
                        printf("cnt=%d\n", cnt);
                        printf("this is father process, pid:%d\n",getpid());
                        sleep(1);
                }
        }
        else if(pid == 0)
        {
                while(1){
                        printf("this is chlid process, pid:%d\n",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 5){
                                exit(3);
                        }
                }
        }


        return 0;
}

2.3、(2)子进程退出状态不被收集,变成僵尸进程(僵死进程)。

vfork没有使用wait,退出状态不被收集,子进程变成僵尸进程

没有使用wait,退出状态不被收集

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
        pid_t pid;

        int cnt = 0;

        pid = vfork();

        if(pid > 0)//父进程
        {
                while(1){
                        printf("cnt=%d\n", cnt);
                        printf("this is father process, pid:%d\n",getpid());
                        sleep(1);
                }
        }
        else if(pid == 0)
        {
                while(1){
                        printf("this is chlid process, pid:%d\n",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 3){
                                exit(0);//break;
                        }
                }
        }


        return 0;
}

使用ps查看进程
image.png
S+ 是正在运行的状态
Z+ 是僵尸进程 zombie


fork没有使用wait,退出状态不被收集,子进程变成僵尸进程

fork 父进程没有wait
同样,子进程执行结束,父进程没收集其退出状态,变为了僵尸进程
image.png

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
        pid_t pid;

        int cnt = 0;

        pid = fork();

        if(pid > 0)
        {
                while(1){
                        printf("cnt=%d\n", cnt);
                        printf("this is father process, pid:%d\n",getpid());
                        sleep(1);
                }
        }
        else if(pid == 0)
        {
                while(1){
                        printf("this is chlid process, pid:%d\n",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 5){
                                exit(0);
                        }
                }
        }


        return 0;
}

三、父进程等待子进程退出(二)

3.1、相关函数

image.png

  • 如果其所有子进程都在执行,则阻塞
  • 如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回
  • 如果它没有任何子进程,则立即出错返回

对于waitpid函数中几个参数解释如下:

  • pid == -1 等待任意子进程。就这一方面而言,waitpid与wait等效。
  • pid > 0 等待其进程ID与pid相等的子进程。
  • pid == 0 等待其组ID等于调用进程组ID的任一子进程。
  • pid < -1 等待其组ID等于pid绝对值的任一子进程。

status参数:是一个整型数指针
非空:子进程退出状态放在它所指向的地址中。
空:不关心退出状态
image.png

阻塞等待

阻塞等待不会出现僵尸进程情况


#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
        pid_t pid;

        int cnt = 0;
        int status = 10;

        pid = fork();

        if(pid > 0)
        {
                wait(&status);//阻塞等待
                printf("chlid quit, chlid status:%d\n", WEXITSTATUS(status));
                while(1){
                        printf("cnt=%d\n", cnt);
                        printf("this is father process, pid:%d\n",getpid());
                        sleep(1);
                }
        }
        else if(pid == 0)
        {
                while(1){
                        printf("this is chlid process, pid:%d\n",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 3){
                                exit(3);
                        }
                }
        }


        return 0;
}

非阻塞等待

非阻塞(se)等待,子进程也会变成僵尸进程(实验证明)。而且waitpid马上打印,好像不关心子进程,所以使用不多。一般使用wait。

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
        pid_t pid;

        int cnt = 0;
        int status = 10;

        pid = fork();

        if(pid > 0)
        {
                //wait(&status);
                waitpid(pid, &status, WNOHANG);//非阻塞等待
                printf("chlid quit, chlid status:%d\n", WEXITSTATUS(status));
                while(1){
                        printf("cnt=%d\n", cnt);
                        printf("this is father process, pid:%d\n",getpid());
                        sleep(1);
                }
        }
        else if(pid == 0)
        {
                while(1){
                        printf("this is chlid process, pid:%d\n",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 3){
                                exit(3);
                        }
                }
        }
        return 0;
}

3.2、孤儿进程(面s)

父进程不等待子进程退出,在子进程结束之前就结束了自己的“生命”,此时子进程叫做孤儿进程。
Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程。
init进程ID为1
实验验证:

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
        pid_t pid;

        int cnt = 0;
        int status = 10;

        pid = fork();

        if(pid > 0)
        {
                printf("this is father process, pid:%d\n",getpid());
        }
        else if(pid == 0)
        {
                while(1){
                        printf("chlid pid:%d, my father pid:%d\n",getpid(), getppid());
                        sleep(1);
                        cnt++;
                        if(cnt == 5){
                                exit(3);
                        }
                }
        }

        return 0;
}

image.png
这里父进程退出后,新的父进程的进程id为1468而不是1。
是因为:这里是在图形界面伪终端运行的,shell其实是init进程的一个子进程,故孤儿进程其实是被shell所收养
在字符化界面运行时,ppid就是1了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据背后的力量:揭秘中间件中的二分查找与树结构应用

平时写业务代码的时候很少写对应的算法&#xff0c;因为很少会在内存中存储大量数据&#xff0c;在需要比较大量数据的查找时&#xff0c;多会依赖的中间件&#xff0c;而中间件底层就应用了很多不同算法&#xff0c;尤其是树结构的查找存储算法&#xff0c;二分查找算法在树里…

状态管理@Prop、@Link装饰器

Prop Link 父子组件进行数据同步化 prop 单向同步 只支持string、number、boolean、enum类型 负组件对象类型&#xff0c;总组件是对象类型 不可以是数组、any 不允许子组件初始化 Link双向同步 父子类型一直&#xff1a;string、number、boolean、enum、object、class以及他们…

centos7 yum 安装mysql8.0.36

一、前置条件&#xff1a;CentOS Mini 安装需要先安装ifconfig 和 wget工具&#xff0c;参考步骤&#xff1a; 1.首先&#xff0c;让我们找出哪个包提供了ifconfig命令。要完成这项任务&#xff0c;输入以下命令&#xff1a; yum provides ifconfig 输出&#xff1a; [rootlo…

Vue3 + Django 前后端分离项目实现密码认证登录

1、功能需求 通常中小型前后端项目&#xff0c;对安全要求不高&#xff0c;也可以采用密码认证方案。如果只用django来实现非常简单。采用 Vue3 前后端分离架构&#xff0c;实现起来稍繁琐一点&#xff0c;好处是可以利用各种前端技术栈&#xff0c;如element-plus UI库来渲染…

Android Jetpack Compose基础之组件的帧渲染

Android Jetpack Compose基础之组件的帧渲染 组合布局LayoutModifier示例 LayoutCompsable示例 绘制CanvasDrawModifierDrawModifier-drawWithContent示例 DrawModifier-drawBehind源码示例 DrawModifier-drawWithCache源码示例 拓展Modifier.graphicsLayer Android View 系统&…

6-191 拓扑排序

一项工程由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其他子任务后才能执行。例如,下图表示了一项工程若干子任务之间的先后关系。 编写函数输出所有子任务的拓扑序列。 函数接口定义: Status Push_SeqStack(SeqStack &s, ElemType x)//入栈,x入到…

Polar 2024春季个人挑战赛 Jay17 WP

Polar 2024春季个人挑战赛 Rank&#xff1a;7 【WEB】机器人 开题 起手敏感文件robots.txt 【WEB】PHP反序列化初试 最简单的php反序列化 POC&#xff1a; <?php class Easy{public $name;public function __wakeup(){echo $this->name;} } class Evil{public $evi…

2、Jenkins持续集成-gitlab安装和源码上传

文章目录 1、Gitlab代码托管服务器安装2、源代码上传托管 环境&资源准备 统一采用VMware中安装CentOS7&#xff0c;安装教程&#xff0c;统一设置静态IP资源包都存在于我的资源里面 资源版本&位置 名称机器IP软件代码托管服务器192.168.2.100Gitlab-12.4.2持续集成服…

idm下载器 idm网络加速器 Internet Download Manager(IDM)免激活不弹窗版

idm下载器官方版英文名为Internet Download Manager。是一款界面简单、体积小巧下载速度飞快的下载工具&#xff0c;支持断点续传、多个任务同时下载&#xff0c;同时还支持限速下载&#xff0c;在自义文件类型里还可以自由设置下载的文件类型。 一、IDM软件安装 Internet Dow…

Linux安装Nginx及配置TCP负载均衡

目录 1、安装编译工具及库文件2、下载解压Nginx压缩包3、Ngnix配置Tcp负载均衡4、配置Ngnix的文件5、Nginx启动 1、安装编译工具及库文件 yum -y install make zlib zlib-devel gcc-c libtool openssl openssl-devel pcre-devel2、下载解压Nginx压缩包 wget https://nginx.o…

Docker 安装 Tomcat

目录 1. 总体步骤 2.安装tomcat 2.1 搜索 2.2 拉取 2.3 查看是否拉取到镜像 2.4 运行镜像 2.5 访问tomcat首页 1. 总体步骤 搜索镜像拉取镜像查看镜像启动镜像停止容器移除容器 2.安装tomcat 2.1 搜索 2.2 拉取 docker pull tomcat 2.3 查看是否拉取到镜像 docker …

连接医患的桥梁:深入了解互联网医院APP的开发与优化

当下&#xff0c;互联网技术的不断发展&#xff0c;越来越多的医院和医疗机构开始关注并投入到互联网医院APP的开发与优化中。接下来&#xff0c;小编将与大家共同探讨互联网医院APP的开发与优化。 一、互联网医院APP的开发原则 &#xff08;1&#xff09;用户体验至上 界面设…

卷积篇 | YOLOv8改进之主干网络中引入可变形卷积DConv

前言:Hello大家好,我是小哥谈。可变形卷积模块是一种改进的卷积操作,它可以更好地适应物体的形状和尺寸,提高模型的鲁棒性。可变形卷积模块的实现方式是在标准卷积操作中增加一个偏移量offset,使卷积核能够在训练过程中扩展到更大的范围,从而实现对尺度、长宽比和旋转等各…

git基础-查看提交历史

查看提交历史 在创建了多个提交之后&#xff0c;或者如果克隆了一个具有现有提交历史的存储库&#xff0c;可能会想要回顾一下发生了什么。最基本和强大的工具就是 git log 命令。 运行下git log查看下输出状态 默认情况下&#xff0c;不带任何参数运行 git log 命令会以逆时…

拌合楼管理系统(八) c#海康威视摄像头车牌识别

前言: c#调用海康威视SDK实现车牌识别 原本以为海康威视sdk的Demo里面没有车牌识别的实例,后来发现自己肤浅了,官方是有提供的,只是车牌识别是通过安防布警的方式实现的.程序主动监听,触发告警后获取到车牌信息. 一、接口调用的流程&#xff1a; 首先初始化sdk -> 开…

袁志佳:前端全栈工程师精英班

教程介绍 本套课程涵盖大前端的全部领域&#xff0c;并对传统的Web前端全栈深入教学。如利用前端知识开发 AI、VR、AR、iOS、Android、PC、Server、智能硬件。 同时我们将核心打造 JavaScript语言新发展、Vue源码分析、前端持续集成方案汇总、MV*框架深度分析 、前端图形学、N…

亚稳态及其解决办法

异步电路 亚稳态 亚稳态亚稳态的产生原因什么是同步异步信号怎么消除亚稳态 亚稳态 在数字电路中&#xff0c;每一位数据不是1&#xff08;高电平&#xff09;就是0&#xff08;低电平&#xff09;。当然对于具体的电路来说&#xff0c;并非1&#xff08;高电平&#xff09;就是…

elementary OS7 Ubuntu 22.04中硬盘挂载报错

elementary OS7 Ubuntu 22.04中硬盘挂载报错 背景目标思路解决方法 背景 上周末安装elementaryos7的过程中将windows10的引导文件搞丢了&#xff0c;这两天准备修复一下&#xff0c;保险期间将固态硬盘上的文件备份到移动硬盘上&#xff0c;备份过程中出现报错的问题&#xff…

2024常用Web支付开发讲解教程

课程介绍 本教程为web支付开发&#xff0c;讲解了最常用的两钟支付&#xff1a;支付宝支付和微信支付&#xff0c;服务器配置和API对接&#xff0c;学完本课程可以学会微信支付、和支付宝支付开发。 学习资料 链接&#xff1a;https://pan.baidu.com/s/19WarLP2dO4dFvNbIHLU…

C++类的6个默认成员函数(构造)

C类和对象基础-CSDN博客https://blog.csdn.net/lh11223326/article/details/136834917?spm1001.2014.3001.5501 目录 1.构造函数 概念 特性 2.析构函数 概念 特性 3.拷贝构造函数 概念 特征 4.赋值运算符重载&#xff08;构造实现&#xff09; 运算符重载 赋值运算…
最新文章