Forkable FHEMWEB Extensions

Begonnen von rudolfkoenig, 12 September 2014, 07:50:59

Vorheriges Thema - Nächstes Thema

rudolfkoenig

Das funktioniert aber nur, wenn es sich um einen reinen Lese-Prozess (wie beim SVG erstellen) handelt, sonst setzt man Attribute/etc im Unterprozess, und der ueberlebende Haupt-Prozess hat keine Ahnung davon.

Wie sollten wir dieses Problem loesen?
Mein Vorschlag: man setzt in ${data}{FWEXT}{<PFAD>}{FORKABLE}, und plotfork wird dann automatisch angewendet, falls im URL Pfad auftaucht.

Dr. Boris Neubert

Hallo Rudi,

finde den Vorschlag gut. Rein interessehalber: welche Module gibt es, die in FW_Read Änderungen an Attributen/ Variablen/ etc. vornehmen?

Habe in 02_RSS.pm
$data{FWEXT}{$url}{FORKABLE} = 1;
hinzugefügt und eingecheckt.

Zum Testen in der Entwicklung folgende Zeilen in der fhem.conf:
define RSS RSS jpg localhost /Pfad/zur/Datei/namens/tinylayout
attr RSS refresh 5


Die Datei tinylayout beinhaltet:
rgb c0c0c0                      # HTML color notation, RGB
pt 18
date 25 470
pt 48                           # font size in points
time 20 525                     # time @ (0.1*width, 0.9*height)


Und im Browser aufgerufen wird die URL:
http://localhost:8083/fhem/rss/RSS.html

Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

rudolfkoenig

Hab FHEMWEB und SVG umgestellt, und damit getestet.
RSS habe ich nicht getestet, weil GD zu installieren mir jetzt zu zeitraubend ist (ging nicht auf Anhieb).

Alle Module nehmen Aenderungen vor, wenn man z.Bsp. im Detailfenster ein Attribut modifiziert, oder im Frontend ein SET durchfuehrt. FLOORPLAN ist z.Bsp. auch via FWEXT realisiert

Dr. Boris Neubert

Danke sehr, Rudi.

Getestet mit RSS und produktiv genommen.

Erster Eindruck: läuft wie geschmiert. Das Antwortverhalten auf der Konsole und im Webinterface ist hervorragend.

Werde noch den Bilderrahmen anwerfen, um die Last zu erhöhen, und melde mich dann mit den endgültigen Testergebnissen zurück, wenn die Installation einige Zeit gelaufen ist.

Viele Grüße
Boris

Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

Dr. Boris Neubert

Hallo,

parallel ist parallel. Eine Seite in FHEMWEB mit vielen Plots reduziert dann die dem FHEM-Hauptprozess zur Verfügung stehende CPU-Zeit dramatisch. Beigefügter Patch (mit Doku :-) wertet das Argument von plotfork als denjenigen Betrag aus, um den der Nice-Wert der Kindprozesse gegenüber dem Elternprozess erhöht wird. Mit attr myFHEMWEB plotfork 10 geht dann auch die Load nicht mehr durch die Decke.

Viele Grüße
Boris

P.S.: Doof übrigens, dass RSS ein Gedächtnis braucht, was so nicht funktioniert...  :'(  Muss mir was überlegen.

Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

rudolfkoenig


Dr. Boris Neubert

Danke fürs Einchecken.

Ich habe mir ins Knie geschossen, da ich trotz meiner Nachfrage völlig ignoriert habe, dass Aufrufe des RSS-Moduls sehr wohl Variablen verändern (den Aufrufzähler sowie die laufende Nummer des Hintergrundbildes). Jetzt muss ich sehen, wie ich da rauskomme.

Nach längerer Lektüre von perlipc scheint mir folgende trickreiche Vorgehensweise minimalinvasiv umsetzbar. Bevor ich Dich mit einem Patch beglücke  ;) möchte ich zunächst Deine Gedanken dazu hören.

Vor dem fork in FW_Read() wird mit

pipe(READER,WRITER);

eine Pipe erstellt. Der Elter-Prozess merkt sich global je Kind-Prozess den READER und schließt den WRITER. Der Kind-Prozess merkt sich den WRITER und schließt den READER.

In SignalHandling() in fhem.pl wird das Signal CHLD nicht mehr ignoriert sondern es wird gewartet und aus dem READER gelesen:


$SIG{'CHLD'} = sub {
    $pid= wait;
    if($pid> 0) {
         Lies aus dem READER für den Kind-Prozess mit PID $pid und werte das Ergebnis mit eval() als Perl-Kode aus;
    }
}


Der Kind-Prozess kann dann seine Wünsche in den WRITER schreiben.

Viele Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

justme1968

boris,

schau mal hier http://forum.fhem.de/index.php/topic,18275.msg173732.html#msg173732

da gibt es den anfang einer sandbox Implementierung die forken und per pipe zwischen parent und child im beide richtungen kommunizieren kann.

das ganze ist noch lange nicht fertig aber im prinzip funktioniert es. man muss auf ein paar dinge achten. der patch  erlaubt neben deiner anwendung natürlich nich ein paar dinge mehr. wenn sich etwas in diese richtung entwickelt fände ich es schön keine modulspezifische einzelimplementierung zu haben sondern so eine globale sandbox oder ein zentrales nonBlockingCall das auch anderen modulen zur verfügung steht.

eine vielleicht kurzfristig einfachere lösung für dich könnte sein wenn du den geforkten SVG prozess einfach per telnet mit dem parent reden lässt. das könnte fhem ohne einen patch. ein beispiel dafür gibt es in BlockingCall.

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

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

rudolfkoenig

@Boris: dein Vorschlag finde ich schon sehr speziell fuer dein Problem, ich wuerde stattdessen die vom andre auch erwaehnte Methode ueber Telnet verwendet, da kannst du mit { perlcode } auch direkt deine Variablen setzen. Musst nur sicherstellen, das ein telnet existiert, und dazu koennten wir die in BlockingCall schon implementierte Verfahren in eine eigene Funktion auslagern.

Als allererstes wuerde ich aber ueberlegen, ob ich diesen Aufrufzaehler brauche, das Bild kann ja auch einen Zeitstempel statt laufenden Nummer haben.

Dr. Boris Neubert

Danke, Andre und Rudi, für Eure Beiträge.

Ich liefere erstmal mehr Kontext mit, welche Gedanken meinem Beitrag vorausgingen.

Ich will die einfachst mögliche Lösung dafür, dass RSS bei jedem Aufruf zyklisch das zuletzt verwendete oder das nächste Element aus der Liste der vom Anwender vorgegebenen Bildern auswählt, abhängig davon, ob seit dem letzten Aufruf höchstens oder mehr als tmin Sekunden vergangen sind. Ich meine, dass sich RSS dazu die Nummer und den Zeitstempel merken muss. Derzeit geschieht dies in internen Variablen am Device. Ich könnte die Werte auf der Platte speichern (örgs) oder, wie auch vorgeschlagen, über einen Aufruf von fhem.pl <port> <perl> von hinten durch die Brust ins Auge aus dem geforkten Prozess in den Hauptprozess einspeisen. Mir schien die zweite Möglichkeit recht plump, aber sie ist meine Fallbacklösung, wenn wir nicht besseres finden.

Zunächst: welche Aufgaben wollen wir jeweils lösen?

1. Bei mir geht es darum, dass nebenläufige Plugins für FHEMWEB (RSS, SVG, ggf. andere) bei Beendigung Veränderungen im Hauptprozess auslösen können.
2. Bei der Sandbox geht es darum, mehrere FHEM-Instanzen abgeschottet nebeneinander laufen zu lassen, die bidirektional mit dem Hauptprozess kommunizieren.

Gemeinsam ist beiden Aufgaben, dass Kind-Prozesse mit dem FHEM-Hauptprozess kommunizieren.

  • Mein Vorschlag verwendet eine Pipe, Andres Sandbox verwendet ein Socketpair.
  • Ich lese aus der Pipe, nachdem der Kindprozess beendet wurde, Andres Sandbox kommuniziert laufend (habe noch nicht verstanden, wo das Hauptprozess-seitige Ende der Kommunikation ist).

Beide Ansätze sind m.E. vereinbar und wir könnten uns die Aufgabe stellen, einen Standard für bidirektionale Kommunikation zwischen Haupt- und Kind-Prozessen zu definieren. So verstehe ich Deinen, Rudis, zweiten Satz mit der Auslagerung.

Was ich mich frage: warum wurde in BlockingCall.pm der Weg über TCP/IP-Kommunikation mit TelnetPort gewählt. Wäre es nicht einfacher, eine Pipe oder ein Socketpair zu verwenden und im Hauptprozess das Hauptprozess-seitige Ende in der Hauptschleife (MAIN) transparent zu verarbeiten?

Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

justme1968

hallo boris,

deinen speziellen anwendungsfall könnte man auch ganz ohne ipc lösen in dem jeweils vor dem forken gezählt wird. z.b. über ein ${data}{FWEXT}{<PFAD>}{usageCount}. wenn rudi das nicht zu speziell ist sondern auch als statistik nützlich findet wäre das die einfachste lösung.

oder ein pre-fork hook ($hash->{preForkFn}) den fhem vor dem forken im modul jeweils aufruft. da könntest du vor dem forken einen counter inkrementieren und hast den jeweils aktuellen wert nach dem forken.


zu den unterschieden zwischen sandbox und deinem vorschlag:
ja die haupt intention ist eine andere. das ziel ist aber die basics die als grundlagen darunter legen so allgemein zu halten das sie einfach auch in modulen wieder verwendbar sind wie deiner anwendung, BlockingCall und bidirektionales FHEM2FHEM.

zu diesen basics zähle ich:
- aufsetzen der kommunikation zwischen parent und child. entweder nur in eine richtung oder in beide
- ein 'simples' protokoll
- eine zentrale stelle in fhem in der die nachritten zurück kommen, das protokoll ausgewertet wird und kommandos direkt ausgeführt oder an das zuständige modul weitergereicht werden
- das forken mit allen randbedingungen 'fhem konform' erfolgt

auf diese basics die auch von anderen modulen und zu anderen zwecken verwendet werden können und sollen setzt dann das sandbox modul auf das komplett gekapselt und eigenständig ist (sein soll).

das ganze könnte man in ein fhemIPC.pm modul auslagern das dann von BlockingCall und sandbox verwendet wird. die preForkFn könnte auch hier teil des api sein.


socketpair macht intern nichts anderes als zwei pipes zu liefern. zwei weil ich in beide Richtungen kommuniziere. wenn nur in eine richtung kommuniziert wird kann eine der beiden sofort wieder zu gemacht werden und das ergebnis ist eine pipe für eine richtung. oder man lässt sich von socketpair nur die eine pipe für die relevante richtung geben.

ob nach dem ende des kind prozesses gelesen wird oder während er noch läuft ist für das lesen an sich kein unterschied. es hängt nur von der programm logik ab. im falle eines zählers würde ich aber vermutlich auch direkt nach dem forken als erstes zählen weil sonst das verhalten von der größe des bildes abhängt. ist aber vermutlich im svg fall egal?


BlockingCall geht vermutlich deshalb über telnet weil es eigentlich ein hack ist/war mit dem rudi probieren wollte ob so etwas überhaupt geht ohne in fhem große änderungen oder infrastruktur zu haben.

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

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

Dr. Boris Neubert

Hallo Andre,

Zitat von: justme1968 am 13 September 2014, 11:12:18
deinen speziellen anwendungsfall könnte man auch ganz ohne ipc lösen in dem jeweils vor dem forken gezählt wird. z.b. über ein ${data}{FWEXT}{<PFAD>}{usageCount}. wenn rudi das nicht zu speziell ist sondern auch als statistik nützlich findet wäre das die einfachste lösung.

oder ein pre-fork hook ($hash->{preForkFn}) den fhem vor dem forken im modul jeweils aufruft. da könntest du vor dem forken einen counter inkrementieren und hast den jeweils aktuellen wert nach dem forken.

aus meiner Sicht spricht gegen beide Lösungsansätze, dass FHEMWEB zum Zeitpunkt des Forks das angesprochene Device gar nicht kennt. Die Extensions funktionieren so, dass für bestimmte URLs die Auslieferung der Antwort auf den HTTP-Get-Request an die Extension delegiert wird. Im Fall von RSS bestimmt dann zunächst eine Klassenfunktion anhand der URL, welches RSS-Device gemeint ist (Ermittlung des Device-$hash) und ruft dann die Funktion zur Auslieferung der Antwort mit dem $hash als Parameter auf.

Mit dem Rest Deines Beitrags gehe ich absolut konform.

Mal sehen, was Rudi meint.

Grüße
Boris

Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

justme1968

ok. verstanden.

der ansatz vor dem forken zu zählen und ohne ilc auszukommen sollte aber dann wieder gehen wenn du das forken selber unter kontrolle hast. also diesen teil aus einem potentiellen fhemIPC.pm selber aufruft.

wenn ihr das auch so seht würde ich als einen ersten schritt vorschlagen das forken (mit dem fhem nötigen drum rum) aus BlockinCall in eine eigene funktion auszulagern.

das wäre auch im OWServer und XBMC modul nützlich.

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

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

Dr. Boris Neubert

Zitat von: justme1968 am 13 September 2014, 11:35:50
wenn ihr das auch so seht würde ich als einen ersten schritt vorschlagen das forken (mit dem fhem nötigen drum rum) aus BlockinCall in eine eigene funktion auszulagern.

zu meinem Verständnis: jedes Modul kann dann Kind-Prozesse mittels dieser Funktion forken gemäß eigener Logik. Ist das Drumherum die bidirektionale Socketkommunikation?

Ich habe mir gestern abend den Kode von FHEMWEB i.V.m. fork angesehen. Wie soll funktionieren, dass die Extension die Auslieferung der Antwort an den Client an einen Kind-Prozess delegiert ohne dass der FHEM-Hauptprozess davon nichts mitbekommt?

Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

justme1968

das drumherum wäre (konfigurierbar) die bidirektionale komunikation und vor allem das schliessen aller nicht benötigten filedescriptioren so wie es in BlockingCall passiert. alle stellen die fork direkt aufrufen lassen z.b. die verbindungen zu den usb devices und der db offen und verhindern das fhem sich per restart sauber neu starten lässt.


er hauptprozess (bzw der teil des Haupt prozesses der dann im child weiter läuft) muss insofern etwas davon mitbekommen als das er sich beenden muss statt weiter zu arbeiten.

den genauen Mechanismus hier zu müssen wir noch festlegen. z.b. über $data{FWEXT}{$url}{FORKABLE} in Verbindung mit einem check auf $$. z.b. so:
$data{FWEXT}{$url}{FORKABLE} = 1 -> fhemweb soll forken, wie für svg

$data{FWEXT}{$url}{FORKABLE} = 2 -> das modul forkt eventuell selber, vor dem aufruf $$ merken, wenn nach dem aufruf die gemerkte pid != $$ ist einfach beenden.

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

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

Dr. Boris Neubert

Zitat von: justme1968 am 13 September 2014, 12:13:59
das drumherum wäre (konfigurierbar) die bidirektionale komunikation und vor allem das schliessen aller nicht benötigten filedescriptioren so wie es in BlockingCall passiert. alle stellen die fork direkt aufrufen lassen z.b. die verbindungen zu den usb devices und der db offen und verhindern das fhem sich per restart sauber neu starten lässt.

OK.

Zitatden genauen Mechanismus hier zu müssen wir noch festlegen. z.b. über $data{FWEXT}{$url}{FORKABLE} in Verbindung mit einem check auf $$. z.b. so:
$data{FWEXT}{$url}{FORKABLE} = 1 -> fhemweb soll forken, wie für svg

$data{FWEXT}{$url}{FORKABLE} = 2 -> das modul forkt eventuell selber, vor dem aufruf $$ merken, wenn nach dem aufruf die gemerkte pid != $$ ist einfach beenden.

Kurz zwecks Dokumentation und Abgleich/Anregung die derzeit in FHEMWEB implementierte Logik:

  • Die Variable $pid ist definiert, wenn geforkt wurde.
  • Sie enthält 0 im Kindprozess und die PID des Kindprozesses im Hauptprozess.
  • Der Hauptprozess beendet die Verarbeitung nach dem Fork und dem Renice.
  • Der weiterlaufende Teil (Kindprozess oder, falls nicht geforkt wurde, der Hauptprozess) merkt sich $pid in $hash->{PID}
  • Nachdem die Antwort ausgeliefert wird, kommt es in FW_closeConn() zum exit, wenn $hash->{PID} definiert ist, also wenn geforkt wurde und wir im Kindprozess sind.

Das ist hart an der Grenze, was ich noch überblicken kann   :-[ 

Das kann wohl mit zwei, drei Zeilen Kode zu dem von Dir vorgeschlagenen Mechanismus aufgebohrt werden.

Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

rudolfkoenig

Ich habe die Diskussion hier durchgelesen, und ich meine, dass ich nichts unternehmen muss :)
Wenn ich mich irre, korrigiert mich bitte.

Kann sich noch jemand daran erinnern, wieso im BlockingCall die FDs zugemacht werden? Diverse Perl-Module beachten nicht, dass man im Kind die Verbindung nur loswerden moechte, und fuehren zusaetzliche Operationen durch, die im Hauptprozess zu Problemen fuehren koennen.

justme1968

ich glaube ganz ohne kommst du nicht davon :) zumindest einem patch zustimmen und ihn anwenden.


unter anderem deswegen: http://forum.fhem.de/index.php/topic,13156.msg89333.html#msg89333 und deswegen: http://forum.fhem.de/index.php/topic,11852.0.html und noch ein paar anderen solchen problemen dieser art. z.b. mit DbLog.

was meinst du für zusätzliche operationen?

eigentlich gibt es probleme nur dann wenn ein fd nicht zu gemacht wurde und im child drauf zugegriffen wird. wenn alles zugemacht ist kann der child nicht mehr drauf zugreifen.

zu forken ohne die nicht benötigten und beabsichtigen resourcen freizugeben ist falsch. selbst wenn es manchmal funktioniert führt es früher oder später zu ziemlich undurchsichtigen problemen.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

rudolfkoenig

Zitateigentlich gibt es probleme nur dann wenn ein fd nicht zu gemacht wurde und im child drauf zugegriffen wird

Dieses Problem sehe ich nicht, da im typischen BlockingCall - Fall nur eine Funktion ausgefuehrt wird. Woran ich mich noch erinnere: Kind laeuft noch, TCP-ServerPort offen, Ursprungs-FHEM kann nicht neu starten. Und irgendetwas gabs auch mit der DB.

Gegen dem close spicht, dass manche Bibliotheken die Parameter der seriellen Leitung beim Close zuruecksetzen, oder beim zumachen einer Verbindung der anderen Seite ein "close" senden, DbLog muss deswegen InactiveDestroy setzen. Ich ueberlege, ob all diese Probleme nicht einfacher dadurch zu loesen sind, dass alle Kind-Prozesse beim Terminieren von FHEM abgeschossen werden.

justme1968

drauf zugreifen war genereller gemeint. Nenn es die resource noch belegen oder offen halten.

nicht nur tcp port. auch alle usb devices die noch offen sind verhindern in diesem fall den neustart.

für kindprozesse lassen sich unter umstaenden nicht immer abschießen weil sie blockierend zum beispiel auf das Netzwerk warten.

und im db fall und eigentlich auch im filelog fall sollte es auch probleme geben wenn das child an einer Stelle aus einem fd liest und der parent gleichzeitig an einer anderen Stelle schreibt. z.b. bei plotfork.

die in kernel file Pointer sind nach dem fork noch geshared.

zumachen aller fd ist wirklich die richtige und saubere lösung.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

Dr. Boris Neubert

Hallo Andre,

hast Du eigentlich an dem Thema gearbeitet?

Ich habe heute RSS wieder auf FORKABLE gestellt und setze die Variablen mittels eines über eine Socket-Verbindung zum Elternprozess abgesetztes geheimes set-Kommando. Was für ein Gewürge!

localhost:7072 ist noch hart verdrahtet. Muss ich mir wirklich auch noch ein telnetPort-Gerät suchen, um den tatsächlich verwendeten Port herauszufinden? Hat wohl noch keiner in irgendeinem FHEM-Modul gemacht - oder ich habe Tomaten auf den Augen.

Viele Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

rudolfkoenig

BlockingCall sucht nach telnet Instanzen (und legt Notfalls eins an), leider ist diese Funktionalitaet nicht in eine separate Funktion ausgelagert.

Dr. Boris Neubert

Zitat von: rudolfkoenig am 12 Oktober 2014, 09:14:50
BlockingCall sucht nach telnet Instanzen ...

Danke, das habe ich gesucht. Ich habe ein Snippet daraus nach 02_RSS.pm übernommen.

##################
# Find a telnet device that allows us to connect to the current instance of FHEM we are running under.
sub
RSS_findTelnetDevice() {
    my $telnetDevice= undef;
    foreach my $d (sort keys %defs) {
      my $h = $defs{$d};
      next if(!$h->{TYPE} || $h->{TYPE} ne "telnet" || $h->{SNAME});
      next if($attr{$d}{SSL} || $attr{$d}{password} ||
              AttrVal($d, "allowfrom", "127.0.0.1") ne "127.0.0.1");
      next if($h->{DEF} =~ m/IPV6/);
      $telnetDevice = $d;
      last;
    }
    return $telnetDevice;
   
}


Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

justme1968

hallo boris,

ich bin noch nicht dazu gekommen es so weiter auszubauen wie ich möchte. habe gerade ein modul für die harmon ultimate fernbedienungen am wickel und das hat viel zeit gekostet. ich möchte das thema aber auf weiter verfolgen und hoffe das ich bi sende des jahres so aufgeräumt haben das die einzelnen komponenten auch einzeln wiederverwendbar sind.

für die telnet verbindung schau mal Blocking.pm. da gibt es die funktionalität und es wird auch ein temporäres telnet angelegt wenn es noch keines gibt. vielleicht könnte man das in eine eigene funktion auslagern.

gruss
  andre

edit: hab eben erst gesehen das rudi geantwortet hat und es schon geklärt war.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

Dr. Boris Neubert

Hallo,

mir ist es unabsichtlich gelungen, lawinenartig FHEM-Prozesse zu forken. Nicht dass ich wüsste, wie ich das genau zu Wege bringe, und gewollt ist das schon gar nicht  :(

Es hängt mit RSS zusammen, meiner FHEMWEB-Extension, die ich nun wieder auf FORKABLE gesetzt habe. Da geschieht nun also folgendes:

Wenn ein Client http://meinserver:8083/fhem/rss/foo/foo.bar aufruft, erzeugt FHEMWEB einen Kindprozess, der den Request-Handler im RSS-Modul aufruft. Das RSS-Modul liefert als Antwort darauf eine Datei zurück, die vom Kindprozess an den Client ausgeliefert wird. Danach verendet der Kindprozess.

Der von FHEMWEB aufgerufene Request-Handler im RSS-Modul baut die Datei in einigen Sekunden auf. Nach Fertigstellung der Datei beschaffe ich mir über ein Telnet-Gerät den Port, unter dem ich FHEM auf localhost erreiche, und setze auf localhost:port ein set-Kommando ab, um die Buchhaltung des RSS-Geräts im Hauptprozess zu aktualisieren. Anschließend liefere ich die erstellte Datei an FHEMWEB zurück.

Es scheint mir, als ob es dadurch zu einer Parallelisierung der Hauptschleife kommt. Dass also nun auch die Kindprozesse über die wohl offenen duplizierten Dateien beginnen, Datagramme von den verschiedenen Geräten (z.B. CUN) entgegenzunehmen, Hinweis-Mails zu verschicken, Aufrufe von FHEMWEB entgegen zu nehmen. Das führt am Ende zu einer lawinenartigen Vermehrung von FHEM-Prozessen. Bei einer Load von 25 hilft dann nur noch killall -9 perl.

Wenn ich mich während der Lawine per telnet meinServer 7072 verbinde, erhalte ich nach Drücken der Eingabetaste gleich x-mal die Startmeldungen. shutdown wirkt nicht bzw. ich sehe an der kaputten fhem.save, dass gleich mehrere FHEM-Prozesse versuchen, die Datei zu schreiben.

Es liegt daran, dass ich aus dem Kind heraus einen set-Befehl absetze, nicht wahr?

Viele Grüße
Boris

Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

Dr. Boris Neubert

Hallo,

ich hole das Thema nochmal aus der Versenkung.

Ich habe das RSS-Modul auf FORKABLE umgestellt. Die Kommunikation mit dem Elterprozess zum Setzen von Variablen habe ich auskommentiert. Trotzdem kommt es ca. zwei Mal am Tag zu einer Explosion: lawinenartige Vermehrung der fhem-Prozesse plus EOB-Meldungen vom CUN. Irgendwann schlägt der Watchdog bei einer Load von > 25 zu und bootet den Raspberry neu.

Die Ursache könnte sein, dass eine Kombination von Ereignissen, z.B. Kalender- und Wetterabfragen ins Internet die Erzeugung und Auslieferung des Bildes so lange verzögert (> 15 Sekunden), dass bereits der nächste Request kommt, bevor der vorige abgearbeitet ist. Damit sinkt die verbleibende Rechenleistung und es kommen immer mehr Requests, die nicht abgearbeitet werden können. Lawinentod.

Das könnte ich ja mit etwas Grips über Locks abfangen.

Aber die über 200 EOB-Meldungen heute vom CUN machen mir Sorgen. Kann es sein, dass in den Kindprozessen ungewollte Kommunikation mit den anderen Devices stattfindet? Das würde dann nämlich auch die SVG-Erzeugung betreffen, die ja ebenfalls FORKABLE ist.

Viele Grüße
Boris



Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

justme1968

wenn ich es auf die schnelle richtig gesehen habe schliesst fhemweb bei forkable die nicht mehr benötigten descriptoren im gegensatz zu BlockingCall nicht. es hängt also von der logik in dem geforkten prozess ab ob es fälle gibt bei denen die select loop ausgeführt werden kann.

unabhängig davon verhindern die offenen deskriptoren aber auf jeden fall ein sauberes restart.

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

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

Dr. Boris Neubert

Zitat von: justme1968 am 02 Dezember 2014, 20:47:17
unabhängig davon verhindern die offenen deskriptoren aber auf jeden fall ein sauberes restart.

Du meinst, shutdown restart startet fhem nicht wieder? Das kann ich bestätigen und das passiert bereits ohne FORKABLE RSS allein mit FORKABLE SVG.

Viele Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

Dr. Boris Neubert

Zitat von: justme1968 am 02 Dezember 2014, 20:47:17
wenn ich es auf die schnelle richtig gesehen habe schliesst fhemweb bei forkable die nicht mehr benötigten descriptoren im gegensatz zu BlockingCall nicht. es hängt also von der logik in dem geforkten prozess ab ob es fälle gibt bei denen die select loop ausgeführt werden kann.

Sollte ich die hier http://forum.fhem.de/index.php/topic,27630.msg222862.html#msg222862 beschriebene Strophe an der Stelle absingen, wo ins geforkte RSS-Modul eingetreten wird?

Viele Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

justme1968

ja. das meine ich. es sollte aber nur passieren wenn solche forakbe prozesse auch gerade laufen. nonblocking OWServer hat leider dieses problem auch noch. ich bin noch nicht dazu gekommen einen patch zu bauen.

das passiert weil fhem beim neustart merkt das der telnetport (durch die children) noch offen ist. das gleiche gilt für alle offenen usb devices.

ein block dieser art (aus BlockingCall) gehört nach jedes fork: 84   # Close all kind of FD. Reasons:
85   # - cannot restart FHEM if child keeps TCP Serverports open
86   # ...?
87   foreach my $d (sort keys %defs) {
88     my $h = $defs{$d};
89     $h->{DBH}->{InactiveDestroy} = 1 if($h->{TYPE} eq 'DbLog');
90     TcpServer_Close($h) if($h->{SERVERSOCKET});
91     if($h->{DeviceName}) {
92       require "$attr{global}{modpath}/FHEM/DevIo.pm";
93       DevIo_CloseDev($h,1);
94     }
95   }


wobei der kommentar nicht stimmt. es geht nicht nur im tcp server ports sondern jeden offenen filedescriptor.

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

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

Dr. Boris Neubert

Zitat von: justme1968 am 02 Dezember 2014, 21:03:36
ein block dieser art (aus BlockingCall) gehört nach jedes fork

Danke Andre. Schön ist das nicht, aber ich werde es am Wochenende versuchen.

STDIN, STDOUT und STDERR muss ich also nicht schließen und auch keine neue Session mit setsid eröffnen?

Grüße
Boris

Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

herrmannj

andre is right,

die strophe hilft zusätzlich (besser STDOUT und co nach null) .

Das shutdown restart thema hatte ich auch, die hat sich aber schlussendlich erledigt weil ich zwischen papa und kind einen tcp aufmache (den telnet hack mag i net, pipe wäre nur linux). Damit bekommt kind sicher ein tcpclose wenn papa, egal aus welchen gründen auch immer, stirbt. Das catche ich und lass das kind durch clean exit gehen. Vom vorgehen ist das nicht wesentlich aufwendiger als die Telnet Geschichte aber deutlicher komfortabler.  Unter diesen Umsänden spart man sich auch das schließen der anderen handles, zumal man ohnehin nur die erwischt die an bekannten Stellen (in der $def) liegen.

Gegen das problem mit dem fork aus define hilft es nix.

vg
jörg

justme1968

du hast recht. um es ganz sauber zu machen sollte man STDIN,STDOUT und STDERR auch behandeln. einfach nur schliessen ist aber falsch. es gibt geforkte programme die hier gültige handles erwarten.

socketpair bietet sich an. das gibt es unter windows und unix. und es geht auch in beide richtungen. in dem sandbox.pm modul an dem ich arbeite mache ich es so.

das schliessen der handles ist nicht nur für den restart sondern verhindert auch probleme wenn der child prozess aus irgendeinem grund und die fhem select loop gerät oder selber eine select loop verwenden will. d.h. das schliessen spart man nicht wirklich wenn es sauber sein soll.

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

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

herrmannj

ja genau. socketpair dachte ich nur unter unix.

select im chield ist übrigens kein problem, darf nur nicht mit den werten aus %defs gefüttert werden. Wichtig ist da natürlich das koreekte nonblock setup, das muss nach einem accept neu gemacht werden.

vg
jörg

justme1968

socketpair geht auch unter windows. http://perldoc.perl.org/perlport.html#socketpair. das zu emulierten ist aber im gegensatz zu fork auch kein problem.


ein eigenes select nur auf die eigenen fds ist kein problem. wenn man aber in der fhem event loop landet oder in irgendeinem anderen code teil das noch fds aus dem parent verwendet geht es schief. dblog ist unter umständen so ein kandidat.

autoflush ist nicht nur nach accept sondern auch nach dup nötig. lieber ein mal zu viel als zu wenig :)

gruss
  andre


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

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

Dr. Boris Neubert

Hallo,

ich habe mich heute morgen daran gemacht, das RSS-Modul weiter aufzubohren. Ich bin zu dem Ergebnis gekommen, dass es sich bei dieser Vorgehensweise um einen Irrweg handelt. Die Komplexität steigt dadurch dermaßen an, dass ich nicht mehr nachvollziehen kann, was abläuft.

Ich darf natürlich nicht alle Handles im Kind schließen, weil ich dann nämlich auch nicht mehr im Kind die von RSS generierte HTML-Ergebnisdatei über die Verbindung vom HTTP-Client zu FHEMWEB zurückliefern kann. Komischerweise funktioniert das aber doch, während der in der HTML-Ergebnisdatei eingebettete IMG-Link zum vom RSS generierten Bild aber nicht bedient wird.

Ferner kann ich nicht mehr loggen und muss mein eigenes Log mitführen, dass ich dann über Callback an den Elterprozess füttere. Das führt zu einem eval im Elternprozess auf Log3(foo, 3, EscapeOrgie).

Und warum ich aus dem Kindprozess heraus überhaupt wieder in die globale select-Schleife komme, verstehe ich schon gar nicht.

Fazit: ich fahre jetzt nicht mehr weiter in diese Sackgasse.

Ich komme wieder auf die parallel laufende API-Diskussion zurück. Eine mögliche Lösung könnte so aussehen, dass FHEM sowohl FHEMWEB als auch RSS nach Definition per exec-Call in separate Prozesse schickt, die sich per API mit FHEM unterhalten.

Viele Grüße
Boris


Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

herrmannj

Hallo,

ZitatFerner kann ich nicht mehr loggen und muss mein eigenes Log mitführen, dass ich dann über Callback an den Elterprozess füttere. Das führt zu einem eval im Elternprozess auf Log3(foo, 3, EscapeOrgie).
Ich glaub das ist gefährlich. Wenn der fork das log3 des papas aufruft macht er das asynchron. fhem-papa steht zu dem Zeitpunkt ja irgendwo.
ZitatUnd warum ich aus dem Kindprozess heraus überhaupt wieder in die globale select-Schleife komme, verstehe ich schon gar nicht.
Hat vielleicht eine Verbindung zu dem noch offenen Thema "forken aus define". Dürfte eigentlich nicht passieren wenn child mit posix::_exit endet, oder geht der dann noch mal in den Hauptprozess ? Dann müssten doch aber nach exit immer noch 2 pids existieren ?
ZitatIch komme wieder auf die parallel laufende API-Diskussion zurück. Eine mögliche Lösung könnte so aussehen, dass FHEM sowohl FHEMWEB als auch RSS nach Definition per exec-Call in separate Prozesse schickt, die sich per API mit FHEM unterhalten.
Würden sie doch auch asynchron machen, regen und traufe ?

vg
jörg

Dr. Boris Neubert

Zitat von: herrmannj am 07 Dezember 2014, 13:30:52
Ich glaub das ist gefährlich. Wenn der fork das log3 des papas aufruft macht er das asynchron. fhem-papa steht zu dem Zeitpunkt ja irgendwo.

Das kommt noch erschwerend dazu. Noch ein Grund mehr, diesen Weg aufzugeben.

Zitat
Hat vielleicht eine Verbindung zu dem noch offenen Thema "forken aus define". Dürfte eigentlich nicht passieren wenn child mit posix::_exit endet, oder geht der dann noch mal in den Hauptprozess ? Dann müssten doch aber nach exit immer noch 2 pids existieren ?

FHEMWEB beendet das Kind mit exit (Zeile 434). Ich sage ja, es ist für mich nicht nachvollziehbar, warum diese Nebenläufigkeit entsteht. Selbst wenn ich es verstehen würde, wäre das ganze Konstrukt so fragil, dass ich es nicht in FHEM haben möchte.

Zitat
Würden sie doch auch asynchron machen, regen und traufe ?

Nicht, wenn das API als Device regulär läuft.

Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

herrmannj

ZitatDas kommt noch erschwerend dazu. Noch ein Grund mehr, diesen Weg aufzugeben.
Naja, hatte das gleiche Problem  :) Wollte nicht aus dem kind ins log schreiben und lass papa am das auch am socket machen. Die eval orgie hatte (was auch immer Du damit meinst) hatte ich nicht. Kind schickt dem papa einen über den gemeinsamen socket ein cmd (log, mit Kindname, loglevel und Text) und papa schreibt das weg. 
ZitatFHEMWEB beendet das Kind mit exit (Zeile 434). Ich sage ja, es ist für mich nicht nachvollziehbar, warum diese Nebenläufigkeit entsteht. Selbst wenn ich es verstehen würde, wäre das ganze Konstrukt so fragil, dass ich es nicht in FHEM haben möchte.
Yepp!. Ist aber auch ein Zeichen für ein Missverständnis irgendwo. Wie stellst Du die Nebenläufigkeit eigentlich fest? Ich sehe nach einem child exit keine zweite pid mehr ...

vg
jörg

Dr. Boris Neubert

Zitat von: herrmannj am 07 Dezember 2014, 14:01:17
Naja, hatte das gleiche Problem  :) Wollte nicht aus dem kind ins log schreiben und lass papa am das auch am socket machen. Die eval orgie hatte (was auch immer Du damit meinst) hatte ich nicht. Kind schickt dem papa einen über den gemeinsamen socket ein cmd (log, mit Kindname, loglevel und Text) und papa schreibt das weg. 

Mit EscapeOrgie meinte ich, dass ich im Text, den ich mit { Log3 FrameRSS,2,"Text" } an den TelnetPort von Papa sende, alle Sonderzeichen maskieren muss.

Zitat
Yepp!. Ist aber auch ein Zeichen für ein Missverständnis irgendwo. Wie stellst Du die Nebenläufigkeit eigentlich fest? Ich sehe nach einem child exit keine zweite pid mehr ...

Ich sehe nach einer gewissen Zeit, dass mein CUL den Puffer voll hat (ein zeitgesteuertes Kommando wird aus allen Instanzen von FHEM an den CUL geschickt) und dass ich zig Mal dieselbe Mail geschickt bekomme (zeitgesteuerter Shell-Aufruf, mir die externe IP-Adresse meines Routers zu mailen).

Ich habe keine Ahnung, wie ich das schaffe.

Viele Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

herrmannj

tricky und als Indizienbeweis zugelassen  :)

Mit folgendem construct hab ich gerade getestet (würde mich auch massiv betreffen, ANGST  :o )

(fhem.pl, line #551)

print "in select $$ ooo\n";
my $nfound = select($rout=$rin, $wout=$win, $eout=$ein, $timeout);
print "out select $$ +++\n";


Kann das zum Glück nicht bestätigen, ich seh nur den Papa. (aufatme)

vg
jörg