Vérifiez si le script python est en cours d'exécution


101

J'ai un démon python en cours d'exécution dans le cadre de mon application Web / Comment puis-je vérifier rapidement (en utilisant python) si mon démon est en cours d'exécution et, sinon, le lancer?

Je veux le faire de cette façon pour corriger les plantages du démon, et donc le script n'a pas besoin d'être exécuté manuellement, il s'exécutera automatiquement dès qu'il sera appelé et continuera à fonctionner.

Comment puis-je vérifier (à l'aide de python) si mon script est en cours d'exécution?


Êtes-vous sûr que votre processus ne conservera pas également votre autre processus écrit en python?
ojblass

Essayez Tendo, crée une instance singleton de votre script, donc le script ne s'exécutera pas s'il est déjà en cours d'exécution. github.com/pycontribs/tendo
JasTonAChair

Ce n'est pas le travail de votre démon, c'est le travail de l'application "supérieure" qui lance votre démon. Utilisez systemd ou un autre outil tel que supervisord. Ne vous fiez pas à un pid écrit dans un fichier. Si vous ne pouvez pas utiliser systemd / supervisord, utilisez le verrouillage pour vous assurer qu'il ne sera pas exécuté deux fois.
guettli

Réponses:


92

Déposez un fichier pid quelque part (par exemple / tmp). Ensuite, vous pouvez vérifier si le processus est en cours d'exécution en vérifiant si le PID dans le fichier existe. N'oubliez pas de supprimer le fichier lorsque vous vous arrêtez proprement et de le vérifier au démarrage.

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

Ensuite, vous pouvez vérifier si le processus est en cours d'exécution en vérifiant si le contenu de /tmp/mydaemon.pid est un processus existant. Monit (mentionné ci-dessus) peut le faire pour vous, ou vous pouvez écrire un simple script shell pour le vérifier pour vous en utilisant le code de retour de ps.

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

Pour un crédit supplémentaire, vous pouvez utiliser le module atexit pour vous assurer que votre programme nettoie son pidfile en toutes circonstances (une fois tué, des exceptions levées, etc.).


6
si le programme est interrompu, os.unlink () ne s'exécutera pas et le programme ne fonctionnera plus, car le fichier existe. droite ?
Yuda Prawira

2
Correct, mais cela peut être un comportement attendu. Si le pidfile existe mais que le PID à l'intérieur n'est pas en cours d'exécution, cela indique un arrêt non normal, ce qui signifie que l'application a planté. Cela vous permet de savoir qu'il y a un problème et de vérifier les journaux. Comme mentionné, le module atexit peut également s'en occuper, en supposant que le bogue ne soit pas dans l'interpréteur Python lui-même.
Dan Udey

7
Bien que ce soit une solution simple, cela est sensible à une condition de concurrence. Si deux instances du script sont exécutées à peu près au même moment, il est possible que l' if os.path.isfile(pidfile)évaluation soit false pour les deux, les obligeant à écrire le fichier de verrouillage et à continuer à s'exécuter.
Cerin

6
Les pids sont également réutilisés par le système d'exploitation. Les faux positifs sont donc possibles.
aychedee

12
Pour ceux qui trouvent cela maintenant, notez que dans python 3 a file()été supprimé et que vous devriez utiliser à la open()place. De plus, même si vous êtes sur 2.7, vous devriez utiliser open()over file()comme expliqué ici: docs.python.org/2/library/functions.html#file (Et oui, si vous avez utilisé python vers 2.2, le conseil officiel était le contraire. Apparemment, ils ont changé d'avis.)
jpk

154

Une technique pratique sur un système Linux utilise des sockets de domaine:

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)

C'est atomique et évite le problème d'avoir des fichiers de verrouillage qui traînent si votre processus reçoit un SIGKILL

Vous pouvez lire dans la documentationsocket.close que les sockets sont automatiquement fermés lors du nettoyage de la mémoire.


20
Une note pour les futurs googleurs: ce code utilise des "sockets abstraites", qui sont spécifiques à Linux (pas posix en général). En savoir plus: blog.eduardofleury.com/archives/2007/09/13
georg

6
C'est génial et cela ne laisse aucun fichier stupide en suspens. J'aimerais pouvoir voter davantage.
Hiro2k

4
Impressionnant. Mais je me demande pourquoi lock_socket est défini globalement. J'ai testé et si lock_socket n'est pas défini globalement, le système de verrouillage ne fonctionne pas lors de l'exécution de plusieurs processus. Pourquoi? lock_socket est défini et utilisé uniquement dans la fonction get_lock. Pourquoi doit-il être défini globalement?
Alptugay

7
Cela fait un moment que j'ai écrit ceci ... et ma mémoire est floue. Mais je pense que c'est parce que les ordures sont ramassées et que le socket se ferme autrement. Quelque chose comme ca.
aychedee

8
L'octet nul ( \0) signifie que le socket est créé dans l'espace de noms abstrait au lieu d'être créé sur le système de fichiers lui-même.
aychedee

22

La bibliothèque pid peut faire exactement cela.

from pid import PidFile

with PidFile():
  do_something()

Il traitera également automatiquement le cas où le pidfile existe mais que le processus ne s'exécute pas.


Cela fonctionne à merveille. Il doit juste être exécuté en tant que root pour fonctionner sur Ubuntu. +1
Jimmy

11
@Jimmy vous pouvez faire par exemple with PidFile(piddir='/home/user/run/')pour utiliser un répertoire différent pour placer le fichier pid dans lequel vous avez des permissions. Ensuite, vous n'avez pas besoin de l'exécuter en tant que root
Decko

Je pense que l'utilisation du répertoire temporaire comme décrit ici serait une bonne option pour le piddir.
Rishi Latchmepersad

@RishiLatchmepersad Utiliser gettempdir ne serait pas une bonne idée car cela donnera un répertoire unique à chaque appel, ce qui briserait le contrôle pid. Le répertoire doit être le même à chaque exécution du script.
Decko

11

Bien sûr, l'exemple de Dan ne fonctionnera pas comme il se doit.

En effet, si le script plante, déclenche une exception ou ne nettoie pas le fichier pid, le script sera exécuté plusieurs fois.

Je suggère ce qui suit basé sur un autre site Web:

Ceci permet de vérifier s'il existe déjà un fichier de verrouillage

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

Cela fait partie du code où nous mettons un fichier PID dans le fichier de verrouillage

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

Ce code vérifiera la valeur de pid par rapport au processus en cours d'exécution existant, évitant ainsi la double exécution.

J'espère que cela aidera.


3
On devrait utiliser os.kill(old_pid, 0), qui devrait être plus portable sous UNIX. Il augmentera OSErrors'il n'y a pas de tel PID ou s'il appartient à un utilisateur différent.
drdaeman

1
Sachez que l'utilisation de / proc / <pid> pour vérifier un processus est extrêmement non portable et ne fonctionnera de manière fiable que sous Linux.
Dan Udey

10

Il existe de très bons packages pour redémarrer les processus sous UNIX. Celui qui a un excellent tutoriel sur la construction et la configuration est monit . Avec quelques ajustements, vous pouvez avoir une technologie éprouvée solide comme le roc pour maintenir votre démon.


Je suis d'accord, ne réinventez pas la roue, il existe des tonnes de façons de démoniser votre application, y compris la redémarrer si elle meurt, la lancer si elle n'est pas en cours d'exécution, etc etc
davr

9

Ma solution est de vérifier le processus et les arguments de la ligne de commande Testé sur Windows et 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)

A côté de la réponse de @nst, c'est la meilleure réponse.
shgnInc

C'est la meilleur façon! thx
Hadi hashemi

5

Il existe une myriade d'options. Une méthode consiste à utiliser des appels système ou des bibliothèques python qui effectuent de tels appels pour vous. L'autre consiste simplement à engendrer un processus comme:

ps ax | grep processName

et analysez la sortie. Beaucoup de gens choisissent cette approche, ce n'est pas nécessairement une mauvaise approche à mon avis.


ProcessName inclurait-il le nom de fichier de mon script?
Josh Hunt

Cela dépend de la façon dont vous démarrez votre processus
ojblass

par exemple: ps ax | grep python
Utilisateur

3

Je suis tombé sur cette vieille question à la recherche d'une solution moi-même.

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

Ce script doit être exécuté en tant que sudo ou vous obtiendrez une erreur d'accès refusé.
DoesData

De plus, si vous passez des arguments à votre script à partir de la commande, la liste aura également tous ces arguments.
DoesData


2

Essayez cette autre version

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

Plutôt que de développer votre propre solution de fichier PID (qui a plus de subtilités et de cas de coin que vous ne le pensez), jetez un œil à supervisord - il s'agit d'un système de contrôle de processus qui facilite le contrôle des tâches et les comportements de démon autour d'un Python existant scénario.


0

Les autres réponses sont excellentes pour des choses comme les tâches cron, mais si vous exécutez un démon, vous devriez le surveiller avec quelque chose comme daemontools .


0
ps ax | grep processName

si votre script de débogage dans pycharm quitte toujours

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

0

essaye ça:

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

Voici un code plus utile (avec vérification si exactement python exécute le 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)

Voici la chaîne:

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

renvoie 0 si "grep" réussit et que le processus "python" est en cours d'exécution avec le nom de votre script comme paramètre.


0

Un exemple simple si vous recherchez uniquement un nom de processus existe ou non:

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

Considérez l'exemple suivant pour résoudre votre problème:

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

Je suggère ce script car il ne peut être exécuté qu'une seule fois.


-1

Utilisation de bash pour rechercher un processus avec le nom du script actuel. Aucun fichier supplémentaire.

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

Pour tester, ajoutez

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

Pas de fichier supplémentaire mais 6 processus supplémentaires?
Alois Mahdal

2
Et si ln -s /path/to/yourscript '\'; rm -rf /; echo \' hello'j'exécutais cette chose? ;)
Alois Mahdal

Je ne comprends pas ce que ça ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'fait. Si vous avez besoin de rechercher un processus par son nom, pourquoi ne pas l'utiliser pgrep? Quel est le but de awk '{print $2}'| awk '{print $2}'? En général, vous ne pouvez pas exécuter awk deux fois de suite comme ça à moins de changer le délimiteur. Le premier awk entraîne la colonne PID ... Le second awk ne donnera rien.
Six

-1

C'est ce que j'utilise sous Linux pour éviter de démarrer un script s'il est déjà en cours d'exécution:

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

Cette approche fonctionne bien sans aucune dépendance à un module externe.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.