自带的 print 函数居然会报错?(上)

简介: 最近用 Python 写了几个简单的脚本来处理一些数据,因为只是简单功能所以我就直接使用 print 来打印日志。

前言


最近用 Python 写了几个简单的脚本来处理一些数据,因为只是简单功能所以我就直接使用 print 来打印日志。


任务运行时偶尔会出现一些异常:


网络异常,图片无法展示
|


因为我在不同地方都有打印日志,导致每次报错的地方都不太一样,从而导致程序运行结果非常诡异;有时候是这段代码没有运行,下一次就可能是另外一段代码没有触发。


虽说当时有注意到 Broken pipe 这个关键异常,但没有特别在意,因为代码中也有一些发送 http 请求的地方,一直以为是网络 IO 出现了问题,压根没往 print 这个最基本的打印函数上思考🤔。


直到这个问题反复出现我才认真看了这个异常,定睛一看 print 不也是 IO 操作嘛,难道真的是自带的  print 函数都出问题了?


但在本地、测试环境我运行无数次也没能发现异常;于是我找运维拿到了线上的运行方式。


原来为了方便维护大家提交上来的脚本任务,运维自己有维护一个统一的脚本,在这个脚本中使用:


cmd = 'python /xxx/test.py'
os.popen(cmd)


来触发任务,这也是与我在本地、开发环境的唯一区别。


popen 原理


为此我在开发环境模拟出了异常:


test.py:


import time
if __name__ == '__main__':
    time.sleep(20)
    print '1000'*1024


task.py:


import os
import time
if __name__ == '__main__':
    start = int(time.time())
    cmd = 'python test.py'
    os.popen(cmd)
    end = int(time.time())
    print 'end****{}s'.format(end-start)


运行:


python task.py


等待 20s 必然会复现这个异常:


Traceback (most recent call last):
  File "test.py", line 4, in <module>
    print '1000'*1024
IOError: [Errno 32] Broken pipe


为什么会出现这个异常呢?


首先得了解 os.popen(command[, mode[, bufsize]]) 这个函数的运行原理。


网络异常,图片无法展示
|


根据官方文档的解释,该函数会执行 fork 一个子进程执行 command 这个命令,同时将子进程的标准输出通过管道连接到父进程;


也就该方法返回的文件描述符。


这里画个图能更好地理解其中的原理:


网络异常,图片无法展示
|

在这里的使用场景中并没有获取 popen() 的返回值,所以 command 的执行本质上是异步的;


也就是说当 task.py 执行完毕后会自动关闭读取端的管道。


网络异常,图片无法展示
|
如图所示,关闭之后子进程会向 pipe 中输出   print '1000'*1024,由于这里输出的内容较多会一下子填满管道的缓冲区;


于是写入端会收到 SIGPIPE 信号,从而导致 Broken pipe 的异常。


从维基百科中我们也可以看出这个异常产生的一些条件:


网络异常,图片无法展示
|


其中也提到了 SIGPIPE 信号。


相关文章
|
6月前
|
Java 测试技术 Python
为什么要用函数
在编程中,函数是一种重要的抽象工具,它使我们能够组织和复用代码,提高代码的可读性、可维护性和效率。函数允许我们将一段代码块封装起来,给它一个名字,并通过参数和返回值来与外部世界交互。下面,我们将深入探讨为什么要使用函数,并附上相应的代码示例。
72 1
|
6月前
|
存储 编译器 Serverless
C++系列十:函数
C++系列十:函数
|
11月前
|
算法 编译器
函数(2)
函数(2)
39 0
基本初等函数 对数函数
基本初等函数 对数函数
130 0
|
编译器 C语言 C++
C++——函数
C++——函数
118 0
C++——函数
|
Linux
opendir函数
opendir函数
163 0
fflush函数
fflush函数
174 0
|
SQL 关系型数据库 安全