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?

Josh Hunt
la source
Ê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.).

Dan Udey
la source
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.

aychédée
la source
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.

Decko
la source
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.

Shylock
la source
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.

ojblass
la source
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)
kabapy
la source
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.

BobbyShaftoe
la source
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'])
NST
la source
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

Je suis un grand fan de Supervisor pour la gestion des démons. Il est écrit en Python, il existe donc de nombreux exemples sur la façon d'interagir avec ou de l'étendre à partir de Python. Pour vos besoins, l' API de contrôle de processus XML-RPC devrait fonctionner correctement.

Matt bon
la source
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)
debuti
la source
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.

Chris Johnson
la source
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 .

bobpoekert
la source
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
user3366072
la source
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)
MrRolling
la source
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.

Dmitry Allyanov
la source
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
Tomba
la source
-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.

edisonex
la source
-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)
Jérôme Jaglale
la source
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.

SH_Rohit
la source