Autostart von C Programm unter rc.local startet nicht

  • Hallo zusammen,

    ich habe ein kleines Programm unter C, welches ich gern im Autostart ausführen lassen möchte. Das Programm selbst beinhaltet PIGPIO und eine Loop, in der permanent die serielle Schnittstelle für ein RFID-Lesegerät abgefragt wird. Wenn ich das Programm manuell starte, klappt alles prima. Das Programm beinhaltet keine Grafik. Test1 ist die ausführbare Datei.

    Code
    pi@raspberrypi:~/Test $ pwd
    /home/pi/Test
    pi@raspberrypi:~/Test $ ls -l
    total 28
    -rwxr-xr-x 1 pi pi 15348 Jul 2 17:47 Test1
    -rw-r--r-- 1 pi pi 11206 Jul 2 17:46 Test1.c

    In rc.local habe ich am Ende der Datei (vor exit 0) folgende Zeile eingefügt

    Code
    /home/pi/Test/Test1 &

    Nach einem reboot tut sich allerdings nichts. Zu Anfang hatte ich das & vergessen, was ja dazu führen müsste, dass er sich in der Loop beim hochfahren einfängt, das ist aber auch nicht passiert...

    Daher denke ich, dass ich grundlegend etwas falsch mache.
    An dieser Stelle schon einmal vielen Dank für eure Hilfe.

    Einmal editiert, zuletzt von Blackbird9 (3. Juli 2016 um 09:46)

  • Autostart von C Programm unter rc.local startet nicht? Schau mal ob du hier fündig wirst!

  • Die rc.local ist imho veraltet. Starte es über eine systemd-unit mit type=idle und RemainAfterExit=yes. Und achte unbedingt darauf, dass dein Programm mit exit 0 zurückkehrt. Ansonsten wird systemd den Job möglicherweise einfach als "fehlerhaft" killen.

  • Hallo ThomasL,

    danke für deine schnelle Antwort.
    Musste mich jetzt zunächst einmal etwas einlesen über systemd weil mir das bei meiner bisherigen Recherche noch nicht über den Weg gelaufen ist.
    Hab nun mal eine solche Service Datei erstellt.

    Die Datei habe ich als Test1.service abgespeichert.
    Kommt das in etwa so hin? Die Datei würde ich dann in /etc/systemd/system/ abspeichern. Muss die Datei dann noch irgendwie aktiviert werden?

    Mit dem exit 0 bin ich leider auch noch nicht vertraut. Muss ich das am Ende von main als returnwert zurückgeben?

    Mein Programm hat vereinfacht als Pseudocode diesen Aufbau

    Kannst du mir sagen an welcher Stelle das exit 0 eingefügt werden müsste?

    Danke :thumbs1:

    Einmal editiert, zuletzt von Blackbird9 (3. Juli 2016 um 16:09)

  • Kleines Update
    Hab das Programm nun durch den Autostart zum Laufen gebracht.
    Vielen Dank nochmals an ThomasL.

    Mit systemctl enable Test1.service wird die Datei aktiviert. Klappt soweit ohne Probleme.

    Dennoch würde ich noch gerne wissen was es mit dem exit 0 auf sich hat da das Programm 24/7 laufen soll. Nicht dass es glücklicherweise jetzt die ersten paar Tage läuft und dann durch Speicherüberlauf oder sonst einen Fehler abschmiert.

    Gruß


  • Dennoch würde ich noch gerne wissen was es mit dem exit 0 auf sich hat da das Programm 24/7 laufen soll. Nicht dass es glücklicherweise jetzt die ersten paar Tage läuft und dann durch Speicherüberlauf oder sonst einen Fehler abschmiert.


    Vergiss es .... das betrifft nur geforkte Prozesse, wenn nur der Fork weiterlaufen soll. Deine Angaben waren vorher sehr vage.

    In der Unit kannst Du noch den "After=syslog.target" entfernen, der ist völlig überflüssig, da der Start Deines Programms durch "idle" sowieso ganz am Ende des Boots durchgefüht wird.
    Und die Zeilen mit den Parametern "start", "stop" und "restart" kannst Du ebenfalls entfernen, da sie (so weit ich das in Deinem Programm erkennen kann) auch gar nicht abgefragt werden. Der Zweck dieser Parameter ist, dem Programm die Möglichkeit zu Unterscheiden zu geben, ob gebootet oder runtergefahren wird oder ob. z.B. nach einer conf-Änderung ein Daemon neu gestartet werden muss. Zum Beispiel kann es ja beim Runterfahren noch notwendig sein, gewisse offene Prozesse/Dateien vorher sauber zu schließen, bevor das Proigramm durch shutdown.target "terminated" wird. Und wenn Du diese Parameter nicht im Programm berücksichtigst, sie aber trotzdem verwendest, kann das ggf. durchaus unerwünschte Effekte nachsichziehen. Im Moment reicht bei Deinem Programm also der Eintrag:

    Code
    ExecStart=/home/pi/Test/Test1

    exit 0 bedeutet, dass das Programm ohne Fehler beendet wurde. Das würde ich auf jeden Fall noch einbauen, einfach auch um zu verhindern, dass systemd evtl. falsche Fehlerlogeinträge ins Journal vornimmt. Und exit 0 beendet naturlich main(), es sei denn, Du willst mit einem abweichenden exit-Code einen Fehlercode zurückgeben...... ich würde also ein "ordentliches" Ende sicherstellen.... die Regel (so wie ich das bei Linux kennengelernt habe) ist 0, wenn kein Fehler vorliegt.

    Einmal editiert, zuletzt von WinterUnit16246 (3. Juli 2016 um 22:37)

  • Okey hab die Änderungen vorgenommen. Der Pi braucht jetzt etwas länger beim Herunterfahren, aber runtergefahren wird er in der Regel eh nicht oft. Soll ja permanent arbeiten.

    Das exit 0 hab ich wie folgt eingefügt:

    Code
    int main()
    {
      while(1)
      {
        machwas
      }
    exit (0);
    return 0;
    }


    Sorry dass ich ständig nachfrag aber wird das exit nun überhaupt beachtet? Durch while(1) würde das Programm doch nie an die Stelle von exit kommen?

  • exit und return ist doppelt gemoppelt. Soweit ich mich erinnere, reicht hier in dem Fall return 0 völlig aus. Allerdings, wenn es keine Abbruchbedingung in der while-schleife gibt, wird das wohl wirklich nie ausgeführt. Sorry, da enthalte ich mich doch besser mal. Meine 10 jährige Zeit, als ich noch beruflich C++ programmiert habe, liegt einfach zu lange zurück ... ich habe schon zu viel vergessen : -(

  • das Programm müsste halt als daemon gestartet werden, so wie z.B. der -> shutdown-daemon <- von mir ...
    Dann klappts auch mit dem exit()


    Ich glaube, damit hat es nix zu tun... sondern eher damit, dass er in seiner while-Schleife überhaupt keinen Abbruch vorgesehen hat. Das Programm wird anscheinend nur beendet, wenn es von "außerhalb" (z.B. shutdown.target) hart terminated wird.

    Einmal editiert, zuletzt von WinterUnit16246 (4. Juli 2016 um 23:11)

  • Ich weiss natürlich was ein daemon ist, wie er funktioniert und nach welchen Regeln er für welche Zwecke eingesetzt wird. Das hat aber überhaupt nichts mit dem grundsätzlichen Ansatz meiner Aussage zu tun. Ich denke nämlich, das gilt imho gleichermaßen für Daemons und normale Programme .... und zwar zu überlegen, wie man programmiert, ob hartes terminate oder ordentliches Beenden. Und in dem Beispiel-Loop (oder eben darüber) sehe ich für "ordentliches Beenden" keine Absicht ... z.b. in Form eines Eventhandlers:
    void (*signal (int sig, void (*func)(int)))(int);

    Allerdings gestehe ich jetzt Unsicherheit darüber, ob systemd zuerst einen sigterm versucht oder gleich mit stumpfer Klinge rasiert. Das werde ich aus Neugier dieser Tage mal testen ....und sofern das hier noch 'offen' ist kurz berichten.

    Einmal editiert, zuletzt von WinterUnit16246 (5. Juli 2016 um 09:30)

  • Moin

    Es ist, wie ich vermutet habe.... systemd versucht einem Prozess mit SIGTERM die Möglichkeit zu geben, sich sauber zu beenden. Ich habe gerade mein allererstes C-Programm unter Linux geschrieben..... :) ... ein kleines Testprogramm, welches mit while(1) bis zum nächsten Stromausfall läuft. Im Programm habe ich aber auch einen Signal-Handler implementiert, der momentan zum Testen nur auf SIGTERM reagiert. Und wie vermutet, wenn ich das Programm "systemctl start tomstest.service" im Hintergrund starte und irgendwann den Prozess mit "systemctl stop tomstest.service" beende, bekommt das Programm das Signal SIGTERM und kann seinen Loop beenden um ggf. Abschlussarbeiten durchzuführen. Das, was das Programm tut, kann im Journal anhand 2 Messages nachgesehen werden oder sogar zur Laufzeit in einem eigenen Terminal mit "journalctl -f".... dann sieht man es sogar live und unmittelbar.

    Wenn Interesse besteht, poste ich mein Testprogramm und die Service-Unit hier.

    Einmal editiert, zuletzt von WinterUnit16246 (5. Juli 2016 um 19:26)

  • Moin,

    @dreamsahder
    danke für deinen Vorschlag, weil ich mich bei Daemons auch nicht auskenne und mich erst wieder einlesen müsste, versuch ich das mit dem Daemon mal bei meinem nächsten Programm.

    @ThomasL
    Ja ich hab großes Interesse :bravo2:

    Gruß

  • Ja ich hab großes Interesse

    Ja klar, hier ist es....

    Code
    nano /home/thomas/Install/tomstest.cpp

    Hier die zum Test verwendete Service-Unit:

    Code
    nano /etc/systemd/system/tomstest.service

    Das Programm erzeugt mit:

    Code
    cd ~/Install
    g++ tomstest.cpp -o tomstest

    Das Programm gestartet und gestoppt mit:

    Code
    cd /etc/systemd/system
    systemctl start tomstest.service
    systemctl stop tomstest.service

    Noch ein paar abschließende Tips:
    1. Zum Testen habe ich mir in einem eigenen Fenster das Journal anzeigen lassen.... mit journalctl -f . Das hat zur Folge, dass fliessend alle neuen Journaleinträge angezeigt werden. Wenn das bei Dir nicht angezeigt wird, liegt es daran, dass Du noch das alte rsyslog nutzt. Dann musst Du dir halt mit dmesg die letzten Zeilen manuell ansehen... oder mit dmesg | grep tomstest -i manuell suchen. Wenn Du allerdings Pech hast, stehen die Einträge aber nur unter messages, da musst Du dann halt mit "cat" ebenfalls manuell reinsehen und grep'en. In diesem Fall empfehle ich Dir aber, diesen ganzen rsyslog-Schrott zu deinstallieren und systemd-journald zu aktivieren. Das hat nur Vorteile.

    2. Ebenfalls zum Testen habe ich in einem zweiten eigenen Fenster htop gestartet und die Ausgabe auf "tomstest" gefiltert. Dann sieht man sofort, ob das Programm lebt und wann es nach dem systemctl stop nicht mehr leb und wie es die CPU belastet. Idealerweise ist das synchron zum journalctl-Fenster.

    3. Wenn Du so eine Endlos-While-Schleife programmierst, dann teste mal den Effekt mit und ohne dem in meinem Programm vorhandenen Sleep-Statement und beobachte die CPU-Last im htop-Fenster. Im Regelfall benötigt man keine Echtzeit in üblichen Schleifen, sofern man keine zeitkritischen Events verpassen will. In solchen Fällen würde ich aber prüfen, ob ich nicht besser einen IRQ verwenden kann. Tatsache ist jedoch der Effekt, wenn Du Deine Schleife permanent durchrennen läßt, und dass womöglicherweise zu 99,9% im Leerlauf, weil gerade nix zu tun ist, dann geht die CPU-Last volle Kanne hoch, die Termperatur steigt, der Stromverbrauch steigt, das Programm beansprucht möglicherweise die CPU fast exklusiv, andere Jobs werden benachteiligt. Also solche Schleifen sollte man sehr umsichtig programmieren und dem restlichen System immer auch Idle-Zeit gewähren.

    4. Die Service-Unit benötigt für den Systemstart noch ein "after-Statement", wenn du das Prgramm beim Systemstart automatisch starten lassen willst. Mit After=network.target machst Du nichts verkehrt, Dein Programm würde erst dann gestartet werden, wenn das Netzwerk gestartet ist. Sofern Du auch noch NFS-Mounts benötigst, solltest Du /etc/init.d/networking deaktivieren (würde ich sowieso empfehlen) und stattdessen das Netzwerk über systemd-networkd starten und dann noch das after-Statement um systemd-networkd-wait-online.service ergänzen. Dazu muss es natürlich aber auch enabled werden.

    HTH

    Einmal editiert, zuletzt von WinterUnit16246 (6. Juli 2016 um 23:01)

Jetzt mitmachen!

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