【问题标题】:Launch a completely independent process启动一个完全独立的进程
【发布时间】:2015-02-21 20:39:03
【问题描述】:

我想从我的 python 脚本main.py 启动一个进程。具体来说,我想运行以下命令:

`nohup python ./myfile.py &`

并且文件myfile.py 应该继续运行,即使在main.py 脚本退出之后。

我也想得到新进程的pid

我试过了:

  • os.spawnl*
  • os.exec*
  • subprocess.Popen

main.py 脚本退出时,所有人都在终止myfile.py


更新:我可以将os.startfilexdg-open 一起使用吗?这是正确的方法吗?


示例

a = subprocess.Popen([sys.executable, "nohup /usr/bin/python25 /long_process.py &"],\
     stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
print a.pid

如果我检查ps aux | grep long_process,则没有进程在运行。

long_process.py 继续打印一些文本:没有退出。

我在这里做错了吗?

【问题讨论】:

  • 您能否发布一个无法正常工作的最小示例?在微不足道的python -c 'import subprocess; subprocess.Popen(["sleep", "60"])' 之后,ps 的输出表明,sleep 在 Python 退出后继续运行良好。
  • 您正在运行带有单个参数 "nohup /usr/bin/python25 ..." 的 Python,这无法正常工作,因为 python 可执行文件将在完全名为 nohup /usr/bin/... 的文件中查找脚本,该文件不存在。并且由于您将stderr 指定为PIPE 而不读取管道的内容,因此您永远不会看到错误消息。去掉nohup&,直接运行subprocess.Popen([sys.executable, "/.../long_process.py"])。另外,除非您是认真的,否则不要将 stdinstderr 指定为管道。
  • nohup 在从 shell 启动进程时最有意义。我在您的 Popen 通话中没有看到 shell=True

标签: python python-2.7 subprocess fork child-process


【解决方案1】:

您打开长期运行的进程并为它保留一个管道。因此,您希望与它交谈。当您的启动器脚本退出时,您将无法再与它交谈。 长时间运行的进程收到SIGPIPE 并退出。

以下对我有用(Linux、Python 2.7)。

创建一个长时间运行的可执行文件:

$ echo "sleep 100" > ~/tmp/sleeper.sh

运行 Python REPL:

$ python
>>>

import subprocess
import os
p = subprocess.Popen(['/bin/sh', os.path.expanduser('~/tmp/sleeper.sh')])
# look ma, no pipes!
print p.pid
# prints 29893

退出 REPL 并查看进程仍在运行:

>>> ^D
$ ps ax | grep sleeper
29893 pts/0    S      0:00 /bin/sh .../tmp/sleeper.sh
29917 pts/0    S+     0:00 grep --color=auto sleeper

如果您想先与已启动的进程通信,然后让它继续运行,您有以下几种选择:

  • 在你的长时间运行的进程中处理SIGPIPE,不要死在上面。启动器进程退出后,在没有标准输入的情况下继续运行。
  • 使用参数、环境或临时文件传递您想要的任何内容。
  • 如果您需要双向通信,请考虑使用命名管道 (man mkfifo) 或套接字,或者编写适当的服务器。
  • 在初始双向通信阶段完成后,使长时间运行的进程分叉。

【讨论】:

  • 很好的答案,由于SIGPIPE,进程终止是一个很好的观点。 (在问题的代码中,该过程从未开始,但 OP 可能还尝试了其他因SIGPIPE 而启动并死亡的变体。)
  • 它不会创建“一个完全独立的进程”(python-daemon 包所做的)。另外,a child process won't get SIGPIPE in Python 2。尽管在简单的情况下,您的解决方案就足够了(您应该重定向到 os.devnullchild 的 stdin/stdout/stderr 以避免等待终端的输入和/或虚假输出)。
  • @user4815162342:它不会因为 Python 2 中的 SIGPIPE 而死(问题有python2.7 标签)。
  • 这里有一个例子说明为什么重定向很重要:它可能允许父进程在其子进程完成输出之前退出:Python subprocess .check_call vs .check_output
【解决方案2】:

您可以使用os.fork()

import os
pid=os.fork()
if pid==0: # new process
    os.system("nohup python ./myfile.py &")
    exit()
# parent process continues

【讨论】:

  • 请注意os.fork() does not exist for Windows
  • 在这里使用nohup 毫无意义。它确实做了两件事:(1) 禁用 HUP 传播,对于非交互式 shell,它已经关闭,因此不需要进一步禁用; (2) 重定向标准输入、标准输出和标准错误(如果它们连接到终端)(您可以自己轻松完成;</dev/null >nohup.out 2>&1 并且您只是做了与 nohup 完全相同的重定向)。正如@jfs 的回答所做的那样,完全不使用所有外壳会更有效。
【解决方案3】:

我看不到任何进程正在运行。

您看不到任何进程正在运行,因为子 python 进程立即退出。 Popen 参数不正确为 user4815162342 says in the comment

要启动一个完全独立的进程,你可以使用python-daemon package或使用systemd/supervisord/etc:

#!/usr/bin/python25
import daemon
from long_process import main

with daemon.DaemonContext():
    main()

虽然在您的情况下可能就足够了,以正确的 Popen 参数启动孩子:

with open(os.devnull, 'r+b', 0) as DEVNULL:
    p = Popen(['/usr/bin/python25', '/path/to/long_process.py'],
              stdin=DEVNULL, stdout=DEVNULL, stderr=STDOUT, close_fds=True)
time.sleep(1) # give it a second to launch
if p.poll(): # the process already finished and it has nonzero exit code
    sys.exit(p.returncode)

如果子进程不需要python2.5,那么您可以改用sys.executable(使用与父进程相同的Python版本)。

注意:代码在父进程中关闭DEVNULL而不等待子进程完成(对子进程没有影响)。

【讨论】:

    猜你喜欢
    • 2018-05-12
    • 2014-07-15
    • 2012-11-15
    • 2010-10-30
    • 2012-07-20
    • 1970-01-01
    • 1970-01-01
    • 2011-02-13
    • 1970-01-01
    相关资源
    最近更新 更多