Проверьте, запущен ли скрипт 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 существует, но процесс не запущен.


Это работает КРАСИВО. Для работы в 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 . С некоторыми настройками вы можете получить надежную, проверенную технологию, поддерживающую вашего демона.


Я согласен, не изобретайте велосипед, существует множество способов демонизировать ваше приложение, включая его перезапуск, если оно умирает, запуск, если не работает, и т. Д. И т. Д.
— 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'])

Этот сценарий должен запускаться как sudo, иначе вы получите ошибку отказа в доступе.
— DoesData 02

Кроме того, если вы передадите аргументы сценарию из команды, например, в списке, все эти аргументы будут также указаны.
— DoesData 02


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 .


0
ps ax | grep processName

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

pydevd.py --multiproc --client 127.0.0.1 --port 33882 --file processName

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)

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)

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


-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()

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.