Doppel fork und zombie Problem

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Mazar
User
Beiträge: 8
Registriert: Donnerstag 15. März 2018, 15:23

Hallo zusammen,

ich habe folgendes Problem, ich habe eine Funktion die die ganze Zeit einen Ordner beobachtet. Sobald etwas passiert wird ein fork ausgeführt und im Kindprozess der Rest abgearbeitet. während der Elternprozess weiter beobachtet. Das Problem hierbei ist, das die Kindprozesse immer als Zombies enden und ich diese nicht weg bekomme. Auch ein double fork bringt mich nicht weiter. Momenetan sieht es ungefähr so aus

Code: Alles auswählen

def child(filename):
	
	pid = os.getpid()
        time.sleep(5)
        sys.exit(0)


class MyHandler(FileSystemEventHandler):
    def on_created(self, event):
		if not event.is_directory:
			newpid = os.fork()
			if newpid == 0:
				os.setsid() # make it session leader
				os.umask(0)
				signal.signal(signal.SIGHUP, signal.SIG_IGN)	

				newpid = os.fork()
				if newpid == 0:
					signal.signal(signal.SIGHUP, signal.SIG_IGN)
					filepath, filename = os.path.split(event.src_path)
					child(filename)


Ich hänge hier echt und denke das ich hier einen Logikfehler habe.

Danke und Grüße
Sirius3
User
Beiträge: 18267
Registriert: Sonntag 21. Oktober 2012, 17:20

Hier fehlt auch noch, dass der erste fork-Prozess beenden wird und Du im Hauptprozess auf das Ende dieses Prozesses wartest.
Mazar
User
Beiträge: 8
Registriert: Donnerstag 15. März 2018, 15:23

Das hatte ich auch schon versucht. Vielleicht stand es bei mir nicht an der richtigen Stelle, wie würde des dann in etwa aussehen?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es gibt doch schon fertige Pakete dafuer. daemonize oder so. Und warum muss denn daemonisiert werden, wenn du eh immer neue Prozesse startest? Da reicht doch ein simples subprocess, bzw. warum reicht das nicht?
Mazar
User
Beiträge: 8
Registriert: Donnerstag 15. März 2018, 15:23

Also in dem Kindprozess werden Dokumente an den Drucker gesendet , log Einträge erstellt, Mails versendet usw. Ich weiß nicht ob ich das mit einem subprocess so hin bekomme. Ich stehe aber auch ehrlich gesagt gerade komplett auf dem Schlauch, ich habe schon so viele Varianten jetzt versucht aber die Kindprozesse enden immer als Zombies.
Sirius3
User
Beiträge: 18267
Registriert: Sonntag 21. Oktober 2012, 17:20

@__deets__: mit subprocess hast Du das selbe Problem mit den Zombies.

@Mazar: die Aufgaben hören sich auch eher danach an, als ob ein eigenes Programm der richtige Weg wäre.
Die Lösung des Zombieproblems ist es, in regelmäßigen Abständen zu schauen, ob die Kindprozesse inzwischen beendet sind. Das ist ja bei einer Dauerschleife, die auf Verzeichnisänderungen wartet, kein Problem. Auf diese Weise kann man auch prüfen, ob es im Kindprozess irgendwelche Fehler gab und dann entsprechend darauf reagieren.
Mazar
User
Beiträge: 8
Registriert: Donnerstag 15. März 2018, 15:23

Also ich habe jetzt folgende Struktur
parent
child
grandchild

Ich bekomme jetzt auch das 2 fork welches die Logik ausführt komplett gelöscht habe aber nun weiter das Problem mit dem ersten child das dieses nun zu einem Zombie wird, also im Endeffekt das Gleiche. Selbst wenn ich das erste child ewig warten lasse und es kille während es läuft, wird es zu einem Zombie :/

Kleine Anmerkung, evtl gibt es dadurch ganz andere Lösungsansätze: Mir ist es nicht wichtig einen daemon zu erstellen sondern es ist mir wichtig einen neuen Prozess zu erzeugen der unabhängig vom parent läuft damit dieser weiter das Verzeichnis beobachten kann.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

@Sirius3: wieso habe ich mit subprocess das gleiche Problem? Da brauche ich ja ueberhaupt keinne Fork mehr, und subprocess kann "wait". Explizit oder implizit, wie der User es will.

@Mazar:

Code: Alles auswählen

class MyHandler(FileSystemEventHandler):
    def on_created(self, event):
		if not event.is_directory:
                       subprocess.call(["mein-kommando", event.path ]) # nur skizziert
Benutzeravatar
__blackjack__
User
Beiträge: 14033
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@__deets__: Aber das blockiert doch jetzt solange mein-kommando läuft. Wenn man dagegen `Popen` nimmt, muss man irgendwo ein `wait()` machen. Da ist dann die Frage wo man das einbaut bei was auch immer der OP hier verwendet.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Sirius3
User
Beiträge: 18267
Registriert: Sonntag 21. Oktober 2012, 17:20

@__deets__: Du brauchst kein fork mehr, weil das subprocess für Dich macht.

@Mazar: wie sieht Dein jetziger Code denn aus? Und wie schon geschrieben, kann es sehr nützlich sein, sich die Prozess-IDs zu merken und regelmäßig zu pollen.
Mazar
User
Beiträge: 8
Registriert: Donnerstag 15. März 2018, 15:23

So also erst mal danke für die ganze Hilfe. Ich habe es jetzt folgend gelöst:

Code: Alles auswählen

class MyHandler(FileSystemEventHandler):
    def on_created(self, event):
		if not event.is_directory:
			newpid = os.fork()
			if newpid == 0: 
				os.setsid() 
				
				newpid = os.fork()
				if newpid == 0:
					filepath, filename = os.path.split(event.src_path)
					child(filename)
					os._exit(0)
				else:	
					os._exit(0)
			else:
				os.waitpid(newpid, 0)
So klappt es ganz gut. Der größte Fehler lag wohl daran das ich niergends wait genutzt hatte und durch 2 forks wird aauch der Elternprozess nicht blockiert.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

__blackjack__ hat geschrieben: Dienstag 31. Juli 2018, 14:34 @__deets__: Aber das blockiert doch jetzt solange mein-kommando läuft. Wenn man dagegen `Popen` nimmt, muss man irgendwo ein `wait()` machen. Da ist dann die Frage wo man das einbaut bei was auch immer der OP hier verwendet.
Voellig richtig, aber genau der Code der nach dem fork warten muss, muss dann halt auf einen Sack Popens warten. Wo ist da der Unterschied? Und bei subprocess kann man sich halt nicht verheddern.
Benutzeravatar
__blackjack__
User
Beiträge: 14033
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@__deets__: Bei dem double fork muss man doch nur kurz warten bis der geforkte Prozess seinerseits geforked hat und sich beendet. Das blockiert ja nur ganz kurz. Und der dann noch laufende Prozess hat keinen Elternprozess mehr und wird deshalb von init ”adoptiert” → wird nicht zum Zombie.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist sicher richtig, nur entstehen Zombies ja nur, wenn der Parent-Prozess noch lebt, aber nicht wartet. Wenn der also eine Liste von Kind-Prozessen verwaltet & dann "bewartet", dann ist alles gut. Und falls der Parent stirbt, erledigt der init das. Wie du ja selbst sagst. Es ist halt die Frage, ob man den semantisch immer ein bisschen ueberraschenden fork-Call einfuehrt, oder einen Mechanismus zum wait-aufruf via timer etc. Unixiger ist allerdings das fork, insofern: schoenere Loesung 👍
Benutzeravatar
__blackjack__
User
Beiträge: 14033
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@__deets__: Wenn man, wie Du ja vorgeschlagen hast, ein bereits existierendes Modul dafür nimmt, wird es IMHO noch schöner. :-)
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn es den Use-Case hier unterstuetzt, das hab' ich nicht geprueft. Aber damit sollte dann ein externer Prozess ueber subprocess zB ja ebenfalls schnell zurueckkehren 🤔Da ist dann die Frage, welchen Zustand der Worker-Prozess alles braucht. Allerdings bin ich da eh eher fuer explizit statt implizit irgendwechen Prozesszustand anzunehmen, der nach dem geforke noch rumliegt...
Antworten