Tôi muốn thực hiện một quá trình, giới hạn thời gian thực hiện bởi một số timeout trong vài giây và lấy đầu ra được sản xuất bởi quá trình này. Và tôi muốn làm điều này trên windows, linux và freebsd.python subprocess với timeout và đầu ra lớn (> 64K)
Tôi đã cố gắng thực hiện điều này theo ba cách khác nhau:
cmd - Nếu không có thời gian chờ và subprocess.PIPE cho chụp đầu ra.
HÀNH VI: Hoạt động như mong đợi nhưng không hỗ trợ thời gian chờ, tôi cần thời gian chờ ...
cmd_to - Với thời gian chờ và subprocess.PIPE cho chụp đầu ra.
BEHAVIOR: Chặn thực thi quy trình con khi đầu ra> = 2^16 byte.
cmd_totf - Với thời gian chờ và tempfile.NamedTemporaryfile để chụp đầu ra.
BEHAVIOR: Hoạt động như mong đợi nhưng sử dụng tệp tạm thời trên đĩa.
Chúng có sẵn bên dưới để kiểm tra kỹ hơn.
Như có thể thấy ở đầu ra bên dưới, sau đó mã hết thời gian thực thi quy trình phụ khi sử dụng xử lý con.PIPE và đầu ra từ tiến trình con là> = 2^16 byte.
Tài liệu quy trình con nói rằng điều này được mong đợi khi gọi process.wait() và sử dụng subprocessing.PIPE, tuy nhiên không có cảnh báo nào được đưa ra khi sử dụng process.poll(), vậy có gì sai ở đây?
Tôi có một giải pháp trong cmd_totf sử dụng mô-đun tempfile nhưng sự cân bằng là nó ghi đầu ra vào đĩa, cái gì tôi thực sự muốn tránh.
Vì vậy, câu hỏi của tôi là:
- Tôi đang làm gì sai trong cmd_to?
- Có cách nào để làm những gì tôi muốn và không sử dụng tempfiles/giữ đầu ra trong bộ nhớ.
Script để tạo ra một loạt các đầu ra ('exp_gen.py'):
#!/usr/bin/env python
import sys
output = "b"*int(sys.argv[1])
print output
Ba triển khai khác nhau (cmd, cmd_to, cmd_totf) của hàm bao quanh subprocessing.Popen:
#!/usr/bin/env python
import subprocess, time, tempfile
bufsize = -1
def cmd(cmdline, timeout=60):
"""
Execute cmdline.
Uses subprocessing and subprocess.PIPE.
"""
p = subprocess.Popen(
cmdline,
bufsize = bufsize,
shell = False,
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE
)
out, err = p.communicate()
returncode = p.returncode
return (returncode, err, out)
def cmd_to(cmdline, timeout=60):
"""
Execute cmdline, limit execution time to 'timeout' seconds.
Uses subprocessing and subprocess.PIPE.
"""
p = subprocess.Popen(
cmdline,
bufsize = bufsize,
shell = False,
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE
)
t_begin = time.time() # Monitor execution time
seconds_passed = 0
while p.poll() is None and seconds_passed < timeout:
seconds_passed = time.time() - t_begin
time.sleep(0.1)
#if seconds_passed > timeout:
#
# try:
# p.stdout.close() # If they are not closed the fds will hang around until
# p.stderr.close() # os.fdlimit is exceeded and cause a nasty exception
# p.terminate() # Important to close the fds prior to terminating the process!
# # NOTE: Are there any other "non-freed" resources?
# except:
# pass
#
# raise TimeoutInterrupt
out, err = p.communicate()
returncode = p.returncode
return (returncode, err, out)
def cmd_totf(cmdline, timeout=60):
"""
Execute cmdline, limit execution time to 'timeout' seconds.
Uses subprocessing and tempfile instead of subprocessing.PIPE.
"""
output = tempfile.NamedTemporaryFile(delete=False)
error = tempfile.NamedTemporaryFile(delete=False)
p = subprocess.Popen(
cmdline,
bufsize = 0,
shell = False,
stdin = None,
stdout = output,
stderr = error
)
t_begin = time.time() # Monitor execution time
seconds_passed = 0
while p.poll() is None and seconds_passed < timeout:
seconds_passed = time.time() - t_begin
time.sleep(0.1)
#if seconds_passed > timeout:
#
# try:
# p.stdout.close() # If they are not closed the fds will hang around until
# p.stderr.close() # os.fdlimit is exceeded and cause a nasty exception
# p.terminate() # Important to close the fds prior to terminating the process!
# # NOTE: Are there any other "non-freed" resources?
# except:
# pass
#
# raise TimeoutInterrupt
p.wait()
returncode = p.returncode
fd = open(output.name)
out = fd.read()
fd.close()
fd = open(error.name)
err = fd.read()
fd.close()
error.close()
output.close()
return (returncode, err, out)
if __name__ == "__main__":
implementations = [cmd, cmd_to, cmd_totf]
bytes = ['65535', '65536', str(1024*1024)]
timeouts = [5]
for timeout in timeouts:
for size in bytes:
for i in implementations:
t_begin = time.time()
seconds_passed = 0
rc, err, output = i(['exp_gen.py', size], timeout)
seconds_passed = time.time() - t_begin
filler = ' '*(8-len(i.func_name))
print "[%s%s: timeout=%d, iosize=%s, seconds=%f]" % (repr(i.func_name), filler, timeout, size, seconds_passed)
Kết quả thực hiện:
['cmd' : timeout=5, iosize=65535, seconds=0.016447]
['cmd_to' : timeout=5, iosize=65535, seconds=0.103022]
['cmd_totf': timeout=5, iosize=65535, seconds=0.107176]
['cmd' : timeout=5, iosize=65536, seconds=0.028105]
['cmd_to' : timeout=5, iosize=65536, seconds=5.116658]
['cmd_totf': timeout=5, iosize=65536, seconds=0.104905]
['cmd' : timeout=5, iosize=1048576, seconds=0.025964]
['cmd_to' : timeout=5, iosize=1048576, seconds=5.128062]
['cmd_totf': timeout=5, iosize=1048576, seconds=0.103183]
Thử câu trả lời từ http://stackoverflow.com/questions/874815/how-do-i-get-real-time-information-back-from-a-subprocess-popen-in-python-2-5 . –
Bạn nên đề cập đến phiên bản của python. Vì AFAIK, có khá ít thay đổi từ 2,6 đến 2,7 liên quan đến mô-đun 'subprocess' –
Xem thêm http://stackoverflow.com/questions/1191374/subprocess-with-timeout/8507775#8507775 – bortzmeyer