Как запустить фоновый процесс в Python?

Я пытаюсь перенести оболочку script в гораздо более читаемую версию python. Исходная оболочка script запускает несколько процессов (утилит, мониторов и т.д.) В фоновом режиме с помощью "&". Как я могу добиться такого же эффекта в python? Я бы хотел, чтобы эти процессы не умирали, когда скрипты python завершались. Я уверен, что это как-то связано с концепцией демона, но я не мог найти, как это сделать легко.

Ответы

Ответ 1

Примечание. Этот ответ менее актуальен, чем когда он был опубликован в 2009 году. Теперь рекомендуется использовать модуль subprocess, показанный в других ответах в документах

(Обратите внимание, что модуль подпроцесса обеспечивает более мощные возможности для создания новых процессов и получения их результатов, использование этого модуля предпочтительнее использования этих функций.)


Если вы хотите, чтобы ваш процесс начинался в фоновом режиме, вы можете использовать system() и вызывать его так же, как ваша оболочка script, или вы можете spawn его:

import os
os.spawnl(os.P_DETACH, 'some_long_running_command')

(или, альтернативно, вы можете попробовать менее переносимый флаг os.P_NOWAIT).

См. документацию здесь.

Ответ 2

В то время как решение jkp работает, более новый способ работы (и способ, рекомендованный документацией) заключается в использовании модуля subprocess. Для простых команд это эквивалентно, но предлагает больше возможностей, если вы хотите сделать что-то сложное.

Пример для вашего случая:

import subprocess
subprocess.Popen(["rm","-r","some.file"])

Это запустит rm -r somefile в фоновом режиме. Обратите внимание, что вызов .communicate() для объекта, возвращенного из Popen, будет блокироваться до его завершения, поэтому не делайте этого, если хотите, чтобы он работал в фоновом режиме:

import subprocess
ls_output=subprocess.Popen(["sleep", "30"])
ls_output.communicate()  # Will block for 30 seconds

Смотрите документацию здесь.

Кроме того, пояснение: "Фон", как вы его здесь используете, является чисто концепцией оболочки; технически, вы имеете в виду, что хотите запустить процесс без блокировки, пока вы ждете его завершения. Тем не менее, я использовал здесь "background" для обозначения поведения, похожего на shell-background.

Ответ 3

Вероятно, вам нужен ответ "Как вызвать внешнюю команду в Python" .

Самый простой способ - использовать функцию os.system, например:

import os
os.system("some_command &")

В принципе, все, что вы передаете функции system, будет выполняться так же, как если бы вы передали его оболочке в script.

Ответ 4

Я нашел это здесь:

В windows (win xp) родительский процесс не завершится, пока longtask.py не завершит свою работу. Это не то, что вы хотите в CGI-скрипте. Проблема не специфична для Python, в сообществе PHP проблемы одинаковы.

Решение состоит в том, чтобы передать DETACHED_PROCESS Флаг создания процесса в базовую функцию CreateProcess в win API. Если вы установили pywin32, вы можете импортировать флаг из модуля win32process, в противном случае вы должны определить его самостоятельно:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

Ответ 5

Используйте subprocess.Popen() с параметром close_fds=True, что позволит отделить порожденный подпроцесс от самого процесса Python и продолжить работу даже после выхода из Python.

https://gist.github.com/yinjimmy/d6ad0742d03d54518e9f

import os, time, sys, subprocess

if len(sys.argv) == 2:
    time.sleep(5)
    print 'track end'
    if sys.platform == 'darwin':
        subprocess.Popen(['say', 'hello'])
else:
    print 'main begin'
    subprocess.Popen(['python', os.path.realpath(__file__), '0'], close_fds=True)
    print 'main end'

Ответ 6

Вероятно, вы захотите начать изучение модуля os для разветвления различных потоков (путем открытия интерактивного сеанса и выдачи справки (os)). Соответствующими функциями являются fork и любой из exec. Чтобы дать вам представление о том, как начать, поместите что-то вроде этого в функцию, которая выполняет fork (функции нужно взять список или кортеж "args" в качестве аргумента, который содержит имя программы и ее параметры, вы также можете захотеть для определения stdin, out и err для нового потока):

try:
    pid = os.fork()
except OSError, e:
    ## some debug output
    sys.exit(1)
if pid == 0:
    ## eventually use os.putenv(..) to set environment variables
    ## os.execv strips of args[0] for the arguments
    os.execv(args[0], args)

Ответ 7

И захватывать вывод и работать на фоне с threading

Как уже упоминалось в этом ответе, если вы перехватываете вывод с помощью stdout= а затем пытаетесь read(), тогда процесс блокируется.

Однако есть случаи, когда вам это нужно. Например, я хотел запустить два процесса, которые общаются через порт между ними, и сохранить их стандартный вывод в файл журнала и стандартный вывод.

Модуль threading позволяет нам это делать.

Во-первых, рассмотрим, как выполнить часть перенаправления вывода в этом вопросе: Python Popen: одновременная запись в стандартный вывод и файл журнала

Затем:

main.py

#!/usr/bin/env python3

import os
import subprocess
import sys
import threading

def output_reader(proc, file):
    while True:
        byte = proc.stdout.read(1)
        if byte:
            sys.stdout.buffer.write(byte)
            sys.stdout.flush()
            file.buffer.write(byte)
        else:
            break

with subprocess.Popen(['./sleep.py', '0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc1, \
     subprocess.Popen(['./sleep.py', '10'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc2, \
     open('log1.log', 'w') as file1, \
     open('log2.log', 'w') as file2:
    t1 = threading.Thread(target=output_reader, args=(proc1, file1))
    t2 = threading.Thread(target=output_reader, args=(proc2, file2))
    t1.start()
    t2.start()
    t1.join()
    t2.join()

sleep.py

#!/usr/bin/env python3

import sys
import time

for i in range(4):
    print(i + int(sys.argv[1]))
    sys.stdout.flush()
    time.sleep(0.5)

После запуска:

./main.py

stdout обновляется каждые 0,5 секунды для каждых двух строк:

0
10
1
11
2
12
3
13

и каждый файл журнала содержит соответствующий журнал для данного процесса.

Вдохновленный: https://eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/

Протестировано на Ubuntu 18.04, Python 3.6.7.