C-Programm dauerhaft ausführen

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Mit dem RPi möchte ich per GPIO jedes Blinken einer LED meines Stromzählers in eine MySQL Datenbank schreiben.
    Dank eines Code-Schnipsels von dreamshader per PN wurde ich schon mal auf den richtigen Pfad geschickt mein Ansinnen umzusetzen. Durchaus schweißtreibend, da Linux, C und vor allem gcc für mich neu sind. Bin eher bei php, Javascript und ein bisschen AppleScript zu Hause.

    Aber im Prinzip klappt schon mal folgendes:


    Mit einer kleinen LED-Testschaltung die einen Fototransistor beleuchtet, der an gpio 0 (WiringPi Nomenklatur) angeschlossen ist, werden problemlos 40 Impulse pro Sekunde in die Datenbank geschrieben. Bei 1000 Impulsen pro kWh würde das einer Momentanleistung von 144 kW entsprechen. Sollte also reichen.

    Nun, damit das Ganze dann produktiv wird, sollte das Programm ja dauerhaft laufen. (Und bei Neustart des RPi auch automatisch starten.) Das Programm mit der Endlosschleife am Leben zu erhalten, erscheint mir etwas "unelegant", und dient ja auch nur zu "unproduktiven" Testzwecken. (Bei AppleScript gibt so eine Möglichkeit "nicht automatisch beenden", um auf bestimmte Events zu reagieren.)

    Eine Möglichkeit scheint mir daemon. Aber wohl auch nicht so einfach umzusetzen.

    Vielleicht kann mir hier jemand den Weg in die richtige (unkomplizierte) Richtung weisen.
    Und die C-Profis dürfen gerne Kritik an meinem Code üben. Ich möchte mich gerne an die üblichen Konventionen halten.

  • Nach einem flüchtigen Blick in Dein Programm fällt mir auf:

    Code
    while (myCounter == globalCounter)
          delay (100) ;

    Wenn die beiden Werte gleich sind, führt das Programm für immer und ewig das delay aus.

    Gruß, mmi

    EDIT:
    Vergiss es, habe die INT Routine übersehen - sorry! :(

  • Moin mbrod ....

    daemon ist das Stichwort ;) ...

    Prinzipiell kein grösseres Problem:



    Sobald Du die Funktion aufrufst und sie mit 0 zurückkehrt, ist das Programm ein "daemon".
    Du musst allerdings im Hinterkopf behalten, dass Du ab nun keine Ein-/Ausgabemöglichkeit per Bildschirm/Tastatur mehr hast.
    Mit anderen Worten: das Programm ist von Dir und Deinem Terminal losgelöst - CTL-C funktioniert nicht mehr.

    Ich hab' so was wie Du vor hast für meinen Bruder gebastelt.
    Wenn Du Interesse hast, hänge ich bei einem der nächsten Posts den Source zum Spicken mal an ...


    ciao,
    -ds-

  • Hallo ds,

    Dein Code "forkt" zweimal, das verstehe ich nicht.

    Nach dem ersten "fork" läuft der Dämonprozess und "parent" sagt ciao - soweit ok.
    Warum aber noch ein fork nach dem handle_signal ?? Versehentlich zuviel reinkopiert ? :s


    Gruß. mmi

  • Moin mmi :) ...

    Ein fork() reicht nicht, weil Du dadurch nur einen child-prozess erzeugst. Würdest Du jetzt im caller (parent) gut sein lassen, hättest Du aus Sicht des Systems einen "zombie" erzeugt. Der wird aber jetzt zum session-leader, erzeugt einen neuen Prozess und dieser ist dann sozusagen "sauber" ohne Spuren in seine Ahnenreihe sozusagen.
    Der signalhandle ist nicht notwendig (ist ein Überbleibsel weil aus meinem RGB-daemon) und die ttys könnte man auch schon eher alle schliessen, dann hast Du aber keine Chance mehr eine Fehlermeldung o.ä. auf den BS auszugeben.

    Genauer kann ich das irgendwie auch nicht erklären.
    Ich hab das vor vielen, vielen Jahren mal von einem Kollegen aus der Kernel-Abteilung von Siemens gelernt.
    Das nannte er "daemonize by doublefork" und das mache ich seitdem immer so.

    Eine Erklärung habe ich z.B. auch hier gefunden. Ist ein bischen ausführlicher und deckt sich in den Grundaussagen mit meiner oben.

    Ich wünsche Dir noch einen schönen Sommertag,
    -ds-

  • Hallo DS,
    danke Dir für die umfangreiche Info :thumbs1:- man lernt nie aus.

    Ist allerdings auch sehr kernelspezifisch und die richtige Doku, wie so oft, kaum zu finden.
    Die meisten Dokus forken nur einmal und es funktioniert wohl in den meisten Fällen.
    Aber jetzt werde ich meinen Dämonen auch die Freude gönnen, ist ja schnell eingebaut.

    Bis dann,
    mmi

  • Hallo mmi,

    bitte ... gern geschehen.

    Ich glaube sogar irgendwo mal gelesen zu haben, dass es eine C-Funktion daemon() gibt ... aber frag' mich jetzt bitte nicht, wo.

    Inwiefern die "modernen" Kernel in ihrer Prozessverwaltung mittlerweile auch Zombies bzw. - wenn sie zusätzlich durch ein & beim Aufruf - sozusagen "HalbZombieDaemons" als vollwertige Daemons behandeln, vermag ich nicht zu sagen.

    Schönen Tag noch und danke für Dein "Daumen hoch" ;) ...

    ciao,
    -ds-

  • Lt. manpage für "daemon" (in unistd.h enthalten) scheint nur einmal geforked zu werden. Wie Du schon sagst, möglicherweise ist für modernere Kernel der double_fork doch schon veraltet. Aber er schadet ja auch nicht. ;)

  • Na, da habe ich ja mit meiner Frage gleich eine Expertendiskussion ausgelöst, der ich mit meinen bescheidenen Kenntnissen nur peripher folgen kann.

    Und leider scheine ich ja um das Thema daemon nicht herum zu kommen, welches offensichtlich doch recht komplex ist. Werde mich mal an Hand dieser Seite der Thematik nähern. Auf der gleichen Homepage habe ich auch die Infos gefunden wie man MySQL per C anspricht.

    Wird wahrscheinlich eine Zeit dauern, bis ich das alles kapiert habe. Dann melde ich mich wieder. Aber "erledigen" werde ich das Thema erst, wenn ich den daemon ans laufen gebracht habe. (Die Forensoftware ist ja richtig streng. :daumendreh2: )

  • Ach ... das ist gar nicht so kompliziert (siehe Post #3) mbrod.

    Auch als Daemon ist das ein Programm wie jedes andere (mit kleinen Einschränkungen, die genau genommen keine sind).

    Wie gesagt, ich stell Dir gerne den Source zum Spicken zur Verfügung, den ich für meinen Bruder erstellt habe ( siehe hier ).

    ciao,
    -ds-

  • mbrod: Lasse Dich von unserer "1- oder 2-fork" Diskussion nicht beirren, wie ds schon geschrieben hat, ist es viel einfacher als es vielleicht klingt und das Codebeispiel von ds ist astrein! Mir geht's dabei nur um den letzten Schliff, der mir bisher nicht bekannt war (nachdem ein Dämon auch einmal geforked normalerweise zuverlässig läuft).

    dreamshader:
    Das hat mir jetzt keine Ruhe gelassen und ich habe doch noch recherchiert. Bei SysV Systemen (und somit auch Linux) ist Deine Methode die saubere Lösung, denn:

    Der erste Kindprozess wird zum session leader (PID=SID) und ist somit von "init" für Aufräumzwecke nicht ansprechbar, desweiteren hat er immer noch die Fähigkeit, jederzeit wieder ein control terminal zu öffnen.

    Der zweite Kindprozess hat diese Eigenschaften nicht mehr (PID != SID) und kann somit von "init" (der Mutter aller Prozesse) sauber verwaltet werden.

    Da muss ich mein *DaumenHoch* doch nicht zurückziehen ;)
    Problem also endgültig geklärt, ich erweitere meinen Dämoncode entsprechend - die Arbeit der Kinder wird auf die Enkel verlagert, heranwachsende zombies sofort umgebracht - wie grausam !

    Gruß, mmi

  • Ihr habt mir Mut gemacht! Danke.
    So habe ich mir auch noch verschiedene Quellen angeschaut und überall ein ähnliches Muster gefunden. Dann habe ich mit copy and paste aus dreamshaders Schnipsel und Quelle1 sowie Quelle2 folgendes zusammengebaut:

    Das funktioniert :thumbs1:
    Wobei ich die daemonize- und handle_signal- Funktion nicht wirklich durchblicke. Schon erstaunlich, was alles geht, wenn man ein paar Strukturen einfach so zusammen kopiert.

    Für den Stromzähler wird der Dämon ja eigentlich nie beendet, heißt: Die Funktion mysql_close (mysql) wird nie aufgerufen. Gibt es eine Möglichkeit den Dämon "ordentlich" zu beenden, sodass auch mysql_close (mysql) aufgerufen wird? Ich habe mit sudo killall gpiod das natürlich beenden können. Dabei könnte es aber doch möglicherweise Ärger mit mysql geben, wenn das nicht ordentlich geschlossen wird. Oder?

  • Das funktioniert :thumbs1:
    Wobei ich die daemonize- und handle_signal- Funktion nicht wirklich durchblicke. Schon erstaunlich, was alles geht, wenn man ein paar Strukturen einfach so zusammen kopiert.
    Für den Stromzähler wird der Dämon ja eigentlich nie beendet, heißt: Die Funktion mysql_close (mysql) wird nie aufgerufen. Gibt es eine Möglichkeit den Dämon "ordentlich" zu beenden, sodass auch mysql_close (mysql) aufgerufen wird? Ich habe mit sudo killall gpiod das natürlich beenden können. Dabei könnte es aber doch möglicherweise Ärger mit mysql geben, wenn das nicht ordentlich geschlossen wird. Oder?

    Moin,

    na klar funktioniert das ... was hast Du denn gedacht ;) ...

    Zum Beenden würde sich imho anbieten, einen handler für z.B. SIG_USR1 zu installieren. Dann kannst Du dort den close machen.
    Ist zwar ein bisschen blöd, weil Du den mysql-handle dann global machen musst, aber ich denke mit solchen kleinen Unschönheiten kann man in diesem Fall leben.

    Na dann noch viel Spass damit ...

    Nachtrag:
    den daemonize()-Aufruf würde ich erst nach der ganzen Initialisierung und dem Verbindungsaufbau zur Datenbank machen.
    Deine fprintf(stderr ...) landen nämlich nach dem Aufruf von daemonize() im Nirvana ...
    Um Fehler- und Status-Infos auszugeben, würde sich ein openlog() anbieten - entweder direkt am Ende von daemonize() oder im main nach dem Aufruf der Funktion. Das Logging bzw. Fehlermeldungen gibst Du dann mit syslog() aus - sie landen in der gleichnamigen Datei unter /var/log.
    Als facility beim openlog verwende ich LOG_LOCAL0, als options nehme ich (LOG_PID | LOG_CONS| LOG_NDELAY).

    Ach ja zu SIG_USR1: mit einem kill -SIG_USR1 <pid> löst Du den Aufruf des installierten handlers aus. Es wäre deshalb sinnvoll, die pid in eine Datei zu schreiben.


    ciao,
    -ds-

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!