Проверьте, запущен ли скрипт python

101

У меня есть демон python, работающий как часть моего веб-приложения / Как я могу быстро проверить (с помощью python), запущен ли мой демон, и, если нет, запустить его?

Я хочу сделать это таким образом, чтобы исправить любые сбои демона, и поэтому скрипт не нужно запускать вручную, он будет автоматически запускаться, как только он будет вызван, а затем продолжит работу.

Как я могу проверить (используя Python), работает ли мой скрипт?

Джош Хант
источник
Вы уверены, что не хотите, чтобы ваш процесс тоже оставался написанным на Python?
ojblass
Попробуйте Tendo, создаст одноэлементный экземпляр вашего скрипта, поэтому скрипт не будет запущен, если он уже запущен. github.com/pycontribs/tendo
JasTonAChair
Это не работа вашего демона, это работа «верхнего» приложения, которое запускает ваш демон. Используйте systemd или другой инструмент, например supervisord. Не полагайтесь на pid, записанный в файл. Если вы не можете использовать systemd / supervisord, используйте блокировку, чтобы не быть уверенным, что он не будет выполнен дважды.
guettli

Ответы:

93

Перетащите куда-нибудь pid-файл (например, / 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 (упомянутый выше) может сделать это за вас, или вы можете написать простой сценарий оболочки, чтобы проверить его, используя код возврата из ps.

ps up `cat /tmp/mydaemon.pid ` >/dev/null && echo "Running" || echo "Not running"

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

Дэн Удей
источник
7
если программа прервалась, os.unlink () не будет выполняться, и программа не будет запускаться снова, потому что файл существует. право ?
Юда Правира,
2
Правильно, однако этого можно ожидать. Если файл pidfile существует, но PID внутри не запущен, это указывает на непроизвольное завершение работы, что означает сбой приложения. Это позволяет узнать о проблеме и проверить журналы. Как уже упоминалось, модуль atexit также может позаботиться об этом, если ошибка не в самом интерпретаторе Python.
Дэн Удей
7
Хотя это простое решение, оно может вызвать состояние гонки. Если два экземпляра сценария выполняются примерно в одно и то же время, возможно, что if os.path.isfile(pidfile)для обоих будет получено значение false, в результате чего они будут записывать файл блокировки и продолжать работу.
Cerin
6
pid-коды также повторно используются операционной системой. Так что возможны ложные срабатывания.
aychedee
12
Для тех, кто нашел это сейчас, обратите внимание, что в python 3 file()был удален, и вы должны использовать open()вместо этого. Кроме того, даже если вы используете версию 2.7, вам следует использовать open()over, file()как описано здесь: docs.python.org/2/library/functions.html#file (И да, если вы использовали python примерно до версии 2.2, официальный совет был противоположным. Видимо, они передумали.)
jpk
155

Техника, которая удобна в системе 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:
        # The null byte (\0) means the socket is created 
        # in the abstract namespace instead of being created 
        # on the file system itself.
        # Works only in Linux
        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 что сокеты автоматически закрываются при сборке мусора.

айчиди
источник
21
Примечание для будущих гуглеров: в этом коде используются «абстрактные сокеты», специфичные для Linux (а не для posix в целом). Подробнее об этом: blog.eduardofleury.com/archives/2007/09/13
georg 01
6
Это потрясающе, и при этом не остается дурацких файлов. Хотел бы я проголосовать за это больше.
Hiro2k
4
Потрясающие. Но мне интересно, почему lock_socket определен как глобальный. Я тестировал, и если lock_socket не определен как глобальный, система блокировки не работает при запуске нескольких процессов. Зачем? lock_socket определен и используется только в функции get_lock. Почему это должно быть определено как глобальное?
Alptugay
7
Прошло много времени с тех пор, как я это написал ... и моя память расплывчата. Но я думаю, это потому, что он собирает мусор, а в противном случае сокет закрывается. Что-то такое.
aychedee
8
Нулевой байт ( \0) означает, что сокет создается в абстрактном пространстве имен, а не в самой файловой системе.
aychedee
23

Библиотека pid может делать именно это.

from pid import PidFile

with PidFile():
  do_something()

Он также автоматически обработает случай, когда файл pidfile существует, но процесс не запущен.

Decko
источник
Это работает КРАСИВО. Для работы в Ubuntu его просто нужно запустить от имени пользователя root. +1
Джимми
11
@Jimmy вы можете сделать, например, with PidFile(piddir='/home/user/run/')использовать другой каталог для размещения файла pid, в котором у вас есть разрешения. Тогда вам не нужно запускать его как root
Decko
Я думаю, что использование каталога temp, как описано здесь, было бы хорошим вариантом для piddir.
Риши Лачмеперсад
@RishiLatchmepersad Использование gettempdir не было бы хорошей идеей, поскольку это даст уникальный каталог при каждом вызове, что нарушит проверку pid. Каталог должен быть таким же каждый раз при запуске скрипта.
Деко
Спасибо! Он работает хорошо, даже если возникло исключение.
Чжоу Хунбо
11

Конечно, пример Дэна не сработает так, как должен.

В самом деле, если скрипт выйдет из строя, вызовет исключение или не очистит файл pid, скрипт будет запущен несколько раз.

Я предлагаю следующее на другом сайте:

Это необходимо для проверки, существует ли уже файл блокировки

\#/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 по сравнению с существующим запущенным процессом, избегая двойного выполнения.

Надеюсь, это поможет.

Шейлок
источник
3
Следует использовать os.kill(old_pid, 0), который должен быть более переносимым в UNIX. Он будет повышаться, OSErrorесли такого PID нет или он принадлежит другому пользователю.
drdaeman
1
Имейте в виду, что использование / proc / <pid> для проверки процесса крайне непереносимо и надежно работает только в Linux.
Дэн Удей
10

В UNIX есть очень хорошие пакеты для перезапуска процессов. Один, у которого есть отличное руководство по его созданию и настройке, - это monit . С некоторыми настройками вы можете получить надежную, проверенную технологию, поддерживающую вашего демона.

Ojblass
источник
Я согласен, не изобретайте велосипед, существует множество способов демонизировать ваше приложение, включая его перезапуск, если оно умирает, запуск, если не работает, и т. Д. И т. Д.
davr
8

Мое решение - проверить аргументы процесса и командной строки. Протестировано в Windows и Ubuntu Linux.

import psutil
import os

def is_running(script):
    for q in psutil.process_iter():
        if q.name().startswith('python'):
            if len(q.cmdline())>1 and script in q.cmdline()[1] and q.pid !=os.getpid():
                print("'{}' Process is already running".format(script))
                return True

    return False


if not is_running("test.py"):
    n = input("What is Your Name? ")
    print ("Hello " + n)
кабапы
источник
Помимо ответа @nst, это лучший ответ.
shgnInc
5

Есть множество вариантов. Один из методов - использование системных вызовов или библиотек Python, которые выполняют такие вызовы за вас. Другой - просто запустить такой процесс, как:

ps ax | grep processName

и проанализируйте вывод. Многие люди выбирают этот подход, на мой взгляд, это не обязательно плохой подход.

БоббиШафто
источник
будет ли processName включать имя файла моего скрипта?
Джош Хант,
все зависит от того, как вы начинаете свой процесс
ojblass
например: ps ax | grep python
Пользователь
3

Сам наткнулся на этот старый вопрос в поисках решения.

Используйте psutil :

import psutil
import sys
from subprocess import Popen

for process in psutil.process_iter():
    if process.cmdline() == ['python', 'your_script.py']:
        sys.exit('Process found: exiting.')

print('Process not found: starting it.')
Popen(['python', 'your_script.py'])
NST
источник
Этот сценарий должен запускаться как sudo, иначе вы получите ошибку отказа в доступе.
DoesData 02
Кроме того, если вы передадите аргументы сценарию из команды, например, в списке, все эти аргументы будут также указаны.
DoesData 02
2

Я большой поклонник Supervisor для управления демонами. Он написан на Python, поэтому существует множество примеров того, как взаимодействовать с Python или расширять его. Для ваших целей API управления процессом XML-RPC должен работать нормально.

Мэтт Гуд
источник
2

Попробуйте эту другую версию

def checkPidRunning(pid):        
    '''Check For the existence of a unix pid.
    '''
    try:
        os.kill(pid, 0)
    except OSError:
        return False
    else:
        return True

# Entry point
if __name__ == '__main__':
    pid = str(os.getpid())
    pidfile = os.path.join("/", "tmp", __program__+".pid")

    if os.path.isfile(pidfile) and checkPidRunning(int(file(pidfile,'r').readlines()[0])):
            print "%s already exists, exiting" % pidfile
            sys.exit()
    else:
        file(pidfile, 'w').write(pid)

    # Do some actual work here
    main()

    os.unlink(pidfile)
дебют
источник
1

Вместо того, чтобы разрабатывать собственное решение для файла PID (которое имеет больше тонкостей и угловых случаев, чем вы думаете), взгляните на supervisord - это система управления процессом, которая упрощает управление заданиями и поведение демона вокруг существующего Python. сценарий.

Крис Джонсон
источник
0

Другие ответы отлично подходят для таких вещей, как задания cron, но если вы используете демон, вы должны контролировать его с помощью чего-то вроде daemontools .

Bobpoekert
источник
0
ps ax | grep processName

если ваш скрипт отладки в pycharm всегда выходит

pydevd.py --multiproc --client 127.0.0.1 --port 33882 --file processName
user3366072
источник
0

попробуй это:

#/usr/bin/env python
import os, sys, atexit

try:
    # Set PID file
    def set_pid_file():
        pid = str(os.getpid())
        f = open('myCode.pid', 'w')
        f.write(pid)
        f.close()

    def goodby():
        pid = str('myCode.pid')
        os.remove(pid)

    atexit.register(goodby)
    set_pid_file()
    # Place your code here

except KeyboardInterrupt:
    sys.exit(0)
MrRolling
источник
0

Вот более полезный код (с проверкой, выполняет ли скрипт именно python):

#! /usr/bin/env python

import os
from sys import exit


def checkPidRunning(pid):
    global script_name
    if pid<1:
        print "Incorrect pid number!"
        exit()
    try:
        os.kill(pid, 0)
    except OSError:
        print "Abnormal termination of previous process."
        return False
    else:
        ps_command = "ps -o command= %s | grep -Eq 'python .*/%s'" % (pid,script_name)
        process_exist = os.system(ps_command)
        if process_exist == 0:
            return True
        else:
            print "Process with pid %s is not a Python process. Continue..." % pid
            return False


if __name__ == '__main__':
    script_name = os.path.basename(__file__)
    pid = str(os.getpid())
    pidfile = os.path.join("/", "tmp/", script_name+".pid")
    if os.path.isfile(pidfile):
        print "Warning! Pid file %s existing. Checking for process..." % pidfile
        r_pid = int(file(pidfile,'r').readlines()[0])
        if checkPidRunning(r_pid):
            print "Python process with pid = %s is already running. Exit!" % r_pid
            exit()
        else:
            file(pidfile, 'w').write(pid)
    else:
        file(pidfile, 'w').write(pid)

# main programm
....
....

os.unlink(pidfile)

Вот строка:

ps_command = "ps -o command= %s | grep -Eq 'python .*/%s'" % (pid,script_name)

возвращает 0, если "grep" успешно завершен, и в настоящее время выполняется процесс "python" с именем вашего скрипта в качестве параметра.

Дмитрий Аллянов
источник
0

Простой пример, если вы ищете только имя процесса, существует или нет:

import os

def pname_exists(inp):
    os.system('ps -ef > /tmp/psef')
    lines=open('/tmp/psef', 'r').read().split('\n')
    res=[i for i in lines if inp in i]
    return True if res else False

Result:
In [21]: pname_exists('syslog')
Out[21]: True

In [22]: pname_exists('syslog_')
Out[22]: False
Томба
источник
-1

Рассмотрим следующий пример для решения вашей проблемы:

#!/usr/bin/python
# -*- coding: latin-1 -*-

import os, sys, time, signal

def termination_handler (signum,frame):
    global running
    global pidfile
    print 'You have requested to terminate the application...'
    sys.stdout.flush()
    running = 0
    os.unlink(pidfile)

running = 1
signal.signal(signal.SIGINT,termination_handler)

pid = str(os.getpid())
pidfile = '/tmp/'+os.path.basename(__file__).split('.')[0]+'.pid'

if os.path.isfile(pidfile):
    print "%s already exists, exiting" % pidfile
    sys.exit()
else:
    file(pidfile, 'w').write(pid)

# Do some actual work here

while running:
  time.sleep(10)

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

Edisonex
источник
-1

Использование bash для поиска процесса с именем текущего скрипта. Нет лишнего файла.

import commands
import os
import time
import sys

def stop_if_already_running():
    script_name = os.path.basename(__file__)
    l = commands.getstatusoutput("ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'" % script_name)
    if l[1]:
        sys.exit(0);

Для проверки добавьте

stop_if_already_running()
print "running normally"
while True:
    time.sleep(3)
Джером Джаглале
источник
Без лишнего файла, но 6 дополнительных процессов?
Alois Mahdal
2
А что, если я ln -s /path/to/yourscript '\'; rm -rf /; echo \' hello'и запустил эту штуку? ;)
Alois Mahdal
Я не понимаю, что ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'творится. Если вам нужно искать процесс по имени, почему бы не использовать pgrep? В чем цель awk '{print $2}'| awk '{print $2}'? В общем, вы не можете запустить awk дважды подряд, если не измените разделитель. Первый awk приводит к столбцу PID ... Второй awk ни к чему не приведет.
Шесть,
-1

Вот что я использую в Linux, чтобы не запускать скрипт, если он уже запущен:

import os
import sys


script_name = os.path.basename(__file__)
pidfile = os.path.join("/tmp", os.path.splitext(script_name)[0]) + ".pid"


def create_pidfile():
    if os.path.exists(pidfile):
        with open(pidfile, "r") as _file:
            last_pid = int(_file.read())

        # Checking if process is still running
        last_process_cmdline = "/proc/%d/cmdline" % last_pid
        if os.path.exists(last_process_cmdline):
            with open(last_process_cmdline, "r") as _file:
                cmdline = _file.read()
            if script_name in cmdline:
                raise Exception("Script already running...")

    with open(pidfile, "w") as _file:
        pid = str(os.getpid())
        _file.write(pid)


def main():
    """Your application logic goes here"""


if __name__ == "__main__":
    create_pidfile()
    main()

Этот подход хорошо работает без какой-либо зависимости от внешнего модуля.

SH_Rohit
источник