Seite 1 von 1

bereits laufende Instanzen erkennen?

Verfasst: Montag 20. Februar 2006, 16:50
von magnat
Hallo zusammen,

wie kann ich in einem Skript erkennen, ob dieses bereits als Prozess ausgeführt wird? Und wie kann ich dieser bereits laufenden Instanz bestimmte Daten übergeben?

Anwendung ist folgende:

Das Skript wird mit Kommandozeilen-Argumenten aufgerufen und arbeitet diese ab (dauert längere Zeit). Wird das Skript erneut aufgerufen, soll nun nicht eine parallele Abarbeitung erfolgen, sondern eine Übergabe der "neuen" Argumente an das bereits laufende Skript stattfinden, das wiederum die neu angekommenen Argumente in seine Abarbeitungsliste einreiht (Spooler).

Vielen Dank für eure Ideen

magnat[/code]

Re: bereits laufende Instanzen erkennen?

Verfasst: Montag 20. Februar 2006, 17:12
von gerold
magnat hat geschrieben: Und wie kann ich dieser bereits laufenden Instanz bestimmte Daten übergeben?
Hi magnat!

Natürlich gibt es dafür viele Wege. Mir fallen spontan zwei ein. Einmal könntest du dein Programm als XML-RPC-Server arbeiten lassen. Zuerst wird geprüft ob bereits ein Programm den TCP-Port belegt. Wenn nicht, dann wird das laufende Skript zum Server (mit Threads). Ist der Port bereits belegt oder wurde das laufende Skript zum Server, dann können die Daten per XML-RPC an den Server weitergereicht werden. Diese Daten können in eine "Queue" gelegt und nacheinander abgearbeitet werden.

Die andere Variante läuft mit Dateien in einem Ordner. Jedes Skript legt die Daten in eine Datei, die mit dem exakten Zeitpunkt benannt wird, in einen Ordner ab. Danach wird geprüft, ob eine besonders benannte Datei im Ordner existiert. Wenn nicht, dann wird eine besonders benannte Datei im Ordner erstellt, welche allen anderen Skripten mitteilt, dass sich ab jetzt schon ein Prozess um die Abarbeitung kümmert. Danach läuft das Skript alle (nicht mit Lockdateien gesperrte) Datendateien durch. Gibt es keine ungesperrten Dateien mehr, beendet sich das Skript. So oder so ähnlich ;-)

Es gibt sicher elegantere Methoden.

mfg
Gerold
:-)

Verfasst: Montag 20. Februar 2006, 19:24
von magnat
Vielen Dank erstmal,

die Idee über Dateien zu kommunizieren hatte ich auch bereits, aber wie du bereits gesagt hast gibts da sicher elegantere Lösungen.
Andererseits finde ich den Datenaustausch via TCP etwas zu aufwendig - ich dachte eher an eine Abfrage aller laufender Prozesse und Identifikation über den zur PID zugehörigen Namen. Geht das?
Und ist es möglich, eine Pipe zu einem bereits laufendem Prozess zu legen? (Unter OS9000 ist sowas kein Problem...) Leider ist os.popen*() dafür wohl nicht geeignet.

Grüße

M.

Verfasst: Sonntag 26. Februar 2006, 20:18
von Kompottkin
Ob Du mit laufenden Programmen via Pipes kommunizieren kannst, hängt natürlich vom Betriebssystem ab.

Ich weiß nicht, ob POSIX diese Möglichkeit erwähnt, aber GNU/Linux unterstützt etwas, das man named pipes nennt. Das sind Pipes, die im Dateisystem als spezielle Dateien erscheinen. Du kannst sie problemlos unter /tmp ablegen, wie viele andere Anwendungen es tun. Am besten dekorierst Du den Namen der Pipe einfach mit der PID Deines Prozesses und irgend einem Präfix. Zum Beispiel: /tmp/turbomagic-5918.

Das vereinfacht natürlich auch das Auffinden eines schon laufenden Prozesses: Du schaust einfach nach, ob so eine Datei schon existiert und noch mit einem Prozeß verbunden (d.h. nicht geschlossen) ist (keine Ahnung, ob das überhaupt geht...).

Verfasst: Sonntag 26. Februar 2006, 20:25
von Kompottkin
Ich habe ein bißchen gegooglet und bin zu dem Schluß gekommen, daß named pipes tatsächlich in POSIX spezifiziert sind. Der zugehörige Syscall heißt mkfifo und existiert mit genau diesem Namen auch in der Pythonstandardbibliothek im os-Modul. Seine Benutzung wird unter http://docs.python.org/lib/os-file-dir.html kurz erklärt.

Verfasst: Montag 27. Februar 2006, 08:57
von modelnine
Named Pipes sind eine Möglichkeit um mit einem laufenden Prozess zu kommunizieren, wobei ich fraglich finde ob es die effektivste Möglichkeit ist.

Um zu erkennen ob ein Prozess bereits läuft ist unter Unix folgender Code (der advisory locking benutzt) hilfreich:

Code: Alles auswählen

import os, atexit, fcntl

PIDFILEFD = None

# Öffnet die PID-Datei und gibt die PID des bestehenden Prozesses zurück falls
# dieser existiert, oder hält selbst das Lock auf den PIDFILE und gibt None zurück.
# In diesem Fall existierte kein Prozess.
def openpidfile(pidfile):
    if PIDFILEFD is not None:
        raise RuntimeError("Wir haben schon eine PID-Datei geöffnet!")
    # Diese Operation erstellt die Datei ohne eine bestehende truncaten
    # (wie bei open(...,"w")), deswegen lowlevel IO.
    PIDFILEFD = os.open(pidfile,os.O_RDWR|os.O_CREAT)
    try:
        # Wenn exklusives Locking geht läuft der Prozess noch nicht.
        fcntl.flock(PIDFILEFD,fcntl.LOCK_EX|fcntl.LOCK_NB)
    except IOError:
        # Der Prozess läuft und hat ein shared lock auf die Datei, was wir auch machen
        # können, und dadurch die PID aus der Datei auslesen. Das shared-lock kann
        # auch ruhig blockieren, das bedeutet nur dass der Prozess vor uns noch ein
        # exklusives lock hat, und wir warten dann bis er es freigibt.
        fcntl.flock(PIDFILEFD,fcntl.LOCK_SH)
        # PIDs sind maximal 10 Zeichen lang als Dezimalzahl (oder noch weniger
        # sogar).
        try:
            return int(os.read(PIDFILEFD,10))
        finally:
            # Hier könnte man auch den File offen lassen mit dem advisory lock, daraus
            # würde sich ergeben dass ein neuer Prozess so lange nicht gestartet werden
            # kann bis dieses Script sich beendet. Das ist aber im Normalfall nicht
            # notwendig.
            fcntl.flock(PIDFILEFD,fcntl.LOCK_UN)
            os.close(PIDFILEFD)
            PIDFILEFD = None
    # Wir haben das exklusive Lock, und schreiben jetzt die PID in die Datei.
    os.ftruncate(PIDFILEFD,0)
    os.write(PIDFILEFD,str(os.getpid()))
    # Und geben das lock frei auf LOCK_SH, so dass andere Prozesse merken dass
    # wir da sind und die PID lesen können.
    fcntl.flock(PIDFILEFD,fcntl.LOCK_SH)
    atexit.register(_closepidfile)

# Das folgende ist nicht _umbedingt_ notwendig da die Ressourcen eh freigegeben
# werden wenn der Prozess vom Betriebssystem zerstört wird, ist aber zumindest
# höflich die Dinge selbst freizugeben.
def _closepidfile():
    fcntl.flock(PIDFILEFD,fcntl.LOCK_UN)
    os.close(PIDFILEFD)
Der Code ist race-frei. :-) (wobei es natürlich sein kann dass der Prozess schon terminiert hat obwohl Du noch eine "alte" PID von openpidfile bekommen hast, aber das mußt Du dann in der Anwendung selbst handlen, es kann den anderen Prozess ja nicht blockieren in der Zeit die es dauert um eine bestehende PID zu handlen) Für mehr Informationen zu flock kann ich nur man 2 flock empfehlen.

Die Kommunikation könntest Du dann zum Beispiel, da Du ja jetzt die PID des alten Prozesses kennst, über Signale erledigen, die den Prozess dazu veranlassen eine Konfigurationsdatei zu lesen o.Ä.

Das ist eigentlich die normale Unix-Herangehensweise.