Проверьте, запущен ли скрипт Python
У меня есть демон python, запущенный как часть моего веб-приложения/Как я могу быстро проверить (используя python), если мой демон запущен, а если нет, запустите его? Я хочу сделать это таким образом, чтобы исправить любые сбои демона, и поэтому script не нужно запускать вручную, он будет автоматически запускаться, как только он будет вызываться, а затем останется работать. Как я могу проверить (используя python), если мой script запущен?
Попробуйте Tendo, создайте единственный экземпляр вашего скрипта, поэтому скрипт не запустится, если он уже запущен. github.com/pycontribs/tendo
19 ответов
Оставьте pidfile где-нибудь (например,/tmp). Затем вы можете проверить, работает ли процесс, проверяя, существует ли PID в файле. Не забудьте удалить файл, когда вы завершите работу, и проверьте его при запуске.
#/usr/bin/env python import os import sys pid = str(os.getpid()) pidfile = "/tmp/mydaemon.pid" if os.path.isfile(pidfile): print "%s already exists, exiting" % pidfile sys.exit() file(pidfile, 'w').write(pid) try: # Do some actual work here finally: os.unlink(pidfile)
Затем вы можете проверить, работает ли процесс, проверяя, является ли содержимое /tmp/mydaemon.pid существующим процессом. Monit (упомянутый выше) может сделать это для вас, или вы можете написать простую оболочку script, чтобы проверить ее для вас, используя код возврата из ps.
ps up `cat /tmp/mydaemon.pid ` >/dev/null && echo "Running" || echo "Not running"
Для дополнительного кредита вы можете использовать модуль atexit, чтобы гарантировать, что ваша программа очистит свой pidfile при любых обстоятельствах (когда убиты, подняты исключения и т.д.).
Поскольку ссылка на файловый объект никогда не сохраняется, она не будет хранить счетчик ссылок после завершения вызова write (). Я не уверен, считается ли это Pythonic, но это работает довольно хорошо. В частности: объект будет выделен, вызван метод write (), а затем он будет немедленно освобожден, что закроет файл для вас.
если программа прервалась, os.unlink () не будет выполняться, и программа не запустится снова, поскольку файл существует. право ?
Правильно, однако это может быть ожидаемым поведением. Если pid-файл существует, но PID внутри не запущен, это указывает на незаметное завершение работы, что означает сбой приложения. Это позволяет вам знать, что есть проблема, и проверять журналы. Как уже упоминалось, модуль atexit также может позаботиться об этом, предполагая, что ошибка не в самом интерпретаторе Python.
Хотя это простое решение, оно подвержено состоянию гонки. Если два экземпляра сценария выполняются примерно в одно и то же время, вполне возможно, что if os.path.isfile(pidfile) может принять значение false для обоих, в результате чего они оба if os.path.isfile(pidfile) файл блокировки и продолжат работу.
Для тех, кто находит это сейчас, обратите внимание, что в python 3 file() был удален, и вы должны использовать open() . Кроме того, даже если вы используете версию 2.7, вы должны использовать open() вместо file() как описано здесь: docs.python.org/2/library/functions.html#file (и да, если вы использовали python обратно около 2.2, Официальный совет был обратным. Видимо, они передумали.)
Техника, которая удобна в системе Linux, использует доменные сокеты:
import socket import sys import time def get_lock(process_name): # Without holding a reference to our socket somewhere it gets garbage # collected when the function exits get_lock._lock_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) try: get_lock._lock_socket.bind('\0' + process_name) print 'I got the lock' except socket.error: print 'lock exists' sys.exit() get_lock('running_test') while True: time.sleep(3)
Это атомарное решение, позволяющее избежать проблем, связанных с блокировкой файлов, если ваш процесс отправляется SIGKILL
Вы можете прочитать в документации по socket.close что сокеты автоматически закрываются при сборке мусора.
Примечание для будущих гуглеров: в этом коде используются «абстрактные сокеты», специфичные для Linux (в общем, не posix). Подробнее об этом: blog.eduardofleury.com/archives/2007/09/13
Потрясающие. Но мне интересно, почему lock_socket определяется глобально. Я проверил, и если lock_socket не определен как глобальный, система блокировки не работает при запуске нескольких процессов. Зачем? lock_socket определен и используется только в функции get_lock. Почему это должно быть определено глобально?
Прошло много времени с тех пор, как я написал это . и моя память туманна. Но я думаю, что это потому, что он собирает мусор, а сокет закрывается в противном случае. Что-то вроде того.
@aychedee: Я получаю сообщение об ошибке на Mac: такого файла или каталога нет. Строка, которая выбрасывает, является lock_socket.bind(‘\0blah’) .
Вы имеете в виду использование сокета в пространстве имен файловой системы? Что-то вроде socket.bind(‘/tmp/socky) ? Когда я вызываю close на этом сокете, файл все еще остается, и я не могу привязать другой сокет к тому же месту. Делать это немного бесполезным с целью блокировки .
Вы можете использовать def get_lock(process_name, lock_socket=socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)): вместо global .
Вы действительно проверяли этот код? Есть причина для использования global . Изменилась ли среда исполнения Python в последнее время? Это Питон 3?
Нулевой байт ( \0 ) означает, что сокет создается в абстрактном пространстве имен, а не в самой файловой системе.
@aychedee> предложение Дж. Ф. Себастьяна использует тот факт, что аргументы по умолчанию оцениваются, когда определение функции интерпретируется и имеет одинаковое время жизни. Поэтому, пока вы не del функцию, сокет не будет собирать мусор. Это будет работать, хотя и немного хакерски. Для того же эффекта и во избежание загрязнения глобального пространства имен вы можете просто использовать вместо него get_lock.socket , поскольку функции являются объектами первого класса.
О верно. Я вижу, что он получал сейчас, и да, мы могли бы привязать это к самой функции. И я проверил это, и это определенно работает.
Легко ответить на вопрос моего новичка, но почему бы просто не проверить, работает ли конкретный сценарий python «xxx.py», работающий с использованием Popen([‘pidof’,»xxx.py») ?
Вы могли бы сделать это . но что, если вы хотите изменить имя вашего сценария? Или, что более важно, что, если две копии сценария были запущены одновременно? Я вполне уверен, что если бы мне удалось правильно выбрать время, я мог бы запустить оба из них, не осознавая, что другое тоже началось. Этот механизм блокировки файлов является атомарным. Это означает, что его нельзя схватить двумя разными процессами.
Кажется, это работает только для меня с перерывами. Иногда, даже после закрытия сценария и ожидания ~ 1-2 минут, сценарий все равно будет содержать блокировку процесса, и 2-й экземпляр не может быть запущен. Есть ли способ обойти это или заставить GC делать свое дело? Я смог выполнить сценарий снова во второй раз, обновив значение параметра process_name, но есть ли лучший способ сделать это? Мне нравится это чистое решение, и я не хочу переходить на использование ps -ef, чтобы проверить, является ли это единственным стабильным способом сделать это
unix.stackexchange.com/questions/216784/… — это работает для меня . Я должен предположить, что что-то на самом деле является ссылкой на сокет вокруг. Мой абстрактный сокет исчезает мгновенно, когда скрипт завершает работу, или он ловит SIGKILL. Действительно ли завершился процесс Python? Это было бы, где я бы расследовал.
есть ли способ снять блокировку? Потому что я продолжаю выходить из программы из-за некоторых работающих сокетов ..
Библиотека pid может сделать именно это.
from pid import PidFile with PidFile(): do_something()
Он также автоматически обрабатывает случай, когда существует файл pidfile, но процесс не запущен.
Это работает КРАСИВО. Он просто должен быть запущен от имени пользователя root для запуска в Ubuntu. +1
@Jimmy вы можете сделать, например, with PidFile(piddir=’/home/user/run/’) чтобы использовать другой каталог для размещения файла pid там, где у вас есть разрешения. Тогда вам не нужно запускать его как root
Конечно, пример от Дэна не будет работать так, как должно быть.
В самом деле, если сбой script, появление исключения или очистка файла pid, script будет выполняться несколько раз.
Я предлагаю следующее на другом сайте:
Это проверить, существует ли уже существующий файл блокировки
\#/usr/bin/env python import os import sys if os.access(os.path.expanduser("~/.lockfile.vestibular.lock"), os.F_OK): #if the lockfile is already there then check the PID number #in the lock file pidfile = open(os.path.expanduser("~/.lockfile.vestibular.lock"), "r") pidfile.seek(0) old_pid = pidfile.readline() # Now we check the PID from lock file matches to the current # process PID if os.path.exists("/proc/%s" % old_pid): print "You already have an instance of the program running" print "It is running as process %s," % old_pid sys.exit(1) else: print "File is there but the program is not running" print "Removing lock file for the: %s as it can be there because of the program last time it was run" % old_pid os.remove(os.path.expanduser("~/.lockfile.vestibular.lock"))
Это часть кода, где мы помещаем PID файл в файл блокировки
pidfile = open(os.path.expanduser("~/.lockfile.vestibular.lock"), "w") pidfile.write("%s" % os.getpid()) pidfile.close()
Этот код проверяет значение pid по сравнению с существующим запущенным процессом., избегая двойного выполнения.
Следует использовать os.kill(old_pid, 0) , который должен быть более переносимым в UNIX. Это вызовет OSError если такой PID отсутствует или он принадлежит другому пользователю.
Имейте в виду, что использование / proc /
Есть очень хорошие пакеты для перезапуска процессов в UNIX. Тот, у которого есть большой учебник по его созданию и настройке, monit. С некоторой настройкой вы можете испытать солидную технологию, поддерживающую ваш демон.
Я согласен, не изобретайте колесо, есть множество способов демонизировать ваше приложение, включая его перезапуск, если оно умирает, запуск, если он не запущен, и т. Д. И т. Д.
Существует множество вариантов. Один из методов — использование системных вызовов или библиотек python, которые выполняют такие вызовы для вас. Другой — это просто создать такой процесс, как:
и проанализировать вывод. Многие люди выбирают этот подход, это не обязательно плохой подход на мой взгляд.