最近在项目中遇到一个需求,前端发来一个命令,这个命令是去执行传递过来的一个脚本(shell 或者python),并返回脚本的标准输出和标准出错,如果执行超过设定时间还没结束就超时,然后终止脚本的执行。实现这个功能,自然而然先想到的是subprocess这个库了。
因此,在后端的一个脚本中调用python的subprocess去执行传递过来的脚本,通常情况下subprocess都能运行的很好,完成脚本的执行并返回。最初的实现代码如下:
run_cmd.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import subprocess
from threading import Timer
import os
class test(object):
def __init__(self):
self.stdout = []
self.stderr = []
self.timeout = 10
self.is_timeout = False
pass
def timeout_callback(self, p):
print 'exe time out call back'
print p.pid
try:
p.kill()
except Exception as error:
print error
def run(self):
cmd = ['bash', '/home/XXXX/test.sh']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
my_timer = Timer(self.timeout, self.timeout_callback, [p])
my_timer.start()
try:
print "start to count timeout; timeout set to be %d \n" % (self.timeout,)
stdout, stderr = p.communicate()
exit_code = p.returncode
print exit_code
print type(stdout), type(stderr)
print stdout
print stderr
finally:
my_timer.cancel()
但是偶然间测试一个shell脚本,这个shell脚本中有一行ping www.baidu.com &,shell脚本如下:
test.sh
#!/bin/bash
ping www.baidu.com (&) #加不加&都没区别
echo $$
python(父进程)用subprocess.Popen新建一个进程(子进程)去开启一个shell, shell新开一个子进程(孙进程)去执行ping www.baidu.com的命令。由于孙进程ping www.baidu.com一直在执行,就类似于一个daemon程序,一直在运行。在超时时间后,父进程杀掉了shell子进程,但是父进程阻塞在了p.communicate函数了,是阻塞在了调用wait()函数之前,感兴趣的朋友可以看一下源码_communicate函数,linux系统重点看_communicate_with_poll和_communicate_with_select函数,你会发现是阻塞在了while循环里