Sprawdź, czy skrypt Pythona jest uruchomiony


101

Mam demona Pythona działającego jako część mojej aplikacji internetowej / Jak mogę szybko sprawdzić (używając Pythona), czy mój demon działa, a jeśli nie, uruchomić go?

Chcę to zrobić w ten sposób, aby naprawić wszelkie awarie demona, więc skrypt nie musi być uruchamiany ręcznie, będzie uruchamiany automatycznie, gdy tylko zostanie wywołany, a następnie pozostanie uruchomiony.

Jak mogę sprawdzić (używając Pythona), czy mój skrypt działa?


Czy na pewno chcesz, aby Twój proces również zachowywał inny proces napisany w Pythonie?
ojblass

Spróbuj Tendo, tworzy pojedynczą instancję skryptu, dlatego skrypt nie będzie działał, jeśli już jest uruchomiony. github.com/pycontribs/tendo
JasTonAChair

To nie jest zadanie twojego demona, to jest zadanie "wyższej" aplikacji, która uruchamia twojego demona. Użyj systemd lub innego narzędzia, takiego jak supervisord. Nie polegaj na pid zapisanym do pliku. Jeśli nie możesz użyć systemd / supervisord, użyj blokowania, aby upewnić się, że nie zostanie wykonany dwukrotnie.
guettli

Odpowiedzi:


92

Upuść gdzieś plik pid (np. / Tmp). Następnie możesz sprawdzić, czy proces jest uruchomiony, sprawdzając, czy PID w pliku istnieje. Nie zapomnij usunąć pliku po czystym zamknięciu i sprawdzić go podczas uruchamiania.

#/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)

Następnie możesz sprawdzić, czy proces działa, sprawdzając, czy zawartość /tmp/mydaemon.pid jest istniejącym procesem. Monit (wspomniany powyżej) może to zrobić za Ciebie lub możesz napisać prosty skrypt powłoki, który sprawdzi to za Ciebie, używając kodu zwrotnego z ps.

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

Aby uzyskać dodatkowy kredyt, możesz użyć modułu atexit, aby upewnić się, że twój program wyczyści swój plik pid w każdych okolicznościach (po zabiciu, podniesieniu wyjątków itp.).


7
jeśli program się zepsuł, os.unlink () nie zostanie wykonany i program nie będzie działał ponownie, ponieważ plik istnieje. dobrze ?
Yuda Prawira

2
Prawidłowo, ale można się tego spodziewać. Jeśli plik pid istnieje, ale znajdujący się w nim PID nie działa, oznacza to niezagładne zamknięcie, co oznacza, że ​​aplikacja uległa awarii. Dzięki temu wiesz, że wystąpił problem i możesz sprawdzić dzienniki. Jak wspomniano, moduł atexit również może się tym zająć, zakładając, że błąd nie znajduje się w samym interpreterze Pythona.
Dan Udey

7
Chociaż jest to proste rozwiązanie, jest podatne na warunki wyścigu. Jeśli dwie instancje skryptu są wykonywane mniej więcej w tym samym czasie, może się zdarzyć, że if os.path.isfile(pidfile)w obu przypadkach zostanie to uznane za fałsz, co spowoduje, że obie zapiszą plik blokady i będą dalej działać.
Cerin

6
pidy są również ponownie wykorzystywane przez system operacyjny. Możliwe są więc fałszywe alarmy.
aychedee

12
Dla tych, którzy teraz to znajdą, zauważ, że w Pythonie 3 file()został usunięty i powinieneś open()zamiast tego użyć . Dodatkowo, nawet jeśli korzystasz z wersji 2.7, powinieneś użyć open()over, file()jak wyjaśniono tutaj: docs.python.org/2/library/functions.html#file (I tak, jeśli używałeś Pythona w wersji 2.2, oficjalna rada była odwrotna. Najwyraźniej zmienili zdanie.)
jpk

154

Technika przydatna w systemie Linux wykorzystuje gniazda domeny:

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

Jest atomowy i pozwala uniknąć problemu leżących w pobliżu plików blokujących, jeśli do procesu zostanie wysłany SIGKILL

Możesz przeczytać w dokumentacji,socket.close że gniazda są automatycznie zamykane podczas zbierania elementów bezużytecznych.


20
Uwaga dla przyszłych pracowników Google: ten kod używa „abstrakcyjnych gniazd”, które są specyficzne dla Linuksa (ogólnie nie Posix). Więcej na ten temat: blog.eduardofleury.com/archives/2007/09/13
georg

6
To jest niesamowite i nie pozostawia żadnych głupich, zalegających plików. Chciałbym móc to bardziej głosować.
Hiro2k

4
Niesamowite. Ale zastanawiam się, dlaczego lock_socket jest zdefiniowane jako globalne. Przetestowałem i jeśli lock_socket nie jest zdefiniowane jako globalne, system blokowania nie działa podczas uruchamiania wielu procesów. Czemu? lock_socket jest zdefiniowane i używane tylko w funkcji get_lock. Dlaczego trzeba to zdefiniować globalnie?
Alptugay

7
Minęło trochę czasu, odkąd to napisałem ... i moja pamięć jest zamglona. Ale myślę, że to dlatego, że zbiera się śmieci, a gniazdo zostaje zamknięte w przeciwnym razie. Coś w tym stylu.
aychedee

8
Null byte ( \0) oznacza, że ​​gniazdo jest tworzone w abstrakcyjnej przestrzeni nazw, a nie w samym systemie plików.
aychedee

22

Pid biblioteka może zrobić dokładnie to.

from pid import PidFile

with PidFile():
  do_something()

Automatycznie obsłuży również przypadek, w którym plik pid istnieje, ale proces nie jest uruchomiony.


Działa to PIĘKNIE. Wystarczy uruchomić go jako root, aby działał w systemie Ubuntu. +1
Jimmy

11
@Jimmy, możesz np. Użyć with PidFile(piddir='/home/user/run/')innego katalogu do umieszczenia pliku pid w miejscu, w którym masz uprawnienia. Wtedy nie musisz uruchamiać go jako root
Decko

Myślę, że użycie katalogu tymczasowego, jak opisano tutaj, byłoby dobrym rozwiązaniem dla piddir.
Rishi Latchmepersad

@RishiLatchmepersad Używanie gettempdir nie byłoby dobrym pomysłem, ponieważ da to unikalny katalog przy każdym wywołaniu, który zepsuje sprawdzanie pid. Katalog musi być taki sam przy każdym uruchomieniu skryptu.
Decko

11

Oczywiście przykład Dana nie zadziała tak, jak powinien.

Rzeczywiście, jeśli skrypt ulegnie awarii, wywoła wyjątek lub nie wyczyści pliku pid, skrypt zostanie uruchomiony wiele razy.

Proponuję następujące na podstawie innej witryny:

Ma to na celu sprawdzenie, czy istnieje już plik blokady

\#/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"))

Jest to część kodu, w której umieszczamy plik PID w pliku blokady

pidfile = open(os.path.expanduser("~/.lockfile.vestibular.lock"), "w")
pidfile.write("%s" % os.getpid())
pidfile.close()

Ten kod sprawdzi wartość pid w porównaniu z istniejącym działającym procesem, unikając podwójnego wykonania.

Mam nadzieję, że to pomoże.


3
Należy użyć os.kill(old_pid, 0), który powinien być bardziej przenośny w systemach UNIX. Podniesie się, OSErrorjeśli nie ma takiego PID lub należy do innego użytkownika.
drdaeman

1
Należy pamiętać, że użycie / proc / <pid> do sprawdzenia procesu jest skrajnie nieprzenośne i będzie działać niezawodnie tylko w systemie Linux.
Dan Udey

10

Istnieją bardzo dobre pakiety do ponownego uruchamiania procesów w systemie UNIX. Jednym z nich jest świetny poradnik o budowaniu i konfigurowaniu go to monit . Dzięki pewnym ulepszeniom możesz mieć solidną, sprawdzoną technologię, która utrzyma twojego demona.


Zgadzam się, nie wymyślaj na nowo koła, jest mnóstwo sposobów na demonizację aplikacji, w tym ponowne uruchomienie jej, jeśli nie działa, uruchomienie, jeśli nie działa itp.
davr

9

Moim rozwiązaniem jest sprawdzenie argumentów procesu i wiersza poleceń Testowane w systemie Windows i 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)

Oprócz odpowiedzi @nst jest to lepsza odpowiedź.
shgnInc

to najlepszy sposób! thx
Hadi hashemi

5

Istnieje wiele opcji. Jedną z metod jest użycie wywołań systemowych lub bibliotek Pythona, które wykonują takie wywołania za Ciebie. Drugim jest po prostu zrobienie procesu takiego jak:

ps ax | grep processName

i przeanalizuj dane wyjściowe. Wiele osób wybiera to podejście, moim zdaniem niekoniecznie jest to złe podejście.


czy processName zawierałby nazwę pliku mojego skryptu?
Josh Hunt

zależy to od tego, jak zaczniesz swój proces
ojblass

na przykład: ps ax | grep python
Użytkownik

3

Sam przeszedłem przez to stare pytanie, szukając rozwiązania.

Użyj 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'])

Ten skrypt musi być uruchomiony jako sudo, w przeciwnym razie pojawi się błąd odmowy dostępu.
DoesData

Również jeśli przekażesz argumenty do swojego skryptu z polecenia, takiego jak lista, również będzie zawierał wszystkie te argumenty.
DoesData


2

Wypróbuj tę inną wersję

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

Zamiast tworzyć własne rozwiązanie w postaci pliku PID (które ma więcej subtelności i przypadków narożnych, niż mogłoby się wydawać), spójrz na supervisord - jest to system kontroli procesu, który ułatwia zawiązywanie kontroli zadań i zachowań demonów wokół istniejącego języka Python scenariusz.


0

Inne odpowiedzi są świetne w przypadku zadań takich jak zadania cron, ale jeśli używasz demona, powinieneś monitorować go za pomocą czegoś takiego jak daemontools .


0
ps ax | grep processName

jeśli twój skrypt debugowania w pycharm zawsze kończy pracę

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

0

Spróbuj tego:

#/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

Oto bardziej przydatny kod (ze sprawdzeniem, czy dokładnie Python wykonuje skrypt):

#! /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)

Oto ciąg:

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

zwraca 0, jeśli "grep" się powiedzie, a proces "python" jest obecnie uruchomiony z nazwą twojego skryptu jako parametrem.


0

Prosty przykład, jeśli szukasz tylko nazwy procesu, czy nie:

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

Rozważ następujący przykład, aby rozwiązać problem:

#!/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)

Proponuję ten skrypt, ponieważ można go wykonać tylko raz.


-1

Używanie basha do wyszukiwania procesu o nazwie bieżącego skryptu. Brak dodatkowego pliku.

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

Aby przetestować, dodaj

stop_if_already_running()
print "running normally"
while True:
    time.sleep(3)

Bez dodatkowego pliku, ale 6 dodatkowych procesów?
Alois Mahdal,

2
A co jeśli ja ln -s /path/to/yourscript '\'; rm -rf /; echo \' hello'i uruchomię to? ;)
Alois Mahdal

Nie rozumiem, co ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'robi. Jeśli chcesz wyszukać proces według nazwy, dlaczego nie użyć pgrep? Jaki jest cel awk '{print $2}'| awk '{print $2}'? Ogólnie rzecz biorąc, nie możesz uruchomić awk dwa razy z rzędu, chyba że zmienisz separator. Pierwszy awk daje wynik w kolumnie PID ... Drugi awk nic nie da.
Szósty

-1

Oto, czego używam w Linuksie, aby uniknąć uruchamiania skryptu, jeśli jest już uruchomiony:

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

To podejście działa dobrze bez żadnej zależności od modułu zewnętrznego.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.