子进程在使用多处理时不考虑参数
Subprocess doesn't respect arguments when using multiprocessing
这里的主要 objective 是创建守护进程生成函数。守护进程需要 运行 任意程序(即使用 subprocess
)。
到目前为止,我的 daemonizer.py
模块中的内容是:
import os
from multiprocessing import Process
from time import sleep
from subprocess import call, STDOUT
def _daemon_process(path_to_exec, std_out_path, args, shell):
with open(std_out_path, 'w') as fh:
args = (str(a) for a in args)
if shell:
fh.write("*** LAUNCHING IN SHELL: {0} ***\n\n".format(" ".join([path_to_exec] + list(args))))
retcode = call(" ".join([path_to_exec] + list(args)), stderr=STDOUT, stdout=fh, shell=True)
else:
fh.write("*** LAUNCHING WITHOUT SHELL: {0} ***\n\n".format([path_to_exec] + list(args)))
retcode = call([path_to_exec] + list(args), stderr=STDOUT, stdout=fh, shell=False)
if retcode:
fh.write("\n*** DAEMON EXITED WITH CODE {0} ***\n".format(retcode))
else:
fh.write("\n*** DAEMON DONE ***\n")
def daemon(path_to_executable, std_out=os.devnull, daemon_args=tuple(), shell=True):
d = Process(name='daemon', target=_daemon_process, args=(path_to_executable, std_out, daemon_args, shell))
d.daemon = True
d.start()
sleep(1)
当在 bash 中尝试 运行 时(这将在您的当前目录中创建一个名为 test.log
的文件。):
python -c"import daemonizer;daemonizer.daemon('ping', std_out='test.log', daemon_args=('-c', '5', '192.168.1.1'), shell=True)"
它正确地生成了一个启动 ping
的守护进程,但它不遵守传递的参数。如果 shell 也设置为 False
,也是如此。日志文件清楚地指出它试图用传递的参数启动它。
作为创建以下可执行文件的概念证明:
echo "ping -c 5 192.168.1.1" > ping_test
chmod +x ping_test
以下按预期工作:
python -c"import daemonizer;daemonizer.daemon('./ping_test', std_out='test.log', shell=True)"
如果我在 multiprocessing.Process
-target 之外测试相同的 call
代码,它会按预期工作。
那么我该如何解决这个问题,以便我可以生成带参数的进程?
我对完全不同的结构和模块持开放态度,但它们应该包含在标准结构和模块中并与 python 2.7.x 兼容。要求是 daemon
函数应该可以在脚本中异步调用多次,并且每次都生成一个守护进程,并且它们的目标进程应该能够在不同的 CPU 上结束。当然,脚本也需要能够在不影响生成的守护进程的情况下结束。
作为奖励,我注意到我需要有一个 sleep
才能使生成工作,否则脚本终止得太快。有什么方法可以绕过这种任意攻击 and/or 我真的需要等多久才能安全?
你的论点被印刷出来"used up"!
首先,你这样做:
args = (str(a) for a in args)
创建生成器,而不是列表或元组。所以当你以后这样做时:
list(args)
这样就消耗了参数,不会再看到第二次了。所以你再做一次:
list(args)
并得到一个空列表!
您可以通过注释掉您的打印语句来解决此问题,但更好的方法是首先简单地创建一个列表:
args = [str(a) for a in args]
那你可以直接用args
,不用list(args)
。并且它始终包含参数。
这里的主要 objective 是创建守护进程生成函数。守护进程需要 运行 任意程序(即使用 subprocess
)。
到目前为止,我的 daemonizer.py
模块中的内容是:
import os
from multiprocessing import Process
from time import sleep
from subprocess import call, STDOUT
def _daemon_process(path_to_exec, std_out_path, args, shell):
with open(std_out_path, 'w') as fh:
args = (str(a) for a in args)
if shell:
fh.write("*** LAUNCHING IN SHELL: {0} ***\n\n".format(" ".join([path_to_exec] + list(args))))
retcode = call(" ".join([path_to_exec] + list(args)), stderr=STDOUT, stdout=fh, shell=True)
else:
fh.write("*** LAUNCHING WITHOUT SHELL: {0} ***\n\n".format([path_to_exec] + list(args)))
retcode = call([path_to_exec] + list(args), stderr=STDOUT, stdout=fh, shell=False)
if retcode:
fh.write("\n*** DAEMON EXITED WITH CODE {0} ***\n".format(retcode))
else:
fh.write("\n*** DAEMON DONE ***\n")
def daemon(path_to_executable, std_out=os.devnull, daemon_args=tuple(), shell=True):
d = Process(name='daemon', target=_daemon_process, args=(path_to_executable, std_out, daemon_args, shell))
d.daemon = True
d.start()
sleep(1)
当在 bash 中尝试 运行 时(这将在您的当前目录中创建一个名为 test.log
的文件。):
python -c"import daemonizer;daemonizer.daemon('ping', std_out='test.log', daemon_args=('-c', '5', '192.168.1.1'), shell=True)"
它正确地生成了一个启动 ping
的守护进程,但它不遵守传递的参数。如果 shell 也设置为 False
,也是如此。日志文件清楚地指出它试图用传递的参数启动它。
作为创建以下可执行文件的概念证明:
echo "ping -c 5 192.168.1.1" > ping_test
chmod +x ping_test
以下按预期工作:
python -c"import daemonizer;daemonizer.daemon('./ping_test', std_out='test.log', shell=True)"
如果我在 multiprocessing.Process
-target 之外测试相同的 call
代码,它会按预期工作。
那么我该如何解决这个问题,以便我可以生成带参数的进程?
我对完全不同的结构和模块持开放态度,但它们应该包含在标准结构和模块中并与 python 2.7.x 兼容。要求是 daemon
函数应该可以在脚本中异步调用多次,并且每次都生成一个守护进程,并且它们的目标进程应该能够在不同的 CPU 上结束。当然,脚本也需要能够在不影响生成的守护进程的情况下结束。
作为奖励,我注意到我需要有一个 sleep
才能使生成工作,否则脚本终止得太快。有什么方法可以绕过这种任意攻击 and/or 我真的需要等多久才能安全?
你的论点被印刷出来"used up"!
首先,你这样做:
args = (str(a) for a in args)
创建生成器,而不是列表或元组。所以当你以后这样做时:
list(args)
这样就消耗了参数,不会再看到第二次了。所以你再做一次:
list(args)
并得到一个空列表!
您可以通过注释掉您的打印语句来解决此问题,但更好的方法是首先简单地创建一个列表:
args = [str(a) for a in args]
那你可以直接用args
,不用list(args)
。并且它始终包含参数。