深入分析在Python模块顶层运行的代码引起的一个Bug


Posted in Python onJuly 04, 2014

然后我们在Interactive Python prompt中测试了一下:

>>> import subprocess
  >>> subprocess.check_call("false")
  0

而在其他机器运行相同的代码时, 却正确的抛出了错误:

>>> subprocess.check_call("false")
  Traceback (most recent call last):
   File "", line 1, in 
   File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 542, in check_call
    raise CalledProcessError(retcode, cmd)
  subprocess.CalledProcessError: Command 'false' returned non-zero exit status 1

看来是subprecess误以为子进程成功的退出了导致的原因.

深入分析

第一眼看上去, 这一问题应该是Python自身或操作系统引起的. 这到底是怎么发生的? 于是我的同事查看了subprocess的wait()方法:

def wait(self):
  """Wait for child process to terminate. Returns returncode attribute."""
  while self.returncode is None:
   try:
    pid, sts = _eintr_retry_call(os.waitpid, self.pid, 0)
   except OSError as e:
    if e.errno != errno.ECHILD:
     raise
    # This happens if SIGCLD is set to be ignored or waiting
    # for child processes has otherwise been disabled for our
    # process. This child is dead, we can't get the status.
    pid = self.pid
    sts = 0
   # Check the pid and loop as waitpid has been known to return
   # 0 even without WNOHANG in odd situations. issue14396.
   if pid == self.pid:
    self._handle_exitstatus(sts)
  return self.returncode

可见, 如果os.waitpid的ECHILD检测失败, 那么错误就不会被抛出. 通常, 当一个进程结束后, 系统会继续记录其信息, 直到母进程调用wait()方法. 在此期间, 这一进程就叫"zombie". 如果子进程不存在, 那么我们就无法得知其是否成功还是失败了.

以上代码还能解决另外一个问题: Python默认认为子进程成功退出. 大多数情况下, 这一假设是没问题的. 但当一个进程明确表明忽略子进程的SIGCHLD时, waitpid()将永远是成功的.

回到原来的代码中

我们是不是在我们的程序中明确设置忽略SIGCHLD? 不太可能, 因为我们使用了大量的子进程, 但只有极少数情况下才出现同样的问题. 再使用git grep后, 我们发现只有在一段独立代码中, 我们忽略了SIGCHLD. 但这一代吗根本就不是程序的一部分, 只是引用了一下.

一星期后

一星期后, 这一错误又再一次发生. 并且通过简单的调试, 在debugger中重现了该错误.

经过一些测试, 我们确定了正是由于程序忽略了SIGCHLD才引起的这一bug. 但这是怎么发生的呢?

我们查看了那段独立代码, 其中有一段:

signal.signal(signal.SIGCHLD, signal.SIG_IGN)
我们是不是无意间import了这段代码到程序中? 结果显示我们的猜测是正确的. 当import了这段代码后, 由于以上语句是在这一module的顶层, 而不是在一个function中, 导致了它的运行, 忽略了SIGCHLD, 从而导致了子进程错误没有被抛出!

总结

这一bug的发生, 给了我们两个教训. 第一是, 在debug检查时, 应该从新的代码到老的代码, 再到Python Library. 因为新代码发生错误的几率大于老代码, 而python library中发生错误的几率更小.

第二是, 不要将可能会引起副作用的代码写在module顶层, 而应当写到functuon中. 因为如果该module被import, 那么在顶层的代码就会运行, 导致各种不可知的事件发生.

Python 相关文章推荐
python共享引用(多个变量引用)示例代码
Dec 04 Python
爬山算法简介和Python实现实例
Apr 26 Python
简单介绍Ruby中的CGI编程
Apr 10 Python
python中的编码知识整理汇总
Jan 26 Python
python 调用c语言函数的方法
Sep 29 Python
用Python下载一个网页保存为本地的HTML文件实例
May 21 Python
对python插入数据库和生成插入sql的示例讲解
Nov 14 Python
Python使用matplotlib实现交换式图形显示功能示例
Sep 06 Python
详解基于python-django框架的支付宝支付案例
Sep 23 Python
详解mac python+selenium+Chrome 简单案例
Nov 08 Python
numpy中生成随机数的几种常用函数(小结)
Aug 18 Python
python实现商品进销存管理系统
May 30 Python
python之import机制详解
Jul 03 #Python
Python之eval()函数危险性浅析
Jul 03 #Python
python的绘图工具matplotlib使用实例
Jul 03 #Python
python绘图库Matplotlib的安装
Jul 03 #Python
Python实现全局变量的两个解决方法
Jul 03 #Python
Python实现端口复用实例代码
Jul 03 #Python
在 Django/Flask 开发服务器上使用 HTTPS
Jul 03 #Python
You might like
PHP系统命令函数使用分析
2013/07/05 PHP
100多行PHP代码实现socks5代理服务器[2]
2016/05/05 PHP
详解EventDispatcher事件分发组件
2016/12/25 PHP
jscript之Read an Excel Spreadsheet
2007/06/13 Javascript
JS array 数组详解
2009/03/22 Javascript
载入jQuery库的最佳方法详细说明及实现代码
2012/12/28 Javascript
浅析AngularJs HTTP响应拦截器
2015/12/28 Javascript
详解Angular 自定义结构指令
2017/06/21 Javascript
总结javascript三元运算符知识点
2018/09/28 Javascript
JS集合set类的实现与使用方法示例
2019/02/01 Javascript
JavaScript中变量提升机制示例详解
2019/12/27 Javascript
js实现数据导出为EXCEL(支持大量数据导出)
2020/03/31 Javascript
基于Vue中的父子传值问题解决
2020/07/27 Javascript
nuxt.js写项目时增加错误提示页面操作
2020/11/05 Javascript
[01:05:40]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS DT第三场
2014/05/24 DOTA
python使用urlparse分析网址中域名的方法
2015/04/15 Python
不要用强制方法杀掉python线程
2017/02/26 Python
python 列表,数组和矩阵sum的用法及区别介绍
2018/06/28 Python
使用Python AIML搭建聊天机器人的方法示例
2018/07/09 Python
PyTorch之图像和Tensor填充的实例
2019/08/18 Python
使用Puppeteer爬取微信文章的实现
2020/02/11 Python
15行Python代码实现免费发送手机短信推送消息功能
2020/02/27 Python
记一次pyinstaller打包pygame项目为exe的过程(带图片)
2020/03/02 Python
python GUI编程(Tkinter) 创建子窗口及在窗口上用图片绘图实例
2020/03/04 Python
Python过滤序列元素的方法
2020/07/31 Python
用python获取txt文件中关键字的数量
2020/12/24 Python
HTML5 对各个标签的定义与规定:body的介绍
2012/06/21 HTML / CSS
宿舍违规用电检讨书
2014/02/16 职场文书
槐乡的孩子教学反思
2014/04/27 职场文书
工作保证书
2015/01/17 职场文书
全国法制宣传日活动总结
2015/05/05 职场文书
《百分数的认识》教学反思
2016/02/19 职场文书
创业计划书之熟食店
2019/10/16 职场文书
Python 读写 Matlab Mat 格式数据的操作
2021/05/19 Python
MySQL面试题讲解之如何设置Hash索引
2021/11/01 MySQL
win10重装系统后上不了网怎么办 win10重装系统网络故障的解决办法
2022/07/23 数码科技