Erstellen eines Daemons unter Linux

110

Unter Linux möchte ich einen Daemon hinzufügen, der nicht gestoppt werden kann und der Änderungen am Dateisystem überwacht. Wenn Änderungen festgestellt werden, sollte der Pfad zu der Konsole, auf der er gestartet wurde, sowie ein Zeilenumbruch geschrieben werden.

Ich habe bereits den Code zum Ändern des Dateisystems fast fertig, kann aber nicht herausfinden, wie ein Daemon erstellt wird.

Mein Code stammt von hier: http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html

Was tun nach der Gabelung?

int main (int argc, char **argv) {

  pid_t pID = fork();
  if (pID == 0)  {              // child
          // Code only executed by child process    
      sIdentifier = "Child Process: ";
    }
    else if (pID < 0) {
        cerr << "Failed to fork" << endl;
        exit(1);
       // Throw exception
    }
    else                                   // parent
    {
      // Code only executed by parent process

      sIdentifier = "Parent Process:";
    }       

    return 0;
}
chrisMe
quelle
1
Mögliches Duplikat: stackoverflow.com/q/5384168/1076451
Chimera
1
Mögliches Duplikat von: stackoverflow.com/questions/5384168/… für den Daemonize-Teil, stackoverflow.com/questions/931093/… für die Dateisystemüberwachung
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功
Wenn Sie keine POSIX-Konformität benötigen, sind Sie möglicherweise an der inotifyAPI interessiert . Sehen:inotify_init , inotify_add_watch, inotify_rm_watch.
patryk.beza

Antworten:

216

Unter Linux möchte ich einen Daemon hinzufügen, der nicht gestoppt werden kann und der Änderungen am Dateisystem überwacht. Wenn Änderungen festgestellt werden, sollte der Pfad zur Konsole, auf der er gestartet wurde, + eine neue Zeile geschrieben werden.

Daemons arbeiten im Hintergrund und gehören (normalerweise ...) nicht zu einem TTY. Deshalb können Sie stdout / stderr nicht so verwenden, wie Sie es wahrscheinlich möchten. Normalerweise ein Syslog-Daemon ( syslogd ) zum Protokollieren von Nachrichten in Dateien verwendet (Debug, Fehler, ...).

Daneben sind einige Schritte erforderlich um einen Prozess zu dämonisieren.


Wenn ich mich richtig erinnere, sind diese Schritte:

  • Gabel Sie den übergeordneten Prozess aus und lassen Sie ihn beenden, wenn das Gabeln erfolgreich war. -> Da der übergeordnete Prozess beendet wurde, wird der untergeordnete Prozess jetzt im Hintergrund ausgeführt.
  • setsid - eine neue Sitzung. Der aufrufende Prozess wird zum Leiter der neuen Sitzung und zum Prozessgruppenleiter der neuen Prozessgruppe. Der Prozess ist jetzt von seinem steuernden Terminal (CTTY) getrennt.
  • Signale fangen - ignorieren und / oder verarbeiten.
  • Gabel wieder und lassen Sie den übergeordneten Prozess beenden, um sicherzustellen, dass Sie den sitzungsführenden Prozess entfernen. (Nur Sitzungsleiter dürfen wieder eine TTY erhalten.)
  • chdir - Ändert das Arbeitsverzeichnis des Dämons.
  • umask - Ändern Sie die entsprechend den Anforderungen des Dämons.
  • schließen - Schließen Sie alle geöffneten Dateideskriptoren, die möglicherweise vom übergeordneten Prozess geerbt werden.

Um Ihnen einen Ausgangspunkt zu geben: Sehen Sie sich diesen Skelettcode an, der die grundlegenden Schritte zeigt. Dieser Code kann jetzt auch auf GitHub: Basic-Skelett eines Linux-Daemons gegabelt werden

/*
 * daemonize.c
 * This example daemonizes a process, writes a few log messages,
 * sleeps 20 seconds and terminates afterwards.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>

static void skeleton_daemon()
{
    pid_t pid;

    /* Fork off the parent process */
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* On success: The child process becomes session leader */
    if (setsid() < 0)
        exit(EXIT_FAILURE);

    /* Catch, ignore and handle signals */
    //TODO: Implement a working signal handler */
    signal(SIGCHLD, SIG_IGN);
    signal(SIGHUP, SIG_IGN);

    /* Fork off for the second time*/
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* Set new file permissions */
    umask(0);

    /* Change the working directory to the root directory */
    /* or another appropriated directory */
    chdir("/");

    /* Close all open file descriptors */
    int x;
    for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)
    {
        close (x);
    }

    /* Open the log file */
    openlog ("firstdaemon", LOG_PID, LOG_DAEMON);
}
int main()
{
    skeleton_daemon();

    while (1)
    {
        //TODO: Insert daemon code here.
        syslog (LOG_NOTICE, "First daemon started.");
        sleep (20);
        break;
    }

    syslog (LOG_NOTICE, "First daemon terminated.");
    closelog();

    return EXIT_SUCCESS;
}


  • Kompilieren Sie den Code: gcc -o firstdaemon daemonize.c
  • Starten Sie den Daemon: ./firstdaemon
  • Überprüfen Sie, ob alles richtig funktioniert: ps -xj | grep firstdaemon

  • Die Ausgabe sollte dieser ähnlich sein:

+ ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- +
| PPID | PID | PGID | SID | TTY | TPGID | STAT | UID | ZEIT | CMD |
+ ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- +
| 1 | 3387 | 3386 | 3386 | ? | -1 | S | 1000 | 0:00 | ./ |
+ ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- +

Was Sie hier sehen sollten, ist:

  • Der Daemon hat kein steuerndes Terminal ( TTY =? )
  • Die übergeordnete Prozess-ID ( PPID ) ist 1 (Der Init-Prozess)
  • Die PID! = SID, was bedeutet, dass unser Prozess NICHT der Sitzungsleiter ist
    (wegen der zweiten Gabelung ())
  • Da PID! = SID, kann unser Prozess die Kontrolle über ein TTY nicht wieder übernehmen

Lesen des Syslogs:

  • Suchen Sie Ihre Syslog-Datei. Meins ist hier:/var/log/syslog
  • Machen Sie eine: grep firstdaemon /var/log/syslog

  • Die Ausgabe sollte dieser ähnlich sein:

  firstdaemon [3387]: Der erste Daemon wurde gestartet.
  firstdaemon [3387]: Der erste Daemon wurde beendet.


Ein Hinweis: In der Realität möchten Sie auch einen Signalhandler implementieren und die Protokollierung ordnungsgemäß einrichten (Dateien, Protokollierungsstufen ...).

Weiterführende Literatur:

Pascal Werkl
quelle
Wow, danke! Das ist großartig. Also muss ich meinen Code in die while-Schleife einfügen und das wars?
ChrisMe
Grundsätzlich ja. Dieser Code ist jedoch nur ein Beispiel. Es hängt ganz davon ab, was Sie mit einem Daemon-Prozess erreichen möchten . Lesen Sie auch diese Antwort: @Edwin
Pascal Werkl
1
fork()Warum nicht einfach anstelle des zweiten verwenden setsid()?
Chimäre
1
Beachten Sie, dass die sigaction()Funktion einen umfassenderen und zuverlässigeren Mechanismus zur Steuerung von Signalen bietet. neue Anwendungen sollten sigaction()eher als verwenden signal().
patryk.beza
4
Den Zuschauern sollte angemerkt werden, dass diese Methode der "alte" Weg ist. Der neue empfohlene Weg , um einen Dämon zu erstellen ist mit dem hier „new style Daemon“: 0pointer.de/public/systemd-man/daemon.html#New-Style%20Daemons oder
Starlord
30

man 7 daemonbeschreibt detailliert, wie ein Daemon erstellt wird. Meine Antwort ist nur ein Auszug aus diesem Handbuch.

Es gibt mindestens zwei Arten von Dämonen:

  1. traditionelle SysV- Dämonen (im alten Stil ),
  2. systemd daemons ( neuer Stil ).

SysV Daemons

Wenn Sie an einem herkömmlichen SysV- Daemon interessiert sind , sollten Sie die folgenden Schritte ausführen :

  1. Schließen aller offenen Datei - Deskriptoren , ausgenommen Standardeingabe , Ausgang und Fehler ( das heißt die ersten drei Datei - Deskriptoren , 0, 1, 2). Dadurch wird sichergestellt, dass kein versehentlich übergebener Dateideskriptor im Dämonprozess verbleibt. Unter Linux wird dies am besten durch Durchlaufen implementiert /proc/self/fd, wobei die Iteration vom Dateideskriptor 3 auf den von getrlimit()for zurückgegebenen Wert zurückgesetzt wird RLIMIT_NOFILE.
  2. Setzen Sie alle Signalhandler auf ihre Standardeinstellungen zurück. Dies geschieht am besten, indem die verfügbaren Signale bis zur Grenze durchlaufen _NSIGund auf zurückgesetzt werden SIG_DFL.
  3. Setzen Sie die Signalmaske mit zurück sigprocmask().
  4. Bereinigen Sie den Umgebungsblock, indem Sie Umgebungsvariablen entfernen oder zurücksetzen, die sich negativ auf die Daemon-Laufzeit auswirken können.
  5. Rufen Sie an fork(), um einen Hintergrundprozess zu erstellen.
  6. Rufen Sie setsid()im untergeordneten Element an , um die Verbindung zu einem Terminal zu trennen und eine unabhängige Sitzung zu erstellen .
  7. Rufen Sie im Kind fork()erneut auf, um sicherzustellen, dass der Dämon ein Terminal nie wieder neu erwerben kann.
  8. Rufen Sie exit()das erste untergeordnete Element auf, sodass nur das zweite untergeordnete Element (der eigentliche Dämonprozess) vorhanden ist. Dadurch wird sichergestellt, dass der Dämonprozess erneut auf init / PID 1 übergeordnet wird, wie dies bei allen Dämonen der Fall sein sollte.
  9. In dem Dämon - Prozess, eine Verbindung /dev/nullzur Standardeingabe , Ausgang und Fehlern .
  10. Setzen Sie im Daemon-Prozess den Wert umaskauf 0 zurück, damit die Dateimodi an übergeben open()werden mkdir()und steuern Sie direkt den Zugriffsmodus der erstellten Dateien und Verzeichnisse.
  11. Ändern Sie im Daemon-Prozess das aktuelle Verzeichnis in das Stammverzeichnis ( /), um zu vermeiden, dass der Daemon die Bereitstellung von Mount-Punkten unfreiwillig blockiert.
  12. Schreiben Sie im Daemon-Prozess die Daemon- PID (wie von zurückgegeben getpid()) beispielsweise in eine PID-Datei /run/foobar.pid(für einen hypothetischen Daemon "foobar"), um sicherzustellen, dass der Daemon nicht mehr als einmal gestartet werden kann. Dies muss rennfrei implementiert werden, damit die PID-Datei nur aktualisiert wird, wenn gleichzeitig überprüft wird, ob die zuvor in der PID-Datei gespeicherte PID nicht mehr vorhanden ist oder zu einem fremden Prozess gehört.
  13. Löschen Sie im Daemon-Prozess nach Möglichkeit Berechtigungen.
  14. Benachrichtigen Sie den ursprünglich gestarteten Prozess über den Daemon-Prozess, dass die Initialisierung abgeschlossen ist. Dies kann über eine unbenannte Pipe oder einen ähnlichen Kommunikationskanal implementiert werden, der vor dem ersten erstellt wurde fork()und daher sowohl im ursprünglichen als auch im Daemon-Prozess verfügbar ist.
  15. Rufen Sie exit()den ursprünglichen Prozess auf. Der Prozess, der den Daemon aufgerufen hat, muss sich darauf verlassen können, dass dies exit()geschieht, nachdem die Initialisierung abgeschlossen ist und alle externen Kommunikationskanäle eingerichtet und zugänglich sind.

Beachten Sie diese Warnung:

Die BSD- daemon()Funktion sollte nicht verwendet werden, da sie nur eine Teilmenge dieser Schritte implementiert .

Ein Daemon, der Kompatibilität mit SysV-Systemen bieten muss, sollte das oben beschriebene Schema implementieren. Es wird jedoch empfohlen, dieses Verhalten über ein Befehlszeilenargument optional und konfigurierbar zu machen, um das Debuggen zu vereinfachen und die Integration in Systeme mit systemd zu vereinfachen.

Beachten Sie, dass dies daemon()nicht POSIX- kompatibel ist.


Daemons im neuen Stil

Für Dämonen neuen Stils werden die folgenden Schritte empfohlen:

  1. Wenn SIGTERMempfangen, fahren Sie den Daemon herunter und beenden Sie ihn sauber.
  2. Wenn SIGHUPempfangen, laden Sie die Konfigurationsdateien neu, falls dies zutrifft.
  3. Geben Sie einen korrekten Exit-Code für den Hauptdämonprozess an, da dieser vom Init-System zum Erkennen von Servicefehlern und -problemen verwendet wird. Es wird empfohlen, das in den LSB-Empfehlungen für SysV-Init-Skripte definierte Exit-Code-Schema zu befolgen .
  4. Wenn möglich und zutreffend, legen Sie die Steuerschnittstelle des Dämons über das D-Bus-IPC- System offen und rufen Sie als letzten Schritt der Initialisierung einen Busnamen ab.
  5. Stellen Sie für die Integration in systemd eine .service- Einheitendatei bereit , die Informationen zum Starten, Stoppen und anderweitigen Verwalten des Dämons enthält. Siehe systemd.service(5)für Details.
  6. Verlassen Sie sich so weit wie möglich auf die Funktionalität des Init-Systems, um den Zugriff des Dämons auf Dateien, Dienste und andere Ressourcen zu beschränken. Wenn Sie also systemd verwenden, verlassen Sie sich auf die Kontrolle der Ressourcenbeschränkungen von systemd, anstatt Ihre eigene zu implementieren. Verlassen Sie sich auf das Löschen der Berechtigungen von systemd Code, anstatt ihn im Daemon zu implementieren, und ähnliches. Siehe systemd.exec(5)für die verfügbaren Steuerelemente.
  7. Wenn D-Bus verwendet wird, aktivieren Sie Ihren Daemon-Bus, indem Sie eine Konfigurationsdatei für die Aktivierung des D-Bus-Dienstes bereitstellen . Dies hat mehrere Vorteile: Ihr Daemon kann bei Bedarf träge gestartet werden. Es kann parallel zu anderen Daemons gestartet werden, die dies erfordern. Dies maximiert die Parallelisierung und die Startgeschwindigkeit . Ihr Daemon kann bei einem Fehler neu gestartet werden, ohne dass Busanforderungen verloren gehen, da der Bus Anforderungen für aktivierbare Dienste in die Warteschlange stellt. Siehe unten für Details.
  8. Wenn Ihr Daemon über einen Socket Dienste für andere lokale Prozesse oder Remoteclients bereitstellt, sollte dieser nach dem unten beschriebenen Schema für den Socket aktiviert werden . Wie die D-Bus-Aktivierung ermöglicht dies das On-Demand-Starten von Diensten sowie eine verbesserte Parallelisierung des Dienststarts. Bei Protokollen ohne Status (wie Syslog, DNS) kann ein Daemon, der eine Socket-basierte Aktivierung implementiert, neu gestartet werden, ohne dass eine einzelne Anforderung verloren geht. Siehe unten für Details.
  9. Falls zutreffend, sollte ein Daemon das Init-System über die sd_notify(3)Schnittstelle über den Abschluss des Startvorgangs oder Statusaktualisierungen informieren .
  10. Anstatt den syslog()Aufruf zu verwenden, um sich direkt beim System-Syslog-Dienst anzumelden, kann sich ein Daemon neuen Stils dafür entscheiden, einfach bei Standardfehler über zu protokollieren fprintf(), der dann vom Init-System an Syslog weitergeleitet wird. Wenn Protokollebenen erforderlich sind, können diese codiert werden, indem einzelnen Protokollzeilen Zeichenfolgen wie "<4>" vorangestellt werden (für Protokollebene 4 "WARNUNG" im Syslog-Prioritätsschema), die einem ähnlichen Stil wie das printk()Level-System des Linux-Kernels folgen . Einzelheiten finden Sie unter sd-daemon(3)und systemd.exec(5).

Um mehr zu erfahren, lesen Sie das Ganze man 7 daemon.

patryk.beza
quelle
11

Sie können unter Linux keinen Prozess erstellen, der nicht beendet werden kann. Der Root-Benutzer (uid = 0) kann ein Signal an einen Prozess senden, und es gibt zwei Signale, die nicht abgefangen werden können: SIGKILL = 9, SIGSTOP = 19. Andere Signale (wenn sie nicht erfasst werden) können ebenfalls zur Beendigung des Prozesses führen.

Möglicherweise möchten Sie eine allgemeinere Daemonisierungsfunktion, in der Sie einen Namen für Ihr Programm / Ihren Daemon und einen Pfad zum Ausführen Ihres Programms angeben können (möglicherweise "/" oder "/ tmp"). Möglicherweise möchten Sie auch Dateien für stderr und stdout (und möglicherweise einen Steuerpfad mit stdin) bereitstellen.

Hier sind die notwendigen Includes:

#include <stdio.h>    //printf(3)
#include <stdlib.h>   //exit(3)
#include <unistd.h>   //fork(3), chdir(3), sysconf(3)
#include <signal.h>   //signal(3)
#include <sys/stat.h> //umask(3)
#include <syslog.h>   //syslog(3), openlog(3), closelog(3)

Und hier ist eine allgemeinere Funktion,

int
daemonize(char* name, char* path, char* outfile, char* errfile, char* infile )
{
    if(!path) { path="/"; }
    if(!name) { name="medaemon"; }
    if(!infile) { infile="/dev/null"; }
    if(!outfile) { outfile="/dev/null"; }
    if(!errfile) { errfile="/dev/null"; }
    //printf("%s %s %s %s\n",name,path,outfile,infile);
    pid_t child;
    //fork, detach from process group leader
    if( (child=fork())<0 ) { //failed fork
        fprintf(stderr,"error: failed fork\n");
        exit(EXIT_FAILURE);
    }
    if (child>0) { //parent
        exit(EXIT_SUCCESS);
    }
    if( setsid()<0 ) { //failed to become session leader
        fprintf(stderr,"error: failed setsid\n");
        exit(EXIT_FAILURE);
    }

    //catch/ignore signals
    signal(SIGCHLD,SIG_IGN);
    signal(SIGHUP,SIG_IGN);

    //fork second time
    if ( (child=fork())<0) { //failed fork
        fprintf(stderr,"error: failed fork\n");
        exit(EXIT_FAILURE);
    }
    if( child>0 ) { //parent
        exit(EXIT_SUCCESS);
    }

    //new file permissions
    umask(0);
    //change to path directory
    chdir(path);

    //Close all open file descriptors
    int fd;
    for( fd=sysconf(_SC_OPEN_MAX); fd>0; --fd )
    {
        close(fd);
    }

    //reopen stdin, stdout, stderr
    stdin=fopen(infile,"r");   //fd=0
    stdout=fopen(outfile,"w+");  //fd=1
    stderr=fopen(errfile,"w+");  //fd=2

    //open syslog
    openlog(name,LOG_PID,LOG_DAEMON);
    return(0);
}

Hier ist ein Beispielprogramm, das zu einem Daemon wird, herumhängt und dann geht.

int
main()
{
    int res;
    int ttl=120;
    int delay=5;
    if( (res=daemonize("mydaemon","/tmp",NULL,NULL,NULL)) != 0 ) {
        fprintf(stderr,"error: daemonize failed\n");
        exit(EXIT_FAILURE);
    }
    while( ttl>0 ) {
        //daemon code here
        syslog(LOG_NOTICE,"daemon ttl %d",ttl);
        sleep(delay);
        ttl-=delay;
    }
    syslog(LOG_NOTICE,"daemon ttl expired");
    closelog();
    return(EXIT_SUCCESS);
}

Beachten Sie, dass SIG_IGN anzeigt, dass das Signal abgefangen und ignoriert werden soll. Sie können einen Signalhandler erstellen, der den Signalempfang protokollieren und Flags setzen kann (z. B. ein Flag, das ein ordnungsgemäßes Herunterfahren anzeigt).

ChuckCottrill
quelle
8

Versuchen Sie es mit der daemonFunktion:

#include <unistd.h>

int daemon(int nochdir, int noclose);

Von der Manpage :

Die Funktion daemon () ist für Programme vorgesehen, die sich vom steuernden Terminal lösen und im Hintergrund als Systemdämonen ausgeführt werden möchten.

Wenn nochdir Null ist, ändert daemon () das aktuelle Arbeitsverzeichnis des aufrufenden Prozesses in das Stammverzeichnis ("/"). Andernfalls bleibt das aktuelle Arbeitsverzeichnis unverändert.

Wenn noclose Null ist, leitet daemon () die Standardeingabe, die Standardausgabe und den Standardfehler nach / dev / null um. Andernfalls werden keine Änderungen an diesen Dateideskriptoren vorgenommen.

weiyin
quelle
2
Beachten Sie, dass im daemon(7)Handbuch Schritte zum Erstellen eines Dämons erwähnt werden und Folgendes gewarnt wird: Die BSD- daemon()Funktion sollte nicht verwendet werden, da sie nur eine Teilmenge dieser Schritte implementiert. daemonDie Funktion wurde erstmals in 4.4BSD angezeigt und ist nicht POSIX-kompatibel .
patryk.beza
2
Beachten Sie auch, dass sich die Warnung zur Verwendung von daemon () im alten SysV-Abschnitt der Manpage von daemon (7) befindet . Von der Verwendung von daemon () wird für systemd nicht abgeraten.
Greg McPherran
7

Ich kann bei der ersten Anforderung "Ein Daemon, der nicht gestoppt werden kann ..." anhalten.

Nicht möglich, mein Freund; Sie können dies jedoch auch mit einem viel besseren Tool, einem Kernelmodul, erreichen.

http://www.infoq.com/articles/inotify-linux-file-system-event-monitoring

Alle Dämonen können gestoppt werden. Einige sind leichter zu stoppen als andere. Sogar ein Daemon-Paar mit dem Partner in der Warteschleife, das den Partner bei Verlust wieder erscheinen lässt, kann gestoppt werden. Man muss nur etwas härter daran arbeiten.

Edwin Buck
quelle
7
Ich denke, mit "Ein Dämon, der nicht gestoppt werden kann" meint der Autor tatsächlich, dass der Dämon immer Hintergrund läuft, wenn die Sitzung beendet wird.
FaceBro
6

Wenn Ihre App eine der folgenden ist:

{
  ".sh": "bash",
  ".py": "python",
  ".rb": "ruby",
  ".coffee" : "coffee",
  ".php": "php",
  ".pl" : "perl",
  ".js" : "node"
}

und Sie haben nichts gegen eine NodeJS-Abhängigkeit, dann installieren Sie NodeJS und dann:

npm install -g pm2

pm2 start yourapp.yourext --name "fred" # where .yourext is one of the above

pm2 start yourapp.yourext -i 0 --name "fred" # run your app on all cores

pm2 list

So lassen Sie alle Apps beim Neustart laufen (und pm2 daemonisieren):

pm2 startup

pm2 save

Jetzt kannst du:

service pm2 stop|restart|start|status

(Außerdem können Sie problemlos nach Codeänderungen in Ihrem App-Verzeichnis suchen und den App-Prozess automatisch neu starten, wenn eine Codeänderung auftritt.)

danday74
quelle
2
Dies hat nichts mit C. zu tun
Melpomene
4
Ich schätze, dass es ein C-Tag gibt. OP erwähnt jedoch keine Anforderung in Bezug auf C in der Frage. Der Titel erstellt einen Dämon unter Linux. Diese Antwort erfüllt das.
danday74
1
Oh, du hast Recht. Es ist mit C gekennzeichnet, aber die eigentliche Anforderung ist C ++ (wie aus dem OP-Code und dem verlinkten Artikel hervorgeht).
Melpomene
3

Durch Aufrufen von fork () haben Sie einen untergeordneten Prozess erstellt. Wenn der Fork erfolgreich ist (Fork hat eine PID ungleich Null zurückgegeben), wird die Ausführung ab diesem Punkt innerhalb des untergeordneten Prozesses fortgesetzt. In diesem Fall möchten wir den übergeordneten Prozess ordnungsgemäß beenden und dann unsere Arbeit im untergeordneten Prozess fortsetzen.

Vielleicht hilft das: http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html

Doug Morrow
quelle
2

Ein Daemon ist nur ein Prozess im Hintergrund. Wenn Sie Ihr Programm beim Booten des Betriebssystems unter Linux starten möchten, fügen Sie Ihren Startbefehl zu /etc/rc.d/rc.local (nach allen anderen Skripten ausführen) oder /etc/startup.sh hinzu

Unter Windows erstellen Sie einen Dienst, registrieren den Dienst und stellen ihn so ein, dass er beim Booten im Bereich Verwaltung -> Dienste automatisch gestartet wird.

Magn3s1um
quelle
1
Vielen Dank. Gibt es also keinen Unterschied zwischen einem "Daemon" und einem normalen Programm? Ich möchte nicht, dass es leicht geschlossen werden kann.
ChrisMe
1
Nein, ein Daemon ist nur ein Hintergrundprozess. Insbesondere geben Sie von einem übergeordneten Element ab, führen den untergeordneten Prozess aus und beenden den übergeordneten Prozess (sodass kein Terminalzugriff auf das Programm besteht). Das ist aber nicht unbedingt notwendig, um ein "Daemon" zu sein: en.wikipedia.org/wiki/Daemon_(computing)
Magn3s1um
1

Daemon-Vorlage

Ich habe eine Daemon-Vorlage geschrieben, die dem neuen Daemon folgt: link

Den gesamten Vorlagencode finden Sie auf GitHub: hier

Main.cpp

// This function will be called when the daemon receive a SIGHUP signal.
void reload() {
    LOG_INFO("Reload function called.");
}

int main(int argc, char **argv) {
    // The Daemon class is a singleton to avoid be instantiate more than once
    Daemon& daemon = Daemon::instance();
    // Set the reload function to be called in case of receiving a SIGHUP signal
    daemon.setReloadFunction(reload);
    // Daemon main loop
    int count = 0;
    while(daemon.IsRunning()) {
        LOG_DEBUG("Count: ", count++);
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    LOG_INFO("The daemon process ended gracefully.");
}

Daemon.hpp

class Daemon {
    public:

    static Daemon& instance() {
        static Daemon instance;
        return instance;
    }

    void setReloadFunction(std::function<void()> func);

    bool IsRunning();

    private:

    std::function<void()> m_reloadFunc;
    bool m_isRunning;
    bool m_reload;

    Daemon();
    Daemon(Daemon const&) = delete;
    void operator=(Daemon const&) = delete;

    void Reload();

    static void signalHandler(int signal);
};

Daemon.cpp

Daemon::Daemon() {
    m_isRunning = true;
    m_reload = false;
    signal(SIGINT, Daemon::signalHandler);
    signal(SIGTERM, Daemon::signalHandler);
    signal(SIGHUP, Daemon::signalHandler);
}

void Daemon::setReloadFunction(std::function<void()> func) {
    m_reloadFunc = func;
}

bool Daemon::IsRunning() {
    if (m_reload) {
        m_reload = false;
        m_reloadFunc();
    }
    return m_isRunning;
}

void Daemon::signalHandler(int signal) {
    LOG_INFO("Interrup signal number [", signal,"] recived.");
    switch(signal) {
        case SIGINT:
        case SIGTERM: {
            Daemon::instance().m_isRunning = false;
            break;
        }
        case SIGHUP: {
            Daemon::instance().m_reload = true;
            break;
        }
    }
}

daemon-template.service

[Unit]
Description=Simple daemon template
After=network.taget

[Service]
Type=simple
ExecStart=/usr/bin/daemon-template --conf_file /etc/daemon-template/daemon-tenplate.conf
ExecReload=/bin/kill -HUP $MAINPID
User=root
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=daemon-template

[Install]
WantedBy=multi-user.target
Fabiano Traple
quelle