Python 之杀不死的Shell子进程
1.1 踩坑案例
踩坑的程序是个常驻的Agent类管理进程, 包括但不限于如下类型的任务在执行:
- a. 多线程的网络通信包处理
- 和控制Master节点交互
- 有固定Listen端口
- b. 定期作业任务, 通过subprocess.Pipe执行shell命令
- c. etc
发现坑的过程很有意思:
a.重启Agent发现Port被占用了
=> 立刻
想到可能进程没被杀死, 是不是停止脚本出问题
- => 排除发现不是, Agent进程确实死亡了
- => 通过
netstat -tanop|grep port_number
发现端口确实有人占用
=> 调试环境, 直接杀掉占用进程了之, 错失首次发现问题的机会
b.问题
在一段时间后
重现
, 重启后Port还是被占用
- 定位问题出现在一个叫做xxxxxx.sh的脚本, 该脚本占用了Agent使用的端口
- => 奇了怪了, 一个xxx.sh脚本使用这个奇葩Port干啥(大于60000的Port, 有兴趣的砖友可以想下为什么Agent默认使用6W+的端口)
- => review该脚本并没有进行端口监听的代码
- 定位问题出现在一个叫做xxxxxx.sh的脚本, 该脚本占用了Agent使用的端口
一拍脑袋,
c.进程共享了父进程资源
了
- => 溯源该脚本,发现确实是Agent启动的任务中的脚本之一
- => 问题基本定位, 该脚本属于Agent调用的脚本
- => 该Agent继承了Agent原来的资源FD, 也就是这个port
- => 虽然该脚本由于超时被动触发了terminate机制, 但terminate并没有干掉这个子进程
- => 该脚本进程的父进程(ppid) 被重置为了1
d.问题出在脚本进程超时kill逻辑
1.2 填坑解法
通过代码review, 找到shell具体执行的库代码如下:
'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
self._subpro = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,stderr=subprocess.PIPE,preexec_fn=_signal_handle
)
# 重点是shell=True !
把上述代码改为:
self._subpro = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE,stderr=subprocess.PIPE, preexec_fn=_signal_handle
)
# 重点是去掉了shell=True
1.3 坑位分析
Agent会在一个新创建的threading线程中执行这段代码, 如果线程执行时间超时(xx seconds), 会调用 self._subpro.terminate()
终止该脚本.
表面正常:
- 启用新线程执行该脚本
- 如果出现问题,执行超时防止hang住其他任务执行调用terminate杀死进程
深层问题:
- Python 2.7.x中subprocess.Pipe 如果shell=True, 会默认把相关的pid设置为shell(sh/bash/etc)本身(执行命令的shell父进程), 并非执行cmd任务的那个进程
- 子进程由于会复制父进程的opened FD表, 导致即使被杀死, 依然保留了拥有这个Listened Port FD
这样虽然杀死了shell进程(未必死亡, 可能进入defunct状态), 但实际的执行进程确活着. 于是1.1
中的坑就被结实的踩上了.
1.4 坑后扩展
1.4.1 扩展知识
本节扩展知识包括二个部分:
- Linux系统中, 子进程一般会继承父进程的哪些信息
- Agent这种常驻进程选择>60000端口的意义
扩展知识留到下篇末尾讲述, 感兴趣的可以自行搜索
1.4.1 技术关键字
- Linux系统进程
- Linux随机端口选择
- 程序多线程执行
- Shell执行
1.5 填坑总结
- 子进程会继承父进程的资源信息
- 如果只kill某进程的父进程, 集成了父进程资源的子进程会继续占用父进程的资源不释放, 包括但不限于
- listened port
- opened fd
- etc
- Python Popen使用上, shell的bool状态决定了进程kill的逻辑, 需要根据场景选择使用方式
Python 之杀不死的Shell子进程相关推荐
- python shell下载很慢_Python踩坑之旅其一杀不死的Shell子进程
1.1 踩坑案例 踩坑的程序是个常驻的Agent类管理进程, 包括但不限于如下类型的任务在执行:a. 多线程的网络通信包处理和控制Master节点交互 有固定Listen端口 b. 定期作业任务, 通 ...
- Python Process类开启多层级进程杀不死问题解决(从父进程杀死子进程)
Python Process类开启多层级进程杀不死问题 问题描述 问题分析 解决方案 问题描述 在使用python multiprocess包下的Process打开子进程,并在打开的子进程中再打开一个 ...
- python定时杀进程
python定时杀进程 之前写了个python脚本用selenium+phantomjs爬新帖子,在循环拉取页面的过程中,phantomjs总是block住,使用WebDriverWait设置最长等待 ...
- 如何让Java应用成为杀不死的小强?(中篇)
[这是一猿小讲的第 48 篇原创分享] 各位坐稳扶好,我们要开车了.不过在开车之前,我们还是例行回顾一下上期分享的要点. 上期我们抛了一个砖:"如何实现 Java 应用进程的状态监控,如果被 ...
- mysql 进程一直杀不死_进程一直杀不死
就是一个简单的杀死进程,名字也是对的,不知道为何用下面方法一直杀不死 private void KillProcess(string processName) { //获得进程对象,以用来操作 Sys ...
- 解决liunx进程杀不死的问题
liunx中有时候通过命令ps -ef|grep 进程名,查看进程pid号,杀进程的时候会杀不死,是因为可能杀的是子进程而不是父进程,整个进程树没有死.针对这种情况,我苦寻到一个命令,来解决这种问题. ...
- Android中创建杀不死的APP进程(5.0以下)
所谓的杀不死指的时进程被杀掉后,会自动重启. 即便时在设置里面强行停止后,也能够自动重启. 那么如何自动重启呢?目前想到的有3种方式: 1.使用AlarmManager来定时发intent启动 2.N ...
- 关于GPU上进程杀不死的解决
最近GPU上某些进程杀不死,查了一下说是因为父进程虽然kill了,但是子进程的内存还没有释放.因此要重新杀死父进程,我们通过 ps -ef|grep 进程ID ,来查找其父进程.然后再使用 ...
- python中执行shell脚本之subprocess模块_如何使用Python中的subprocess模块检查shell脚本的状态?...
我有一个简单的Python脚本,它将使用Python中的subprocessmdoule来执行shell脚本.在 下面是我的pythonshell脚本,它正在调用testing.shshell脚本,它 ...
最新文章
- Spring多数据源配置和使用
- Arduino 各种模块篇 motor shield
- 征战蓝桥 —— 2013年第四届 —— C/C++A组第10题——大臣的旅费
- POJ - 1284 Primitive Roots(原根+欧拉函数)
- 高等数学上-赵立军-北京大学出版社-题解-练习2.3
- 用计算机MR,计算机上的【MC、MR、M
- 又一位!发40篇SCI,90后博士受聘985教授
- php什么是变量6,PHP变量是什么
- core api其他电脑不能访问接口_第 12 篇:加缓存为接口提速
- c语言格式化浮点数多余的0
- 2022-2028年全球与中国细菌生物农药行业市场深度调研及投资预测分析
- 笔记本电脑应用商店服务器错误,打开win10商店出错 出现win10商店请稍后重试问题怎么办 - 驱动管家...
- android adb 分析,android adb shell常用脚本分析课件.ppt
- python怎么获取向量中非零元素的行号
- css之@media网页适配
- 【3d游戏模型】女骑士制作指南:硬表面和纹理
- 给定空间的四个点的笛卡尔积坐标,使用python、shell计算二面角
- k8s 中部署kafka集群
- c语言除法取两位小数点,高精度除法小数点位数
- 回归预测 | MATLAB实现ANN神经网络多输入单输出