Blocking.pm - laufender Fork verhindert ein "shutdown restart"-Neustart

Begonnen von Markus Bloch, 22 März 2013, 18:22:33

Vorheriges Thema - Nächstes Thema

Markus Bloch

Hallo Rudi,

tut mir leid, das ich schonwieder mit einem Problem bei Blocking.pm ankomme. Diesmal entspringt es aus dem folgenden Thread: Link

Zusammengefasst ist das Problem das folgende:

Sobald eine Funktion mit BlockingCall() gestartet wird und ein wenig brauch (z.B. > 10 Sekunden), ist das ja soweit kein Problem. Problematisch wird es, wenn man in genau dieser Zeit, wo der Fork die Funktion abarbeitet ein "shutdown restart" durchführen will, kann FHEM nicht automatisch gestartet werden, da ja zu diesem Zeitpunkt noch der Fork aktiv ist und FHEM am starten hindert.

Ich vermute es liegt daran, dass die Ports für Telnet und Web nicht geöffnet werden können, da sie ja noch von dem Fork belegt sind.

Leider hab ich für diesen Fall keinen wirklich einfachen Vorschlag wie man das ganze schlau lösen könnte.

Vielleicht hast du ja eine Idee.

Vielen Dank dennoch.

Viele Grüße

Markus
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

Dirk

Mi Marco,

das ist mir auch hin und wieder beim Testen passiert.
Meine Idee währ, dass sich FHEM die PID's seiner Kinder merkt und bei einem Shutdown diese gezielt terminiert.

Das hilft allerdings bei einem abgestürzten Elternprozess auch nix. Daher könnte man überlegen ob ein Kindprozess z.B. alle 10 Sekunden nachschaut ob sein Elternprozess noch lebt und sich sonst eben auch terminiert.

Gruß
Dirk

justme1968

oder die sockets im child zu machen wie es sich eigentlich gehört...
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Dirk

Dazu müsste der Child aber vom beenden des Parent was mitbekommen.
Die Sockets vom Child vom Parent aus zu schließen wir schwierig.
Und eine Kommunikation zwischen den Prozessen geht derzeit nur vom Client zum Parent.

Ansonsten gebe ich dir aber recht.

rudolfkoenig

BlockingCall muess dem Aufrufer das pid des Kindes zurueckliefern, und der Aufrufer muss im UndefFn des Moduls BlockingKill mit diesem zurueckgelieferten pid aufrufen. Wenn keine Einwaende kommen, dann baue ich das so ein.

justme1968

die sockets muessen natuerlich direkt nach dem fork zu gemacht werden. es gibt keinen grund der mir einfaellt im child das gleiche ende des telnet sockets wie im parent offen zu halten. prinzipiell kann alles moegliche schief gehen wenn ports und filedescriptoren in parent und child offen gehalten werden und es ist eher gluecklicher zufall das hier nicht noch mehr schief geht.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Dirk

@Rudi +1

@justme1968
Der Parent muss aber weiter von den Sockets lesen können. Also darf man diese Sockets nicht schließen.
Der Child ließt derzeit nicht von den Sockets. Scheibt ggf. aber schon.
Das ist also nicht so einfach wie gedacht.
Ein Kill ist sicher nicht so elegant, vermutlich aber erst einmal leichter unzusetzen.

justme1968

doch. natuerlich ist es so einfach.

jedes socket hat zwei enden. eins zum lesen und zum schreiben. nach dem fork sind jeweils im parent und im child beide enden offen. wenn mit dem socket zwischen parent und child kommuniziert werden soll muss jeweils im im parent und im child eines der beiden enden so  geschlossen werden das die kommunikation in eine richtung möglich ist. wenn in beide richtungen kommuniziert werden soll sind zwei sockets nötig. genau dafür gibt es socketpair().

das schließen betrifft ansonsten auch alle anderen filedescriptoren. jeder filedescriptor darf nur entweder im parent oder im child offen bleiben. nicht in beiden.


das mit dem beenden der child prozesse wenn der parent beendet wird ist davon völlig unabhängig und natürlich auch nötig.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Dirk

um nochmal zu deiner Ausgangsidee zurück zu kommen:
Zitatoder die sockets im child zu machen wie es sich eigentlich gehört...
Nach dem Forken willst du im Child und im Parent die Sockets bzw. eines der Enden schliessen.
Wie ermittelst du welches Ende von welchem Socket im Child bzw. im Parent noch gebraucht wird?
Es geht ja nicht um den Socket mit dem Child und Parent kommunizieren, sondern um alle anderen.
Das stelle ich mir nicht so einfach vor.

justme1968

doch. es ist wirklich so einfach.

wenn es um parent und child geht macht man es in parent und child jeweils spiegelverkehrt. du hast den code ja auf beiden seiten unter kontrolle.

wenn es um alles andere geht wird einfach alles zu gemacht. der denkfehler ist das man im child nichts zu machen darf weil dann der parent nicht mehr kommunizieren kann. das ist aber gerade falsch. im moment des fork werden alle filedescriptoren kopiert. wenn danach auf einer seite etwas zu gemacht wird hat das keine auswirkung auf die andere seite mehr. das was beide noch teilen ist der endpunkt des sockets oder filedescriptors im kernel. die jeweiligen enden im prozess sind sind völlig unabhängig und beeinflussen sich nicht mehr gegenseitig.

wenn im child etwas zu gemacht wird ist die kopie im parent trotzdem noch offen und arbeitet wie gewohnt. genau das ist ja im umgekehrten fall das problem mit dem belegten telnet port. der wird beim beenden im parent zu gemacht ist aber im child noch offen. der endpunkt im kernel wird aber erst in dem moment zugemacht und die ressourcen freigegeben wenn alle filedescriptoren in allen prozessen zu gemacht worden sind und das muss jede prozess selbständig machen.

stell es dir als rohr zwischen prozess und kernel vor. nach dem fork wird daraus ein Y. ein ende in jedem prozess und eins im kernel. wenn ein ende in einem prozess zu gemacht wird bekommt der andere prozess davon nichts mit und der andere ast des Y im anderen prozess bleibt natürlich noch offen und verhindert das die Ressourcen im kernel (in diesem fall der telnet port) freigegeben wird.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Markus Bloch

Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

rudolfkoenig

Ich muss mich etwas korrigieren: das beschriebene Vorgehen war auch bis jetzt moeglich, da in der alten Version BlockingCall das pid zurueckgeliefert hat, und BlockingKill das pid haben wollte.

In der neuen Version liefert BlockingCall ein Hash zurueck, da BlockingKill mehr haben will (neben pid auch noch finishFn). Fuer die Module sollte es egal sein: Rueckgabewert von BlockingCall merken, und im UndefFn BlockingKill mit diesem Wert aufrufen. Und darauf achten, dass nur auf eigene Kinder geschossen wird (im finishFn Rueckgabewert loeschen), man will ja keine fremden Treffen.
Oder war das jetzt politisch unkorrekt? :)

Markus Bloch

Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

justme1968

auch auf die gefahr hin das es nervt...

das diverse server ports nach dem fork nicht geschlossen werden ist ein prinzipieller fehler der behoben werden sollte. das dies bis jetzt nicht schon zu mehr problemen geführt hat liegt nur daran das BlockingCall noch recht neu ist und noch nicht für wirklich lange laufende oder komplexe hintergrund aufgaben verwendet wird. dieses problem betrifft im übrigen nicht nur den telnet port alle offenen server sockets so z.b. auch die web ports.

der patch beim beenden von fhem die clients zu beenden ist hiervon völlig unabhängig und trotzdem nötig. dies hilft aber weder wenn fhem abstürzt noch wenn in einem child prozess selber eine main loop mit select abgearbeitet werden soll.

das rumbasteln an der selectlist ist auch wieder nur ein rumbasteln an den symtpomen.

beispiele für szenarien die nur nach reparatur dieses fehlers einfach möglich sind:
- forken eines OWServer um das pollen des 1-wire busses im hintergrund zu machen ohne das die interaktivität von fhem beeinträchtigt wird
- module die zur zeit nur über threads realisierbar sind. z.b. sonos
- module selber aktiv per select warten müßen
- module die nicht pro kommando ein child forken sondern nur ein mal und dieses länger laufen lassen
- das HM485

anbei ein kleiner patch der nach dem fork alle server ports schließt. damit treten dann die oben genannten probleme alle nicht mehr auf.

das gleiche ist auch für alle anderen filedescriptoren noch nötig. z.b. die geöffneten culs.

gruss
andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Dirk

Also doch alle Ports, nicht nur Telnet?
Ich probier das hier nachher mal aus.
Müssen denn jetzt alle Patches auch aus Link zusammen geführt werden?


justme1968

die patches aus den beiden threads sind unabhängig und haben nichts mit einander zu tun.  es ist nur deshalb die gleiche gegend weil ich beim suchen früher gestolpert bin.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

> das diverse server ports nach dem fork nicht geschlossen werden ist ein prinzipieller fehler der behoben werden sollte.

Sehe ich anders: bitte client==Kind und server==Hauptprozess nicht verwechseln.
Das Client liest von keinem dieser Ports/FDs, insofern stoert es nicht. Selectlist spielt nur im Server ein Rolle, und nur dann, wenn das Lesen vom Client uebernommen werden soll.

justme1968

ich verwechsle sicher nichts und mir ist der unterschied zwischen parent/child und client/server sehr geläufig.

gerade deshalb sehe ich das hier ein problem schlummert das immer wieder zum vorschein kommen wird wenn die verwendungszwecke für das BlockingCall komplexer werden. erst recht wenn nicht dokumentiert ist was ein child prozess darf und was nicht und es für diese einschränkungen keinen wirklich zwingend grund gibt. auch gibt es prinzipiell keine harte 1:1 zuordnung sondern ein child prozess von fhem kann durch aus server für weitere eigene child prozesse sein und nur gegenüber fhem weiter client.

es sind zwei probleme die miteinander zu tun haben weil beide am ende über den gleichen mechanismus (filedescriptoren) ressourcen blockieren und die korrekte lösung die gleiche ist: alle nicht benötigten filedescriptoren sind zu schließen und damit die ressourcen freizugeben.

zum einen die offenen ports: es ist richtig das diese erst mal keine probleme machen weil der child prozess nicht davon liest. es ist aber trotzdem unsauber sie offen zu lassen. und die probleme beim shutdown restart sind doch genau der beweis dafür. wenn sie geschlossen werden gibt es diese probleme nicht mehr! die child prozesse zu beenden wenn der parent prozess beendet wird ist richtig und nötig, hilft aber nur so lange alles sauber läuft. es hilft weder wenn der parent abstürzt noch wenn der child prozess z.b. in einem blockierenden lesen über nfs fest steckt.

zum anderen die offen filedescriptoren: auch hier ist es richtig das die erst mal keine probleme machen so lange der child prozess nicht von ihnen liest. aber es gibt sehr viele anwendungsfälle wo ein child prozess sinnvoller weise mehr machen könnte als nur warten und einen wert zurzeck zu geben und dabei ist das select absichtlich oder nebenbei aufgerufen sehr wohl ein thema.

vielleicht übersehe ich etwas aber es ist für mich völlig unverständlich warum so hartnäckig an offensichtlich falschem verhalten festgehalten wird das keinerlei vorteile bringt sondern statt dessen unter allen möglichen randbedingungen probleme verursachen kann und es auch tut. der patch ist 5 zeilen lang und schafft das problem (und alle damit in zusammenhang stehenden) ein für alle mal aus der welt.

ich denke es ist wichtig defensiv zu programmieren und mögliche fehlerquellen von vornherein auszuschliessen. erst recht wenn das mit so geringem aufwand möglich ist.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

>  für mich völlig unverständlich warum ...

Weil der Patch keinen mir bekannten Problem loest. Wenn das child weiterlebt, waehrend das parent weg ist, dann finde ich sollte fhem lieber nicht gestartet werden koennen.

justme1968

in dem fall nicht zu starten ist ein verhalten das sicher sinnvoll ist. aber das sollte nicht nebenbei über den seiteneffekt eines unsauberen verhaltens erreicht werden.

ressourcen nicht freizugeben ist nicht korrekt und es wird wieder zu problemen führen. es ging mir nicht nur darum ein problem zu lösen sondern problemen vorzubeugen.

aber es ist dein projekt und deine entscheidung welche fehler drin bleiben.

nur weil ich bis jetzt jedes mal zu faul war meinem verdacht nach zu gehen und selber nach zu schauen hab ich bis jetzt nicht gesagt: 'ich hab es ja gleich gesagt'. beim nächsten mal darf ich es aber dann.

vielleicht sollte man im Blockig.pm dokumentieren was erlaubt ist: nur blockieren und antworten zurück geben und was nicht: z.b. kein TcpServer_Open und eigene main loop, keine devices schalten , ...
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

justme1968

mit dem offiziellen stand von heute ist es in meiner installation nicht möglich fhem zuverlässig mit shutdown restart neu zu starten.

von 10 versuchen ist genau einer erfolgreich durchgelaufen. alle anderen 9 bleiben reproduzierbar mit 2013.03.25 15:21:45 1: telnetPort: Can't open server port at 7072: Address already in use. Exiting. beteiligt sind nur eine handvoll presence pings.

scheinbar werden die geforkten child prozesse nicht zuverlässig und/oder schnell genug beendet.

mit meinem patch der die offenen ports schließt tritt das problem nicht auf. ein shutdown restart funktioniert problemlos in 10 von 10 fällen.

wenn aber das korrekte handling der sever ports keine alternative ist wäre es schön wenn wenigstens die bevorzugte methode zuverlässig funktionieren würde.

gruss
  andre

ps: sich auf den belegten port zu verlassen um einen mehrfach start zu verhindern funktioniert ziemlich sicher unter windows überhaupt nicht.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

justme1968

hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Markus Bloch

Hallo Andre,

leider ist diese Funktion noch nicht in Presence übernommen worden. Da ich aktuell unterwegs bin, wird es noch 2 wochen dauern bevor ich das einbringen kann. Testen kann ich aus der Ferne nicht zu 100%. ;-)

Viele Grüße

Markus
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

justme1968

hallo markus,

das sollte kein vorwurf sein und schon gar nicht an dich.

ich bin der meinung der richtige umgang mit systemfunktionen und das korrekte beenden gehört in die infrastruktur die fhem liefert und ist nichts etwas das in jedem einzelnen modul extra eingebaut werden muß damit es geht. erst recht weil die richtige korrekte und nebenwirkungsfreie  lösung 5 zeilen code an einer zentralen stelle sind die das problem ein für alle mal beheben.

ein server der durch module erweiterbar ist sollte so defensiv programmiert sein das er nicht darauf angewiesen ist das diese module nicht nur alles richtig machen sondern sogar probleme für die sie garnichts können ausbügeln.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

>  aber es ist dein projekt und deine entscheidung welche fehler drin bleiben.

Soviel darueber.

Ich habe den Patch eingecheckt, weil ich keine Lust auf weitere Diskussionen habe, zu meiner Meinung stehe ich immer noch. Bitte beachten:

- wenn der Kindprozess im UndefFn nicht abgeschossen wird, dann wartet er weiterhin auf Daten, und der nach dem fhem-restart aufgerufene zweite BlockingCall wird vermutlich Probleme bekommen.
- es werden nicht nur telnet-ports, sondern alle TCP Server Ports (FHEMWEB/FRM) geschlossen
- mit BlockingCall kann man ab jetzt keine Serverports mehr bedienen
- alle anderen FDs sind weiterhin offen

Gruss,
  Rudi

Markus Bloch

Hallo Rudi,

nur kurz zum Verständnis für mich, da ich bei eurer Diskussion nicht ganz mitgekommen bin.

Für PRESENCE müsste ich in der UndefFn sämtliche noch laufenden Childs via BlockingKill und der gespeicherten ProzessId nieder mähen?

Oder hab ich noch was übersehen?

Vielen Dank
Gruß

Markus
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

rudolfkoenig

>   Für PRESENCE müsste ich in der UndefFn sämtliche noch laufenden Childs via BlockingKill und der gespeicherten ProzessId nieder mähen?

Ja, immer. Egal ob mit oder ohne Patch.
Wenn das schwierig ist, koennte man ueberlegen das in fhem global zu verwalten.

Markus Bloch

Nein, nein. Schwierig ist das nicht ;-)

Ging nur darum ob ich das so richtig interpretiert habe aus eurer Diskussion.

Viele Grüße

Markus
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

justme1968

danke! ganz wirklich und ohne ironie.

eine anmerkungen noch: natuerlich kann man in einem blocking call server ports bedienen. entweder im child auf machen und nicht im parent das geht jetzt direkt oder nach dem accept im child genau dieses eine socket offen lassen und es statt dessen im parent schließen. beides ist sauber weil nie beide das gleiche socket offen habe.  ich baue den patch gerne um wenn es bedarf dafür gibt.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968