简体   繁体   English

如何启动进程并将其放入python的后台?

[英]How can I start a process and put it to background in python?

I am currently writing my first python program (in Python 2.6.6). 我目前正在编写我的第一个python程序(在Python 2.6.6中)。 The program facilitates starting and stopping different applications running on a server providing the user common commands (like starting and stopping system services on a Linux server). 该程序有助于启动和停止在服务器上运行的不同应用程序,这些应用程序提供用户公共命令(例如在Linux服务器上启动和停止系统服务)。

I am starting the applications' startup scripts by 我正在启动应用程序的启动脚本

p = subprocess.Popen(startCommand, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
print(output)

The problem is, that the startup script of one application stays in foreground and so p.communicate() waits forever. 问题是,一个应用程序的启动脚本保持在前台,因此p.communicate()会永远等待。 I have already tried to use "nohup startCommand &" in front of the startCommand but that did not work as expected. 我已经尝试在startCommand前使用“nohup startCommand&”,但是没有按预期工作。

As a workaround I now use the following bash script to call the application's start script: 作为一种解决方法,我现在使用以下bash脚本来调用应用程序的启动脚本:

#!/bin/bash

LOGFILE="/opt/scripts/bin/logs/SomeServerApplicationStart.log"

nohup /opt/someDir/startSomeServerApplication.sh >${LOGFILE} 2>&1 &

STARTUPOK=$(tail -1 ${LOGFILE} | grep "Server started in RUNNING mode" | wc -l)
COUNTER=0

while [ $STARTUPOK -ne 1 ] && [ $COUNTER -lt 100 ]; do
   STARTUPOK=$(tail -1 logs/SomeServerApplicationStart.log | grep "Server started in RUNNING mode" | wc -l)
   if (( STARTUPOK )); then
      echo "STARTUP OK"
      exit 0
   fi
   sleep 1
   COUNTER=$(( $COUNTER + 1 ))
done

echo "STARTUP FAILED"

The bash script is called from my python code. 从我的python代码调用bash脚本。 This workaround works perfect but I would prefer to do all in python... 这种解决方法很完美但我宁愿在python中做所有...

Is subprocess.Popen the wrong way? 是subprocess.Popen错误的方式? How could I accommplish my task in Python only? 我怎么能只用Python来完成我的任务?

First it is easy not to block the Python script in communicate... by not calling communicate! 首先,通过不调用通信很容易不阻止Python脚本的通信! Just read from output or error output from the command until you find the correct message and just forget about the command. 只需从命令的输出或错误输出中读取,直到找到正确的消息并忘记命令。

# to avoid waiting for an EOF on a pipe ...
def getlines(fd):
    line = bytearray()
    c = None
    while True:
        c = fd.read(1)
        if c is None:
            return
        line += c
        if c == '\n':
            yield str(line)
            del line[:]

p = subprocess.Popen(startCommand, shell=True, stdout=subprocess.PIPE,
               stderr=subprocess.STDOUT) # send stderr to stdout, same as 2>&1 for bash
for line in getlines(p.stdout):
    if "Server started in RUNNING mode" in line:
        print("STARTUP OK")
        break
else:    # end of input without getting startup message
     print("STARTUP FAILED")
     p.poll()    # get status from child to avoid a zombie
     # other error processing

The problem with the above, is that the server is still a child a the Python process and could get unwanted signals such as SIGHUP. 上面的问题是,服务器仍然是Python进程的子进程,可能会收到不需要的信号,如SIGHUP。 If you want to make it a daemon, you must first start a subprocess that next start your server. 如果要使其成为守护程序,则必须首先启动下一个启动服务器的子进程。 That way when first child will end, it can be waited by caller and the server will get a PPID of 1 (adopted by init process). 这样,当第一个子节点结束时,它可以被调用者等待,服务器将获得PPID为1(由init进程采用)。 You can use multiprocessing module to ease that part 您可以使用多处理模块来简化该部分

Code could be like: 代码可以是:

import multiprocessing
import subprocess

# to avoid waiting for an EOF on a pipe ...
def getlines(fd):
    line = bytearray()
    c = None
    while True:
        c = fd.read(1)
        if c is None:
            return
        line += c
        if c == '\n':
            yield str(line)
            del line[:]

def start_child(cmd):
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                         shell=True)
    for line in getlines(p.stdout):
        print line
        if "Server started in RUNNING mode" in line:
            print "STARTUP OK"
            break
    else:
        print "STARTUP FAILED"

def main():
    # other stuff in program
    p = multiprocessing.Process(target = start_child, args = (server_program,))
    p.start()
    p.join()
    print "DONE"
    # other stuff in program

# protect program startup for multiprocessing module
if __name__ == '__main__':
    main()

One could wonder what is the need for the getlines generator when a file object is itself an iterator that returns one line at a time. 当文件对象本身是一次返回一行的迭代器时,人们可能想知道对getlines生成器的需求是什么。 The problem is that it internally calls read that read until EOF when file is not connected to a terminal. 问题是,当文件未连接到终端时,它内部调用read直到EOF的读取。 As it is now connected to a PIPE, you will not get anything until the server ends... which is not what is expected 因为它现在连接到PIPE,所以在服务器结束之前你不会得到任何东西......这不是预期的

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

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