python 执行 shell 命令卡死

最近发现了一个 python 特有的卡死问题,是通过 python 调用 shell 命令出现的,特此记录一下。

 

1、问题描述

这里我用一个例子来进行说明,并非真实使用场景。

 

1.1、普通 shell 命令执行:

yes yes | echo 'hello'

在 shell 中能够正常结束并输出。

 

1.2、python 调用 shell 命令执行:

import osos.system("yes yes | echo 'hello'")

但在 python 中会卡死,用其他调用函数或者换 subprocess 模块也一样。

 

1.3、yes 进程

这里也解释一下 yes 进程的作用,一些命令可能需要手动输入 ‘yes’ 来告知选择,但不方便自动化,所以利用 yes 进程不断地输出 ‘yes’ 来自动填。

 

2、问题分析&解决

 

2.1、管道

yes "yes" | $(其他命令)

‘|’其实是管道符号,前后两句命令其实是通过管道通信的,

即前面命令的结果写入管道,而后面命令从管道中读取数据作为输入。

 

管道遵循 FIFO 的使用方式

由于 yes 进程是不断地输出的,命令执行时间为无限(若不从外部中止);

而此时其他命令执行时间短,这时候管道的读端已经关闭,

这时 yes 进程再想写入管道的时候,内核立刻将 SIGPIPE 发送给 yes 进程,SIGPIPE 默认行为是终止进程。

/* write on a pipe with no one to read it */#define        SIGPIPE        13

也即术语:管道破裂

管道破裂,这个信号通常在进程间通信产生,比如采用 FIFO(管道)通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到 SIGPIPE 信号.  -- 维基百科

 

2.2、python 问题

那为什么 shell 执行不会卡死,通过 python 调用 shell 命令反而卡死了呢?

On Unix, Python sets SIGPIPE to SIG_IGN on startup, because it prefers to check every write and raise an IOError exception rather than taking SIGPIPE. 

Python 为了自己能处理写入错误,报告异常,启动时就把 SIGPIPE 设置为忽略状态。

但是对于非 python 的类 Unix 中的子进程,是靠 SIGPIPE 进行通信的。

如果不采用 SIGPIPE 的话,就需要自己检查系统调用 write 返回的 EPIPE。

 

2.3、如何解决​​​​​​​

import signal# 恢复为默认状态signal.signal(signal.SIGPIPE, signal.SIG_DFL)

 

3、结论

使用 python 调用子进程执行 bash 语句要注意管道破裂问题,为保安全,可以先设置信号为默认状态。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值