处理方案:
那死锁问题如何避免呢?官方文档里推荐使用 Popen.communicate()。这个方法会把输出放在内存,而不是管道里,所以这时候上限就和内存大小有关了,一般不会有问题。而且如果要获得程序返回值,可以在调用 Popen.communicate() 之后取 Popen.returncode 的值。
3)死锁形式3
call、check_call、popen、check_output 这四个函数,参数shell=True,命令参数不能为list,若为list则引发死锁
处理方案:
参数shell=True时,命令参数为字符串形式
0×02. 关闭subprocess.Popen 子进程时存在子进程关闭失败而成为僵尸进程的风险
Python 标准库 subprocess.Popen 是 shellout 一个外部进程的首选,它在 Linux/Unix 平台下的实现方式是 fork 产生子进程然后 exec 载入外部可执行程序。
于是问题就来了,如果我们需要一个类似“夹具”的子进程(比如运行 Web 集成测试的时候跑起来的那个被测试 Server), 那么就需要在退出上下文的时候清理现场,也就是结束被跑起来的子进程。
最简单粗暴的做法可以是这样:
@contextlib.contextmanager
def process_fixture(shell_args):
proc = subprocess.Popen(shell_args)
try:
yield
finally:
# 无论是否发生异常,现场都是需要清理的
proc.terminate()
proc.wait()
if __name__ == '__main__':
with process_fixture(['python', 'SimpleHTTPServer', '8080']) as proc:
print('pid %d' % proc.pid)
print(urllib.urlopen('http://localhost:8080').read())
那个 proc.wait() 是不可以偷懒省掉的,否则如果子进程被中止了而父进程继续运行, 子进程就会一直占用 pid 而成为僵尸,直到父进程也中止了才被托孤给 init 清理掉。
这个简单粗暴版对简单的情况可能有效,但是被运行的程序可能没那么听话。被运行程序可能会再fork 一些子进程来工作,自己则只当监工 —— 这是不少 Web Server 的做法。 对这种被运行程序如果简单地 terminate ,也即对其 pid 发 SIGTERM , 那就相当于谋杀了监工进程,真正的工作进程也就因此被托孤给 init ,变成畸形的守护进程…… 嗯没错,这就是我一开始遇到的问题,CI Server上明明已经中止了 Web S