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

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, 那么在顶层的代码就会运行, 导致各种不可知的事件发生.

本条技术文章来源于互联网,如果无意侵犯您的权益请点击此处反馈版权投诉

本文系统来源:php中文网

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值