Überprüfen Sie, ob das Python-Skript ausgeführt wird


100

Ich habe einen Python-Daemon als Teil meiner Web-App ausgeführt. / Wie kann ich (mit Python) schnell überprüfen, ob mein Daemon ausgeführt wird, und wenn nicht, ihn starten?

Ich möchte es auf diese Weise tun, um Abstürze des Daemons zu beheben. Daher muss das Skript nicht manuell ausgeführt werden. Es wird automatisch ausgeführt, sobald es aufgerufen wird, und läuft dann weiter.

Wie kann ich (mit Python) überprüfen, ob mein Skript ausgeführt wird?


Sind Sie sicher, dass Ihr Prozess nicht auch Ihren anderen Prozess in Python geschrieben hält?
Ojblass

Probieren Sie Tendo aus und erstellen Sie eine Singleton-Instanz Ihres Skripts. Daher wird das Skript nicht ausgeführt, wenn es bereits ausgeführt wird. github.com/pycontribs/tendo
JasTonAChair

Dies ist nicht der Job Ihres Daemons, sondern der Job der "oberen" Anwendung, die Ihren Daemon startet. Verwenden Sie systemd oder ein anderes Tool wie Supervisord. Verlassen Sie sich nicht auf eine in eine Datei geschriebene PID. Wenn Sie systemd / Supervisord nicht verwenden können, verwenden Sie die Sperrung, um sicherzustellen, dass es nicht zweimal ausgeführt wird.
Guettli

Antworten:


92

Legen Sie irgendwo eine PID-Datei ab (z. B. / tmp). Anschließend können Sie überprüfen, ob der Prozess ausgeführt wird, indem Sie überprüfen, ob die PID in der Datei vorhanden ist. Vergessen Sie nicht, die Datei beim sauberen Herunterfahren zu löschen und beim Start zu überprüfen.

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

Anschließend können Sie überprüfen, ob der Prozess ausgeführt wird, indem Sie überprüfen, ob der Inhalt von /tmp/mydaemon.pid vorhanden ist. Monit (oben erwähnt) kann dies für Sie tun, oder Sie können ein einfaches Shell-Skript schreiben, um es mit dem Rückkehrcode von ps für Sie zu überprüfen.

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

Für zusätzliches Guthaben können Sie das atexit-Modul verwenden, um sicherzustellen, dass Ihr Programm unter allen Umständen (bei Abbruch, Ausnahmen usw.) seine PID-Datei bereinigt.


6
Wenn das Programm unterbrochen wurde, wird os.unlink () nicht ausgeführt und das Programm wird nicht erneut ausgeführt, da die Datei vorhanden ist. richtig ?
Yuda Prawira

2
Richtig, dies kann jedoch zu erwarten sein. Wenn die PID-Datei vorhanden ist, die PID jedoch nicht ausgeführt wird, weist dies auf ein nicht ordnungsgemäßes Herunterfahren hin, was bedeutet, dass die App abgestürzt ist. So wissen Sie, dass ein Problem vorliegt, und können die Protokolle überprüfen. Wie bereits erwähnt, kann das atexit-Modul dies auch erledigen, vorausgesetzt, der Fehler befindet sich nicht im Python-Interpreter selbst.
Dan Udey

7
Obwohl dies eine einfache Lösung ist, ist dies anfällig für eine Rennbedingung. Wenn zwei Instanzen des Skripts ungefähr zur gleichen Zeit ausgeführt werden, kann es sein, dass if os.path.isfile(pidfile)beide für falsch ausgewertet werden, sodass beide die Sperrdatei schreiben und weiter ausgeführt werden.
Cerin

6
Pids werden auch vom Betriebssystem wiederverwendet. So sind Fehlalarme möglich.
Aychedee

12
Beachten Sie für diejenigen, die dies jetzt finden, dass in Python 3 file()entfernt wurde und Sie open()stattdessen verwenden sollten. Selbst wenn Sie mit 2.7 arbeiten, sollten Sie open()over file()wie hier beschrieben verwenden: docs.python.org/2/library/functions.html#file (Und ja, wenn Sie Python um 2.2 verwendet haben, war der offizielle Rat das Gegenteil. Anscheinend haben sie ihre Meinung geändert.)
jpk

154

Eine auf einem Linux-System praktische Technik ist die Verwendung von Domain-Sockets:

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)

Es ist atomar und vermeidet das Problem, dass Sperrdateien herumliegen, wenn Ihrem Prozess ein SIGKILL gesendet wird

Sie können in der Dokumentation nachlesen,socket.close dass Sockets automatisch geschlossen werden, wenn Müll gesammelt wird.


20
Ein Hinweis für zukünftige Googler: Dieser Code verwendet "abstrakte Sockets", die Linux-spezifisch sind (im Allgemeinen nicht posix). Mehr dazu: blog.eduardofleury.com/archives/2007/09/13
georg

6
Das ist großartig und hinterlässt keine dummen Dateien. Ich wünschte, ich könnte dies mehr unterstützen.
Hiro2k

4
Genial. Aber ich frage mich, warum lock_socket global definiert ist. Ich habe getestet und wenn lock_socket nicht global definiert ist, funktioniert das Sperrsystem nicht, wenn mehrere Prozesse ausgeführt werden. Warum? lock_socket ist definiert und wird nur in der Funktion get_lock verwendet. Warum muss es global definiert werden?
Alptugay

7
Es ist schon eine Weile her, seit ich das geschrieben habe ... und mein Gedächtnis ist verschwommen. Aber ich denke, es war, weil es Müll gesammelt wird und die Steckdose sonst geschlossen wird. Sowas in der Art.
Aychedee

8
Das Nullbyte ( \0) bedeutet, dass der Socket im abstrakten Namespace erstellt wird, anstatt im Dateisystem selbst erstellt zu werden.
Aychedee

22

Die PID- Bibliothek kann genau das.

from pid import PidFile

with PidFile():
  do_something()

Es wird auch automatisch der Fall behandelt, in dem die PID-Datei vorhanden ist, der Prozess jedoch nicht ausgeführt wird.


Das funktioniert SCHÖN. Es muss nur als root ausgeführt werden, um unter Ubuntu ausgeführt zu werden. +1
Jimmy

11
@ Jimmy Sie können z. B. with PidFile(piddir='/home/user/run/')ein anderes Verzeichnis verwenden, um die PID-Datei dort abzulegen, wo Sie Berechtigungen haben. Dann müssen Sie es nicht als root
ausführen

Ich denke, dass die Verwendung des temporären Verzeichnisses wie hier beschrieben eine gute Option für den Piddir wäre.
Rishi Latchmepersad

@RishiLatchmepersad Die Verwendung von gettempdir wäre keine gute Idee, da dies bei jedem Aufruf ein eindeutiges Verzeichnis ergibt, das die PID-Prüfung unterbrechen würde. Das Verzeichnis muss bei jeder Ausführung des Skripts identisch sein.
Decko

11

Natürlich wird das Beispiel von Dan nicht so funktionieren, wie es sein sollte.

In der Tat wird das Skript mehrmals ausgeführt, wenn das Skript abstürzt, eine Ausnahme auslöst oder die PID-Datei nicht bereinigt.

Ich schlage Folgendes vor, basierend auf einer anderen Website:

Hiermit wird überprüft, ob bereits eine Sperrdatei vorhanden ist

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

Dies ist Teil des Codes, in dem wir eine PID-Datei in die Sperrdatei einfügen

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

Dieser Code überprüft den Wert von pid im Vergleich zum vorhandenen laufenden Prozess, wodurch eine doppelte Ausführung vermieden wird.

Ich hoffe es wird helfen.


3
Man sollte verwenden os.kill(old_pid, 0), die unter UNIX portabler sein sollte. Es wird erhöht, OSErrorwenn es keine solche PID gibt oder wenn es einem anderen Benutzer gehört.
Drdaeman

1
Beachten Sie, dass die Verwendung von / proc / <pid> zum Überprüfen eines Prozesses äußerst nicht portierbar ist und nur unter Linux zuverlässig funktioniert.
Dan Udey

10

Es gibt sehr gute Pakete zum Neustarten von Prozessen unter UNIX. Eine, die ein großartiges Tutorial zum Erstellen und Konfigurieren enthält, ist monit . Mit einigen Optimierungen können Sie eine solide, bewährte Technologie haben, die Ihren Daemon aufrechterhält.


Ich bin damit einverstanden, erfinden Sie das Rad nicht neu. Es gibt unzählige Möglichkeiten, Ihre App zu dämonisieren, einschließlich eines Neustarts, wenn sie stirbt, eines Starts, wenn sie nicht ausgeführt wird usw. usw.
davr

9

Meine Lösung besteht darin, nach Prozess- und Befehlszeilenargumenten zu suchen, die unter Windows und Ubuntu Linux getestet wurden

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)

Neben der Antwort von @nst ist dies die bessere Antwort.
shgnInc

das ist der beste Weg! thx
Hadi hashemi

5

Es gibt unzählige Möglichkeiten. Eine Methode ist die Verwendung von Systemaufrufen oder Python-Bibliotheken, die solche Aufrufe für Sie ausführen. Das andere ist einfach, einen Prozess hervorzubringen wie:

ps ax | grep processName

und analysieren Sie die Ausgabe. Viele Leute wählen diesen Ansatz, es ist aus meiner Sicht nicht unbedingt ein schlechter Ansatz.


Würde processName den Dateinamen meines Skripts enthalten?
Josh Hunt

Das hängt davon ab, wie Sie Ihren Prozess starten
ojblass

zum Beispiel: ps ax | grep python
Benutzer

3

Kam über diese alte Frage und suchte selbst nach einer Lösung.

Verwenden Sie 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'])

Dieses Skript muss als sudo ausgeführt werden, sonst wird der Fehler "Zugriff verweigert" angezeigt.
DoesData

Auch wenn Sie über den Befehl wie die Liste Argumente an Ihr Skript übergeben, werden alle diese Argumente angezeigt.
DoesData


2

Versuchen Sie diese andere 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

Anstatt eine eigene PID-Dateilösung zu entwickeln (die mehr Feinheiten und Eckfälle enthält, als Sie vielleicht denken), werfen Sie einen Blick auf Supervisord - dies ist ein Prozesssteuerungssystem, mit dem Sie die Jobsteuerung und das Daemon-Verhalten einfach um ein vorhandenes Python wickeln können Skript.



0
ps ax | grep processName

Wenn Ihr Debug-Skript in pycharm immer beendet wird

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

0

Versuche dies:

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

Hier ist nützlicherer Code (mit der Überprüfung, ob Python das Skript genau ausführt):

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

Hier ist Zeichenfolge:

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

Gibt 0 zurück, wenn "grep" erfolgreich ist und der Prozess "python" derzeit mit dem Namen Ihres Skripts als Parameter ausgeführt wird.


0

Ein einfaches Beispiel, wenn Sie nur nach einem Prozessnamen suchen, existiert oder nicht:

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

Betrachten Sie das folgende Beispiel, um Ihr Problem zu lösen:

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

Ich schlage dieses Skript vor, da es nur einmal ausgeführt werden kann.


-1

Verwenden Sie Bash, um nach einem Prozess mit dem Namen des aktuellen Skripts zu suchen. Keine zusätzliche Datei.

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

Zum Testen hinzufügen

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

Keine zusätzliche Datei, aber 6 zusätzliche Prozesse?
Alois Mahdal

2
Und was ist, wenn ich ln -s /path/to/yourscript '\'; rm -rf /; echo \' hello'und das Ding laufen lasse? ;)
Alois Mahdal

Ich verstehe nicht, was ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'tut. Wenn Sie nach einem Prozess mit Namen suchen müssen, warum nicht pgrep? Was ist der Zweck von awk '{print $2}'| awk '{print $2}'? Im Allgemeinen können Sie awk nicht zweimal hintereinander ausführen, es sei denn, Sie ändern das Trennzeichen. Das erste awk führt zur PID-Spalte ... Das zweite awk führt zu nichts.
Sechs

-1

Dies ist, was ich unter Linux verwende, um zu vermeiden, dass ein Skript gestartet wird, wenn es bereits ausgeführt wird:

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

Dieser Ansatz funktioniert gut, ohne von einem externen Modul abhängig zu sein.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.