【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.mzph.cn/news/767331.shtml

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

相关文章

LeetCode---389周赛

题目列表 3083. 字符串及其反转中是否存在同一子字符串 3084. 统计以给定字符开头和结尾的子字符串总数 3085. 成为 K 特殊字符串需要删除的最少字符数 3086. 拾起 K 个 1 需要的最少行动次数 一、字符串及其反转中是否存在同一子字符串 直接暴力枚举即可&#xff0c;代码…

【PHP + 代码审计】数组函数

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

力扣面试150 x 的平方根 二分 换底法 牛顿迭代法 一题多解

Problem: 69. x 的平方根 思路 &#x1f468;‍&#x1f3eb; 参考题解 &#x1f496; 袖珍计算器算法 class Solution {public int mySqrt(int x){if (x 0)return 0; // Math.exp(3)&#xff1a;e的三次方int ans (int) Math.exp(0.5 * Math.log(x));return (long) (an…

配置环境安装包遇到的问题

1. error: could not install packages due to an oserror: [errno 28] no space left on device 说是磁盘占用过多&#xff0c;查看磁盘占用情况。 df -h 果然占比100% 清理了众多缓存.cache都不行&#xff0c;而且其他利用率很低&#xff0c;可能是pip install的过程中使…

qt 实现 轮播图效果,且还有 手动 上一页和下一页 已解决

QT中有 轮播图的需求&#xff0c;按照正常html版本 。只需要配置数组就能搞定&#xff0c;但是c qt版本 应该用什么了。 第一想到的是采用定时器。 // 定时器初始化{m_pTime new QTimer(this);m_pTime->start(4 * 1000);//启动定时器并设置播放时间间隔m_pAutoFlag true;/…

【python】python turtle绘制坤坤打球

一、效果图&#xff1a; 二、准备工作 &#xff08;1)、导入必要的模块&#xff1a; 代码首先导入了需要使用的模块&#xff1a;requests、lxml和csv。 import requests from lxml import etree import csv 如果出现模块报错 进入控制台输入&#xff1a;建议使用国内镜像源 p…

智慧公厕:跨界融合,打造智慧城市新名片

随着城市化进程的不断加快&#xff0c;公共厕所建设成为一个亟待解决的问题。传统的公厕存在着管理繁琐、卫生差、服务不到位等一系列问题&#xff0c;与城市发展的节奏不协调。为此&#xff0c;推进新型智慧公厕建设成为了一个重要的解决方案。智慧公厕的建设需要推进技术融合…

【论文精读】VIT:vision transformer论文

相关文章 【论文精读】Transformer&#xff1a;Attention Is All You Need 文章目录 相关文章一、文章概览&#xff08;一&#xff09;研究背景&#xff08;二&#xff09;核心思路&#xff08;三&#xff09;相关工作&#xff08;三&#xff09;文章结论 二、模型细节&#x…

【MySQL系列】优化数据库时区设置,确保数据一致性与准确性

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

图论基础|深度优先dfs、广度优先bfs

dfs 与 bfs 区别 提到深度优先搜索&#xff08;dfs&#xff09;&#xff0c;就不得不说和广度优先搜索&#xff08;bfs&#xff09;有什么区别 先来了解dfs的过程&#xff0c;很多录友可能对dfs&#xff08;深度优先搜索&#xff09;&#xff0c;bfs&#xff08;广度优先搜索…

鸿蒙Harmony应用开发—ArkTS-@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化

上文所述的装饰器仅能观察到第一层的变化&#xff0c;但是在实际应用开发中&#xff0c;应用会根据开发需要&#xff0c;封装自己的数据模型。对于多层嵌套的情况&#xff0c;比如二维数组&#xff0c;或者数组项class&#xff0c;或者class的属性是class&#xff0c;他们的第二…

MySQL--select count(*)、count(1)、count(列名) 的区别你知道吗?

MySQL select count(*)、count(1)、count(列名) 的区别&#xff1f; 这里我们先给出正确结论&#xff1a; count(*)&#xff0c;包含了所有的列&#xff0c;会计算所有的行数&#xff0c;在统计结果时候&#xff0c;不会忽略列值为空的情况。count(1)&#xff0c;忽略所有的列…

AtCoder Beginner Contest 346

A. Adjacent Product(循环) 题意 给出 N N N个数字 A 1 , A 2 , … , A N A_1, A_2, \ldots, A_N A1​,A2​,…,AN​。定义 B i A i A i 1 ( 1 ≤ i ≤ N − 1 ) B_i A_i \times A_{i 1}(1 \le i \le N - 1) Bi​Ai​Ai1​(1≤i≤N−1)。 请你打印 B 1 , B 2 , … , B …

javase day09笔记

第九天课堂笔记 构造方法★★★★ 完成对属性赋值构造方法的名字必须与类名一致没有返回值类型public 类名&#xff08;【参数】&#xff09;{ }构造方法在创建对象时同步执行没写无参构造&#xff0c;系统默认提供写了构造方法&#xff0c;系统不再提供构造方法:重载 引用数…

牛客NC108 最大正方形【中等 动态规划 Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/0058c4092cec44c2975e38223f10470e 思路 动态规划: 先初始化第一行和第一列。然后其他单元格依赖自己的上边&#xff0c;左边和左上角参考答案Java import java.util.*;public class Solution {/*** 代码中的类…

Naive UI:一个 Vue 3 组件库,比较完整,主题可调,使用 TypeScript,快有点意思。

在当今的前端开发领域&#xff0c;Vue 3已成为中后台应用的首选框架。为了满足开发者的需求&#xff0c;各种组件库如雨后春笋般涌现。其中&#xff0c;Naive UI以其独特的优势&#xff0c;成为了Vue 3开发者的得力助手。本文将深入探讨Naive UI的特性、优势以及如何使用它来提…

docker镜像安装空间不足no space left on device

报错&#xff1a;Error processing tar file(exit status 1): open /usr/local/lib/libmkl_tbb_thread.so.1: no space left on device 原先docker模型保存位置&#xff1a; docker info -f ‘{{ .DockerRootDir}}’ docker 高点版本&#xff0c;这里26.0 解决参考&#xf…

学习次模函数-第1章 引言

许多组合优化问题可以被转换为集合函数的最小化&#xff0c;集合函数是在给定基集合的子集的集合上定义的函数。同样地&#xff0c;它们可以被定义为超立方体的顶点上的函数&#xff0c;即&#xff0c;其中是基集合的基数-它们通常被称为伪布尔函数[27]。在这些集合函数中&…

Linux 创建交换空间

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

鸿蒙Harmony应用开发—ArkTS-应用级变量的状态管理

状态管理模块提供了应用程序的数据存储能力、持久化数据管理能力、UIAbility数据存储能力和应用程序需要的环境状态。 说明&#xff1a; 本模块首批接口从API version 7开始支持&#xff0c;后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 本文中T和S的含义…