Ответ 1
Поскольку @mark разъяснил систему Linux, script может легко сделать себя полностью независимым, т.е. демоном, следуя этому recipe. (Вы также можете сделать это в родительском после os.fork
и только тогда os.exec...
дочерний процесс).
Изменить: чтобы прояснить некоторые детали. wrt @mark комментарий к моему ответу: привилегии суперпользователя не нужны для "демонализации" процесса в соответствии с рецептами поваренной книги, и нет необходимости изменять текущий рабочий каталог (хотя код в рецепте делает это и многое другое, это не ключевая часть, а скорее правильная логическая последовательность вызовов fork
, _exit
и setsid
). Различные варианты os.exec...
, которые не заканчиваются на e
, используют родительскую среду процесса, так что эта часть тоже легко - см. Python online docs.
Чтобы обратиться к предложениям, высказанным в комментариях и ответах других пользователей: Я считаю, что subprocess
и multiprocessing
сами по себе не демонизируют дочерний процесс, который, по-видимому, нуждается в @mark; script мог бы сделать это сам по себе, но поскольку какой-то код должен делать fork
и setsid
, мне кажется, что мне лучше поддерживать все нерестилища на этой низкоуровневой плоскости, а не смешивать некоторые высокоуровневые и некоторый низкоуровневый код в ходе операции.
Здесь значительно уменьшенная и упрощенная версия рецепта по указанному выше URL-адресу, предназначенная для вызова родителем, чтобы вызвать ребенка-демона. Таким образом, этот код можно использовать и для выполнения исполняемых файлов, отличных от Python. Как указано, код должен удовлетворять потребностям @mark, пояснил, конечно, его можно настроить разными способами - я настоятельно рекомендую прочитать оригинальный рецепт, его комментарии и обсуждения, а также книги, которые он рекомендует, для получения дополнительной информации.
import os
import sys
def spawnDaemon(path_to_executable, *args)
"""Spawn a completely detached subprocess (i.e., a daemon).
E.g. for mark:
spawnDaemon("../bin/producenotify.py", "producenotify.py", "xx")
"""
# fork the first time (to make a non-session-leader child process)
try:
pid = os.fork()
except OSError, e:
raise RuntimeError("1st fork failed: %s [%d]" % (e.strerror, e.errno))
if pid != 0:
# parent (calling) process is all done
return
# detach from controlling terminal (to make child a session-leader)
os.setsid()
try:
pid = os.fork()
except OSError, e:
raise RuntimeError("2nd fork failed: %s [%d]" % (e.strerror, e.errno))
raise Exception, "%s [%d]" % (e.strerror, e.errno)
if pid != 0:
# child process is all done
os._exit(0)
# grandchild process now non-session-leader, detached from parent
# grandchild process must now close all open files
try:
maxfd = os.sysconf("SC_OPEN_MAX")
except (AttributeError, ValueError):
maxfd = 1024
for fd in range(maxfd):
try:
os.close(fd)
except OSError: # ERROR, fd wasn't open to begin with (ignored)
pass
# redirect stdin, stdout and stderr to /dev/null
os.open(os.devnull, os.O_RDWR) # standard input (0)
os.dup2(0, 1)
os.dup2(0, 2)
# and finally let execute the executable for the daemon!
try:
os.execv(path_to_executable, args)
except Exception, e:
# oops, we're cut off from the world, let just give up
os._exit(255)