深入分析在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 相关文章推荐
解决python2.7用pip安装包时出现错误的问题
Jan 23 Python
python 统计代码行数简单实例
May 04 Python
Python yield 使用方法浅析
May 20 Python
python3.4用函数操作mysql5.7数据库
Jun 23 Python
Python中的is和==比较两个对象的两种方法
Sep 06 Python
python获取命令行输入参数列表的实例代码
Jun 23 Python
详解python单元测试框架unittest
Jul 02 Python
​如何愉快地迁移到 Python 3
Apr 28 Python
python时间序列按频率生成日期的方法
May 14 Python
python函数的万能参数传参详解
Jul 26 Python
python可迭代对象去重实例
May 15 Python
class类在python中获取金融数据的实例方法
Dec 10 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
2020最新CPU的性能排名
2020/04/02 数码科技
第七节 类的静态成员 [7]
2006/10/09 PHP
PHP四舍五入精确小数位及取整
2014/01/14 PHP
PHP命名空间namespace的定义方法详解
2017/03/29 PHP
PHP进阶学习之命名空间基本用法分析
2019/06/18 PHP
javaScript 数值型和字符串型之间的转换
2009/07/25 Javascript
JavaScript prototype属性深入介绍
2012/11/27 Javascript
JQuery表格内容过滤的实现方法
2013/07/05 Javascript
jquery查找tr td 示例模拟
2014/05/08 Javascript
JS版的date函数(和PHP的date函数一样)
2014/05/12 Javascript
js数组去重的5种算法实现
2015/11/04 Javascript
Bootstrap每天必学之标签页(Tab)插件
2020/08/09 Javascript
JQuery 传送中文乱码问题的简单解决办法
2016/05/24 Javascript
js注入 黑客之路必备!
2016/09/14 Javascript
微信小程序表单验证错误提示效果
2017/05/19 Javascript
vue-cli构建项目使用 less的方法
2017/10/04 Javascript
vue页面离开后执行函数的实例
2018/03/13 Javascript
微信小程序实现默认第一个选中变色效果
2018/07/17 Javascript
使用ECharts实现状态区间图
2018/10/25 Javascript
Python两个内置函数 locals 和globals(学习笔记)
2016/08/28 Python
Python2实现的LED大数字显示效果示例
2017/09/04 Python
pyqt5的QWebEngineView 使用模板的方法
2018/08/18 Python
详解如何设置Python环境变量?
2019/05/13 Python
python selenium爬取斗鱼所有直播房间信息过程详解
2019/08/09 Python
Python:合并两个numpy矩阵的实现
2019/12/02 Python
Pytorch之parameters的使用
2019/12/31 Python
Python通过两个dataframe用for循环求笛卡尔积
2020/04/29 Python
Django获取model中的字段名和字段的verbose_name方式
2020/05/19 Python
英国最受欢迎的价格比较网站之一:MoneySuperMarket
2018/12/19 全球购物
德国领先的大尺码和超大尺码男装在线零售商:Bigtex
2019/06/22 全球购物
Aosom西班牙:家具在线商店
2020/06/11 全球购物
《问银河》教学反思
2014/02/19 职场文书
市场营销大学生职业规划书
2014/02/25 职场文书
外贸采购员岗位职责
2014/03/08 职场文书
购房委托书范本
2014/09/18 职场文书
OpenCV-Python 实现两张图片自动拼接成全景图
2021/06/11 Python