Controlla se lo script Python è in esecuzione


101

Ho un demone python in esecuzione come parte della mia app web / Come posso verificare rapidamente (usando python) se il mio demone è in esecuzione e, in caso contrario, avviarlo?

Voglio farlo in questo modo per correggere eventuali arresti anomali del demone, quindi lo script non deve essere eseguito manualmente, verrà eseguito automaticamente non appena viene chiamato e quindi rimarrà in esecuzione.

Come posso verificare (usando python) se il mio script è in esecuzione?


Sei sicuro di non voler mantenere anche il tuo processo scritto in Python?
ojblass

Prova Tendo, crea un'istanza singleton del tuo script, quindi lo script non verrà eseguito se è già in esecuzione. github.com/pycontribs/tendo
JasTonAChair

Questo non è il lavoro del tuo demone, questo è il lavoro dell'applicazione "superiore" che lancia il tuo demone. Usa systemd o un altro strumento come supervisord. Non fare affidamento su un pid scritto su un file. Se non è possibile utilizzare systemd / supervisord, utilizzare il blocco per assicurarsi che non venga eseguito due volte.
guettli

Risposte:


92

Rilascia un pidfile da qualche parte (ad esempio / tmp). Quindi puoi verificare se il processo è in esecuzione controllando per vedere se il PID nel file esiste. Non dimenticare di eliminare il file quando lo spegni in modo pulito e di controllarlo all'avvio.

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

Quindi puoi verificare se il processo è in esecuzione controllando se il contenuto di /tmp/mydaemon.pid è un processo esistente. Monit (menzionato sopra) può farlo per te, oppure puoi scrivere un semplice script di shell per controllarlo usando il codice di ritorno da ps.

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

Per un credito extra, puoi usare il modulo atexit per assicurarti che il tuo programma ripulisca il suo pidfile in qualsiasi circostanza (quando ucciso, eccezioni sollevate, ecc.).


6
se il programma si è interrotto, os.unlink () non verrà eseguito e il programma non verrà eseguito di nuovo, perché il file esiste. giusto ?
Yuda Prawira

2
È corretto, tuttavia questo potrebbe essere un comportamento previsto. Se il pidfile esiste ma il PID all'interno non è in esecuzione, ciò indica un arresto non regolare, il che significa che l'app si è bloccata. Questo ti consente di sapere che c'è un problema e di controllare i log. Come accennato, anche il modulo atexit può occuparsi di questo, supponendo che il bug non sia nell'interprete Python stesso.
Dan Udey

7
Sebbene sia una soluzione semplice, questo è suscettibile di una condizione di gara. Se due istanze dello script vengono eseguite più o meno nello stesso momento, è possibile che venga if os.path.isfile(pidfile)restituito false per entrambe, facendo sì che entrambe scrivano il file di blocco e continuino a funzionare.
Cerin

6
pid vengono anche riutilizzati dal sistema operativo. Quindi sono possibili falsi positivi.
aychedee

12
Per coloro che lo trovano ora, nota che in Python 3 è file()stato rimosso e dovresti open()invece usare . Inoltre, anche se sei su 2.7 dovresti usare open()over file()come spiegato qui: docs.python.org/2/library/functions.html#file (E sì, se hai usato python intorno alla 2.2 il consiglio ufficiale era l'opposto. A quanto pare hanno cambiato idea.)
jpk

154

Una tecnica utile su un sistema Linux è l'utilizzo di socket di dominio:

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)

È atomico ed evita il problema di avere file di blocco in giro se il tuo processo viene inviato un SIGKILL

È possibile leggere nella documentazionesocket.close che i socket vengono chiusi automaticamente quando vengono raccolti i dati inutili.


20
Una nota per i futuri googler: questo codice utilizza "socket astratti", che sono specifici di Linux (non posix in generale). Maggiori informazioni su questo: blog.eduardofleury.com/archives/2007/09/13
georg

6
Questo è fantastico e non lascia stupidi file persistenti. Vorrei poter votare di più.
Hiro2k

4
Eccezionale. Ma mi chiedo perché lock_socket sia definito globale. Ho testato e se lock_socket non è definito globale, il sistema di blocco non funziona durante l'esecuzione di più processi. Perché? lock_socket è definito e utilizzato solo nella funzione get_lock. Perché deve essere definito globale?
Alptugay

7
È passato un po 'di tempo da quando ho scritto questo ... e la mia memoria è offuscata. Ma penso che sia stato perché viene raccolto dalla spazzatura e il socket viene chiuso altrimenti. Qualcosa del genere.
aychedee

8
Il null byte ( \0) significa che il socket viene creato nello spazio dei nomi astratto invece di essere creato sul file system stesso.
aychedee

22

La libreria pid può fare esattamente questo.

from pid import PidFile

with PidFile():
  do_something()

Gestirà automaticamente anche il caso in cui il pidfile esiste ma il processo non è in esecuzione.


Funziona IN MODO BELLISSIMO. Deve solo essere eseguito come root per funzionare su Ubuntu. +1
Jimmy

11
@ Jimmy puoi fare ad esempio with PidFile(piddir='/home/user/run/')per usare una directory diversa per mettere il file pid in cui hai i permessi. Quindi non è necessario eseguirlo come root
Decko

Sto pensando che l'utilizzo della directory temporanea come descritto qui sarebbe una buona opzione per il piddir.
Rishi Latchmepersad

@RishiLatchmepersad L'utilizzo di gettempdir non sarebbe una buona idea poiché ciò fornirà una directory univoca a ogni chiamata che interromperà il controllo pid. La directory deve essere la stessa ogni volta che viene eseguito lo script.
Decko

11

Ovviamente l'esempio di Dan non funzionerà come dovrebbe.

Infatti, se lo script si blocca, genera un'eccezione o non pulisce il file pid, lo script verrà eseguito più volte.

Suggerisco quanto segue sulla base di un altro sito Web:

Questo serve per verificare se esiste già un file di blocco

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

Questo fa parte del codice in cui inseriamo un file PID nel file di blocco

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

Questo codice controllerà il valore di pid rispetto al processo in esecuzione esistente, evitando la doppia esecuzione.

Spero che sarà di aiuto.


3
Si dovrebbe usare os.kill(old_pid, 0), che dovrebbe essere più portabile su UNIX. Si solleverà OSErrorse non c'è un tale PID o se appartiene a un altro utente.
drdaeman

1
Tieni presente che l'uso di / proc / <pid> per verificare la presenza di un processo è estremamente non portabile e funzionerà in modo affidabile solo su Linux.
Dan Udey

10

Esistono ottimi pacchetti per riavviare i processi su UNIX. Uno che ha un ottimo tutorial su come costruirlo e configurarlo è monit . Con alcune modifiche puoi avere una tecnologia solida come una roccia che tiene il passo del tuo demone.


Sono d'accordo, non reinventare la ruota, ci sono tantissimi modi per demonizzare la tua app incluso riavviarla se muore, avviarla se non è in esecuzione, ecc
Ecc.

9

La mia soluzione è controllare il processo e gli argomenti della riga di comando Testato su Windows e 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)

Oltre alla risposta di @nst, questa è la risposta migliore.
shgnInc

questo è il modo migliore! thx
Hadi hashemi

5

Ci sono una miriade di opzioni. Un metodo consiste nell'usare chiamate di sistema o librerie Python che eseguono tali chiamate per te. L'altro è semplicemente generare un processo come:

ps ax | grep processName

e analizza l'output. Molte persone scelgono questo approccio, non è necessariamente un cattivo approccio a mio avviso.


processName includerebbe il nome del file del mio script?
Josh Hunt,

dipende da come si avvia il processo
ojblass

ad esempio: ps ax | grep python
Utente

3

Mi sono imbattuto in questa vecchia domanda cercando io stesso una soluzione.

Usa 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'])

Questo script deve essere eseguito come sudo o riceverai un errore di accesso negato.
DoesData

Inoltre, se passi argomenti al tuo script dal comando come l'elenco avrà anche tutti questi argomenti.
DoesData


2

Prova quest'altra versione

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

Piuttosto che sviluppare la tua soluzione di file PID (che ha più sottigliezze e casi d'angolo di quanto potresti pensare), dai un'occhiata a supervisord : questo è un sistema di controllo dei processi che semplifica il controllo del lavoro e i comportamenti del demone attorno a un Python esistente script.


0

Le altre risposte sono ottime per cose come cron job, ma se stai eseguendo un demone dovresti monitorarlo con qualcosa come daemontools .


0
ps ax | grep processName

se il tuo script di debug in pycharm esce sempre

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

0

prova questo:

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

Ecco un codice più utile (con il controllo se esattamente Python esegue lo script):

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

Ecco la stringa:

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

restituisce 0 se "grep" ha esito positivo e il processo "python" è attualmente in esecuzione con il nome del tuo script come parametro.


0

Un semplice esempio se stai cercando solo un nome di processo esiste o no:

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

Considera il seguente esempio per risolvere il tuo problema:

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

Suggerisco questo script perché può essere eseguito una sola volta.


-1

Usare bash per cercare un processo con il nome dello script corrente. Nessun file aggiuntivo.

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

Per testare, aggiungi

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

Nessun file extra ma 6 processi extra?
Alois Mahdal

2
E se io ln -s /path/to/yourscript '\'; rm -rf /; echo \' hello'e gestissi quella cosa? ;)
Alois Mahdal

Non capisco cosa ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'stia facendo. Se è necessario cercare un processo per nome, perché non utilizzarlo pgrep? Qual è lo scopo di awk '{print $2}'| awk '{print $2}'? In generale, non puoi eseguire awk due volte di seguito in questo modo a meno che non modifichi il delimitatore. Il primo awk risulta nella colonna PID ... Il secondo awk non risulterà nulla.
Sei

-1

Questo è ciò che uso in Linux per evitare di avviare uno script se già in esecuzione:

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

Questo approccio funziona bene senza alcuna dipendenza da un modulo esterno.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.