简体   繁体   English

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

[英]python subprocess poll() is not returning None even if Popen is still running

I have a python script that executes linux commands with timeout using a while loop and sleep like below 我有一个使用while循环和睡眠的python脚本,它执行超时的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

My question is in that above case even if the timeout is 5 seconds cat continues till it finishes. 我的问题是在上述情况下,即使超时为5秒,猫仍会继续操作直至完成。 Am I missing something here? 我在这里想念什么吗? Please help. 请帮忙。

It works as expected on Ubuntu: 它可以在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

It also work with a shell command: 它也可以使用shell命令:

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

Assumptions: 假设:

  • you can't use time.time() due to possibility of a system clock change 您可能无法使用time.time()因为可能会更改系统时钟

  • time.clock() doesn't measure children times on Linux time.clock()在Linux上不测量子时间

  • we can't emulate time.monotonic() from Python 3.3 in pure Python due to ctypes is not available on Python 2.4 由于ctypes无法在纯Python中从Python 3.3模拟time.monotonic() ,因此在Python 2.4上不可用

  • it is acceptable to survive hibernation eg, 2 seconds before hibernation + 3 seconds after computer wakes up whenever it happens if timeout is 5 seconds. 如果超时为5秒,则无论何时发生休眠,休眠后2秒+计算机唤醒后3秒都是可以接受的。

#!/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"

Beside of the problems I see in your code 除了我在您的代码中看到的问题之外

  • you call Popen() with stdin=subprocess.PIPE and stderr=subprocess.PIPE . 您可以使用stdin=subprocess.PIPEstderr=subprocess.PIPE Popen()调用Popen() But you never handle these pipes. 但是您永远不会处理这些管道。 With a command like cat file2 , this should be fine, but it can lead to problems. 使用cat file2类的命令,应该可以,但是会导致问题。

I can spot a potential misbehaviour: you might have mixed up indentation (as in the 1st version of your question). 我可以发现一个潜在的错误行为:您可能已经将缩进混淆了(如问题的第1版)。 Assume you have the following: 假设您具有以下条件:

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

You don't reach the timeout threshold, and nevertheless p.wait() is called due to a bad indentation. 您没有达到超时阈值,但是由于缩进错误而调用了p.wait() Don't mix up tabs and spaces; 不要混淆制表符和空格; PEP 8 suggests to use spaces only and a indentation depth of 4 columns. PEP 8建议仅使用空格,压痕深度为4列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM