Linux: bash起后台进程引发的僵尸进程
1. 前言
限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
2. 案例
原来的故事是 这样 的,感兴趣的读者可以直接前往。我从中截取了一段重现故事中问题的代码(对原代码做了小小调整):
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>#define SLEEP_SCRIPT_PATH "/home/bill/Study/qemu-lab/app/issue/1/sleep.sh&"int main(void)
{int pid;if ((pid = fork()) == 0) {printf("children: %d\n", getpid());/* /bin/bash -c /home/bill/Study/qemu-lab/app/issue/1/sleep.sh& */execle("/bin/bash", "/bin/bash", "-c", SLEEP_SCRIPT_PATH, (char *)0, NULL);}printf("parent: %d\n", getpid());//printf("waitfing for children... ");//wait(NULL);//printf("done.\n");while (1)sleep(1);return 0;
}
sleep.sh
的内容如下:
#!/bin/bashsleep 3
编译并运行:
$ make zombie_issue$ strace -f -t -e execve ./zombie_issue
16:28:33 execve("./zombie_issue", ["./zombie_issue"], [/* 69 vars */]) = 0
parent: 11128
strace: Process 11129 attached
children: 11129
[pid 11129] 16:28:33 execve("/bin/bash", ["/bin/bash", "-c", "/home/bill/Study/qemu-lab/app/is"...], NULL) = 0
strace: Process 11130 attached
[pid 11130] 16:28:33 execve("/home/bill/Study/qemu-lab/app/issue/1/sleep.sh", ["/home/bill/Study/qemu-lab/app/is"...], [/* 3 vars */] <unfinished ...>
[pid 11129] 16:28:33 +++ exited with 0 +++
[pid 11128] 16:28:33 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=11129, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
[pid 11130] 16:28:33 <... execve resumed> ) = 0
strace: Process 11131 attached
[pid 11131] 16:28:33 execve("/bin/sleep", ["sleep", "3"], [/* 3 vars */]) = 0
[pid 11131] 16:28:36 --- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
[pid 11128] 16:28:36 --- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
[pid 11130] 16:28:36 --- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
[pid 11131] 16:28:36 +++ exited with 0 +++
[pid 11130] 16:28:36 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=11131, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
[pid 11130] 16:28:36 +++ exited with 0 +++
16:28:37 --- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---$ ps -ef -o pid,ppid,commPID PPID COMMAND9539 2774 bash11133 9539 \_ ps9439 2774 bash11126 9439 \_ strace11128 11126 \_ zombie_issue11129 11128 \_ bash <defunct>
看看,进程 11129
进程变僵尸了:<defunct>
标注表示进程变僵尸了。用 top
可以观察到变 Z
了:
top - 16:51:36 up 5:39, 1 user, load average: 0.09, 0.04, 0.01
Tasks: 1 total, 0 running, 0 sleeping, 0 stopped, 1 zombie
%Cpu(s): 0.5 us, 2.1 sy, 0.0 ni, 97.4 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 4015908 total, 844272 free, 928832 used, 2242804 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 2735724 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 11129 bill 20 0 0 0 0 Z 0.0 0.0 0:00.02 bash
开始分析问题之前,我们先来了解 bash
是怎么处理 & 操作符
的:
If a command is terminated by the control operator &, the shell executes the
command in the background in a subshell. The shell does not wait for the command
to finish, and the return status is 0.
上面是摘自 bash手册 原文,翻译下它的意思:
从 bash
启动的命令,如果尾接 & 操作符
,则 bash
启动 子shell
来运行命令,而 bash
本身不等待
(即不对命令程序发起 wait()
调用)命令的结束,直接以退出码 0 退出。
我们再来简单了解下,什么样的进程会变成 僵尸进程
:
一个进程退出了,其存活的父进程
又不对其进行回收(没有对进程发起 wait()
调用),则该进程就会变成 僵尸进程
。
有了上述对 bash & 操作符
和 僵尸进程
的基础知识,我们就可以来理一理为什么会出现僵尸进程了。
我们不关注用来调试的 strace
进程,直接从 zombie_issue
说起。结合 strace
的追踪记录,以及程序 zombie_issue
的输出信息,我们按 进程 PID
来小结一下出现的几个进程:
11128: zombie_issue 进程
11129: zombie_issue 进程 fork 的子进程,用来启动程序 /bin/bash
11130: /bin/bash 的子shell,用来启动脚本 sleep.sh
11131: 运行脚本 sleep.sh 中 sleep 3 语句的进程
上面说了,进程变僵尸,是因为无人对它进行回收。我们一步步来看,为什么 进程 11129
最后变成了僵尸:
1. 脚本 sleep.sh 中执行 sleep 3 语句的进程 11131 运行完成后,子shell进程 11130 对其进行了回收,所以它不会变僵尸;
2. 子 shell 进程 11130 等到执行 sleep 3 语句的进程 11131 退出后,它自己也退出了。此时因为启动它的父进程程序 /bin/bash 已经退出了,它变成了无人理的孤儿,那么谁来回收它呢?针对这种父进程比子进程先结束的情形,Linux内核会将子进程托孤给 始祖进程init,由 init进程 负责完成子进程的回收。于是,我们的孤儿进程 11130 也被回收了,所以它不会变僵尸;
3. 而启动程序 /bin/bash 的进程 11129 ,自从它退出后,父程序 zombie_issue 进程 11128 对它不理睬,任其曝尸荒野,何其惨也,但由于父进程 zombie_issue 又没有退出,Linux内核也不会将其托孤给 init 进程,所以只能变僵尸了。
通过上面的分析,我们知道了 进程 11129
为什么变僵尸的原因。
上面的测试代码单独拿出来,就是一个编程BUG:存活的父进程
理应对子进程发起 wait()
。如果放开对代码中的 wait()
调用的注释,就不会出现僵尸进程。
这是一个简单的问题,但放在复杂的环境下,我们确实可能犯这样的错误。其实仅仅是要模拟出现僵尸进程的情形,上面的测试代码还可以简化:
#include <unistd.h>
#include <sys/wait.h>int main(void)
{int pid;if ((pid = fork()) == 0) {execle("/bin/bash", "/bin/bash", "-c", "/bin/ls", (char *)0, NULL);}//wait(NULL);while (1)sleep(1);return 0;
}
3. 参考资料
cron 僵尸进程问题分析
man bash
相关文章:
Linux: bash起后台进程引发的僵尸进程
1. 前言 限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。 2. 案例 原来的故事是 这样 的,感兴趣的读者可以直接前往。我从中截取了一段重现故事中问题的代码(对原代码做了小小调整&a…...

网络安全攻防中,Rock-ON自动化的多功能网络侦查工具,Burpsuite被动扫描流量转发
网络安全攻防中,Rock-ON自动化的多功能网络侦查工具,Burpsuite被动扫描流量转发。 #################### 免责声明:工具本身并无好坏,希望大家以遵守《网络安全法》相关法律为前提来使用该工具,支持研究学习ÿ…...

电子技术——共模抑制
电子技术——共模抑制 我们在之前学习过,无论是MOS还是BJT的差分输入对,共模信号并不会改变漏极电流的大小,因此我们说差分输入对共模信号无响应。但是实际上由于各种客观非理想因素,例如电流源有限阻抗等,此时共模是影…...

对KMP简单的理解
声明:下边的例子均表示下标从1开始的数组 ne数组的定义: next[i] 就是使子串 s[1…i] 有最长相等前后缀的前缀的最后一位的下标。ne[i]也可以表示相等子串的长度 准备执行jne[j]时, 表示当前s[i]!p[j1] , 如果ne[j]1 ,那么下…...
Hibernate不是过时了么?SpringDataJpa又是什么?和Mybatis有什么区别?
一、前言 ps: 大三下学期,拿到了一份实习。进入公司后发现用到的技术栈有Spring Data Jpa\Hibernate,但对于持久层框架我只接触了Mybatis\Mybatis-Plus,所以就来学习一下Spring Data Jpa。 1.回顾MyBatis 来自官方文档的介绍:MyBatis 是一款…...

数学建模拓展内容:卡方检验和Fisher精确性检验(附有SPSS使用步骤)
卡方检验和Fisher精确性检验卡方拟合度检验卡方独立性检验卡方检验的前提假设Fisher精确性检验卡方拟合度检验 卡方拟合度检验概要:卡方拟合度检验也被称为单因素卡方检验,用于检验一个分类变量的预期频率和观察到的频率之间是否存在显著差异。 卡方拟…...
【Python学习笔记之七大数据类型】
Python数据类型:Number数字、Boolean布尔值、String字符串、list列表、tuple元组、set集合、dictionary字典 int整数 a1 print(a,type(a))float浮点数 b1.1 print(b,type(b))complex复数 c100.5j print(c,type(c))bool布尔值:True、False,true和false并非Python…...
Android系统之onFirstRef自动调用原理
前言:抽丝剥茧探究onFirstRef究竟为何在初始化sp<xxx>第一个调用?1.onFirstRef调用位置<1>.system/core/libutils/RefBase.cpp#include <utils/RefBase.h>//1.初始化强指针 void RefBase::incStrong(const void* id) const {weakref_i…...

ipv6上网配置
一般现在的宽带都已经支持ipv6了,但是需要一些配置才能真正用上ipv6。记录一下配置过程。 当前测试环境为移动宽带,光猫下面接了一个路由器,家里所有的设备都挂到这个路由器下面的。 1. 光猫改桥接 光猫在使用路由模式下,ipv6无…...
python实现聚类技术—复杂网络社团检测 附完整代码
实验内容 某跆拳道俱乐部数据由 34 个节点组成,由于管理上的分歧,俱乐部要分解成两个社团。 该实验的任务即:要求我们在给定的复杂网络上检测出两个社团。 分析与设计 实验思路分析如下: 聚类算法通常可以描述为用相似度来衡量两个数据的远近,搜索可能的划分方案,使得目标…...

如何判断两架飞机在汇聚飞行?(如何计算两架飞机的航向夹角?)内含程序源码
ok,在开始一切之前,让我先猜一猜,你是不是想百度“二维平面下如何计算两个移动物体的航向夹角?”如果是,那就请继续往下看。 首先,我们要明确一个概念:航向角≠航向夹角!࿰…...
Scipy稀疏矩阵bsr_array
文章目录基本原理初始化内置方法基本原理 bsr,即Block Sparse Row,bsr_array即块稀疏行矩阵,顾名思义就是将稀疏矩阵分割成一个个非0的子块,然后对这些子块进行存储。通过输入维度,可以创建一个空的bsr数组࿰…...
LeetCode笔记:Weekly Contest 332
LeetCode笔记:Weekly Contest 332 1. 题目一 1. 解题思路2. 代码实现 2. 题目二 1. 解题思路2. 代码实现 3. 题目三 1. 解题思路2. 代码实现 4. 题目四 1. 解题思路2. 代码实现 比赛链接:https://leetcode.com/contest/weekly-contest-332/ 1. 题目一…...

autox.js在vscode(win7)与雷神模拟器上的开发环境配置
目录 下载autox.js 安装autox.js? 在电脑上搭建autox.js开发环境 安装vscode 安装autox.js插件 雷神模拟器连接vscode 设置雷神模拟器IP 设置autox.js应用IP地址等 下载autox.js 大体来说,就是一个运行在Android平台上的JavaScript 运行环境 和…...

创建阿里云物联网平台
创建阿里云物联网平台 对云平台设备创建过程做记录,懒得再看视频 文章参考视频:https://www.bilibili.com/video/BV1jP4y1E7TJ?p26&vd_source50694678ae937a743c59db6b5ff46c31 阿里云:https://www.aliyun.com 1.物联网平…...

【链式二叉树】数据结构链式二叉树的(万字详解)
前言: 在上一篇博客中,我们已经详解学习了堆的基本知识,今天带大家进入的是二叉树的另外一种存储方式----“链式二叉树”的学习,主要用到的就是“递归思想”!! 本文目录1.链式二叉树的实现1.1前置说明1.2结…...
Koa2篇-简单介绍及使用
一.简介koa2是基于 Node.js 平台的下一代 web 开发框架, 致力于成为一个更小、更富有表现力、更健壮的 Web 框架。 可以避免异步嵌套. express中间件是异步回调,Koa2原生支持async/await二.async/awaitconst { rejects } require("assert"); const { resolve } req…...

Linux ALSA 之十一:ALSA ASOC Path 完整路径追踪
ALSA ASOC Path 完整路径追踪一、ASoc Path 简介二、ASoc Path 完整路径2.1 tinymix 设置2.2 完整路径 route一、ASoc Path 简介 如前面小节所描述,ASoc 中 Machine Driver 是 platform driver 和 codec driver 的粘合剂,audio path 离不开 FE/BE/DAI l…...

【Spring Cloud总结】1、服务提供者与服务消费者快速上手
目录 文件结构 代码 1、api 1.1实体类(Dept ) 1.2数据库 2、provider 2.1 DeptController 2.2 DeptDao 2.3 DeptService 2.4 DeptServiceImpl 2.5 application.yml 3、consumer 3.1 ConfigBean 3.2 DeptConsumerController 测试 1.启动…...
若依项目学习之登录生成验证码
若依项目学习之登录生成验证码 使用DefaultKaptcha生成验证码 /*** 验证码配置* * author ruoyi*/ Configuration public class CaptchaConfig {/*** 生成字符类型的验证码**/Bean(name "captchaProducer")public DefaultKaptcha getKaptchaBean(){DefaultKaptcha…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...

MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...