深入分析在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 相关文章推荐
使用IPython来操作Docker容器的入门指引
Apr 08 Python
Python实现的RSS阅读器实例
Jul 25 Python
浅析Python基础-流程控制
Mar 18 Python
Python实现Sqlite将字段当做索引进行查询的方法
Jul 21 Python
python 根据正则表达式提取指定的内容实例详解
Dec 04 Python
TensorFlow实现AutoEncoder自编码器
Mar 09 Python
django连接mysql配置方法总结(推荐)
Aug 18 Python
pandas 转换成行列表进行读取与Nan处理的方法
Oct 30 Python
Python面向对象基础入门之编码细节与注意事项
Dec 11 Python
Django model select的多种用法详解
Jul 16 Python
python tornado使用流生成图片的例子
Nov 18 Python
Django后端分离 使用element-ui文件上传方式
Jul 12 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
法压式咖啡之制作法
2021/03/03 冲泡冲煮
php和javascript之间变量的传递实现代码
2012/12/19 PHP
php统计时间和内存使用情况示例分享
2014/03/13 PHP
常见php数据文件缓存类汇总
2014/12/05 PHP
PHP实现批量重命名某个文件夹下所有文件的方法
2017/09/04 PHP
PHP的PDO预处理语句与存储过程
2019/01/27 PHP
Apache+PHP+MySQL搭建PHP开发环境图文教程
2020/08/06 PHP
显示、隐藏密码
2006/07/01 Javascript
js监听表单value的修改同步问题,跨浏览器支持
2009/12/31 Javascript
关于js注册事件的常用方法
2013/04/03 Javascript
js获取当前路径的简单示例代码
2014/01/08 Javascript
Bootstrap轮播插件使用代码
2016/10/11 Javascript
微信小程序 教程之模板
2016/10/18 Javascript
JS获得一个对象的所有属性和方法实例
2017/02/21 Javascript
JavaScript运动框架 解决防抖动问题、悬浮对联(二)
2017/05/17 Javascript
iframe与主框架跨域相互访问实现方法
2017/09/14 Javascript
Jquery的autocomplete插件用法及参数讲解
2019/03/12 jQuery
基于vue实现简易打地鼠游戏
2020/08/21 Javascript
详解python string类型 bytes类型 bytearray类型
2017/12/16 Python
运行django项目指定IP和端口的方法
2018/05/14 Python
Python中Unittest框架的具体使用
2019/08/27 Python
Django 对IP访问频率进行限制的例子
2019/08/30 Python
python银行系统实现源码
2019/10/25 Python
python实现图像高斯金字塔的示例代码
2020/12/11 Python
英国领先的运动物理治疗供应公司:Vivomed
2018/07/14 全球购物
预订旅游活动、景点和旅游:GetYourGuide
2019/09/29 全球购物
保研推荐信
2014/05/09 职场文书
解除财产保全担保书
2014/05/20 职场文书
农民工预备党员思想汇报
2014/09/14 职场文书
2014年社区国庆节活动方案
2014/09/16 职场文书
支部书记四风问题对照检查材料
2014/10/04 职场文书
北京故宫导游词
2015/01/31 职场文书
个人优缺点总结
2015/02/28 职场文书
mysql数据插入覆盖和时间戳的问题及解决
2022/03/25 MySQL
MySQL查询日期时间
2022/05/15 MySQL
ant design charts 获取后端接口数据展示
2022/05/25 Javascript