即使Popen仍在运行,python子进程poll()也没有返回None

时间:2012-10-19 19:15:13

标签: python timeout subprocess pipe python-2.4

我有一个python脚本,使用while循环执行带有超时的linux命令,并像下面一样睡眠

fout = tempfile.TemporaryFile()
try:
    p = subprocess.Popen(["/bin/bash","-c", options.command], bufsize=-1, shell=False, preexec_fn=os.setsid, stdin=subprocess.PIPE, stdout=fout, stderr=subprocess.PIPE)
except:
    sys.exit(UNEXPECTED_ERROR)
if options.timeout:
    print "options.timeout = %s" % options.timeout
    elapsed = 0
    time.sleep(0.1) # This sleep is for the delay between Popen and poll() functions
    while p.poll() is None:
        time.sleep(1)
        elapsed = elapsed + 1
        print "elapsed = %s" % elapsed
        if elapsed >= options.timeout:
            # TIMEDOUT
            # kill all processes that are in the same child process group
            # which kills the process tree
            pgid = os.getpgid(p.pid)    
            os.killpg(pgid, signal.SIGKILL)
            p.wait()
            fout.close()
            sys.exit(TIMEOUT_ERROR)
            break
else:
    p.wait()

fout.seek(0) #rewind to the beginning of the file
print fout.read(),
fout.close()
sys.exit(p.returncode)

$ time myScript -c "cat file2" 2>&1 -t 5
options.timeout = 5
elapsed = 1

real    0m11.811s
user    0m0.046s
sys     0m1.153s

我的问题在于上述情况,即使超时是5秒,猫继续直到它完成。我在这里错过了什么吗?请帮忙。

2 个答案:

答案 0 :(得分:1)

它在Ubuntu上按预期工作:

$ /usr/bin/ssh root@localhost -t 'sync && echo 3 > /proc/sys/vm/drop_caches'
$ /usr/bin/time python2.4 myscript.py 'cat big_file'
timeout
done
0.01user 0.63system 0:05.16elapsed 12%CPU 

$ /usr/bin/ssh root@localhost -t 'sync && echo 3 > /proc/sys/vm/drop_caches'
$ /usr/bin/time cat big_file >/dev/null
0.02user 0.82system 0:09.93elapsed 8%CPU

它也适用于shell命令:

$ /usr/bin/time python2.4 myscript.py 'while : ; do sleep 1; done'
timeout
done
0.02user 0.00system 0:05.03elapsed 0%CPU

假设:

  • 由于系统时钟更改的可能性,您无法使用time.time()

  • time.clock()无法衡量Linux上的儿童时间

  • 我们无法在纯Python中使用Python 3.3模拟time.monotonic() 由于ctypes在Python 2.4上不可用

  • 可以接受休眠,例如,休眠前2秒+计算机唤醒后3秒,如果超时为5秒则发生。

#!/usr/bin/env python2.4
import os
import signal
import sys
import tempfile
import time
from subprocess import Popen

class TimeoutExpired(Exception):
    pass

def wait(process, timeout, _sleep_time=.1):
    for _ in xrange(int(timeout * 1. / _sleep_time + .5)):
        time.sleep(_sleep_time)  # NOTE: assume it doesn't wake up earlier
        if process.poll() is not None:
            return process.wait()
    raise TimeoutExpired  # NOTE: timeout precision is not very good

f = tempfile.TemporaryFile() 
p = Popen(["/bin/bash", "-c", sys.argv[1]], stdout=f, preexec_fn=os.setsid,
          close_fds=True)
try:
    wait(p, timeout=5)
except TimeoutExpired:
    print >>sys.stderr, "timeout"
    os.killpg(os.getpgid(p.pid), signal.SIGKILL)
    p.wait()
else:
    f.seek(0)
    for line in f:
        print line,
f.close()  # delete it
print >>sys.stderr, "done"

答案 1 :(得分:0)

除了我在你的代码中看到的问题

  • 您使用Popen()stdin=subprocess.PIPE致电stderr=subprocess.PIPE。但你永远不会处理这些管道。使用类似cat file2的命令,这应该没问题,但可能会导致问题。

我可以发现一个潜在的不当行为:你可能会混淆缩进(如问题的第一个版本)。假设您有以下内容:

while p.poll() is None:
    time.sleep(1)
    elapsed = elapsed + 1
    print "elapsed = %s" % elapsed
    if elapsed >= options.timeout:
        # TIMEDOUT
        # kill all processes that are in the same child process group
        # which kills the process tree
        pgid = os.getpgid(p.pid)    
        os.killpg(pgid, signal.SIGKILL)
    p.wait()
    fout.close()
    sys.exit(TIMEOUT_ERROR)
    break

您没有达到超时阈值,但由于缩进不良而调用p.wait()。不要混淆标签和空格; PEP 8建议仅使用空格,并且压痕深度为4列。