Daemons sind Prozesse, die unbemerkt im Hintergrund auf Ihrem Rechner laufen. Hier erfahren Sie, wie Sie Ihre eigenen Daemons unter Linux programmieren können.

Daemons sind Prozesse, die nicht direkt unter der Kontrolle des Benutzers laufen, sondern im Hintergrund arbeiten. Normalerweise werden sie beim Systemstart gestartet und laufen ununterbrochen, bis das System heruntergefahren wird. Der einzige Unterschied zu normalen Prozessen besteht darin, dass sie keine Meldungen an die Konsole oder den Bildschirm senden.

Im Folgenden erfahren Sie, wie Sie einen Daemon auf einem Linux-Rechner erstellen können.

Eine kurze Einführung in die Erstellung von Daemons

Viele Daemons laufen auf dem System und einige bekannte Daemon-Beispiele sind wie folgt:

  • crond: Lässt Befehle zur angegebenen Zeit laufen
  • sshd: Ermöglicht die Anmeldung am System von entfernten Rechnern aus
  • httpd: Serviert Webseiten
  • nfsd: Ermöglicht die gemeinsame Nutzung von Dateien über das NetzwerkAußerdem enden die Namen von Daemon-Prozessen normalerweise mit dem Buchstaben d, obwohl dies nicht zwingend erforderlich ist.Damit ein Prozess als Daemon läuft, wird der folgende Pfad eingehalten:
  • Anfängliche Operationen, wie das Lesen von Konfigurationsdateien oder das Abrufen der erforderlichen Systemressourcen, müssen durchgeführt werden, bevor der Prozess zu einem Daemon wird. Auf diese Weise kann das System die empfangenen Fehler an den Benutzer melden, und der Prozess wird mit einem entsprechenden Fehlercode abgebrochen.
  • Ein im Hintergrund laufender Prozess wird mit init als Elternprozess erstellt. Dazu wird zunächst ein Unterprozess vom init-Prozess abgezweigt, und dann wird der obere Prozess mit exit beendet.
  • Eine neue Sitzung sollte durch den Aufruf der setid-Funktion geöffnet und der Prozess vom Terminal getrennt werden.
  • Alle vom Elternprozess geerbten offenen Dateideskriptoren werden geschlossen.
  • Standardeingaben, Ausgaben und Fehlermeldungen werden nach /dev/null umgeleitet.
  • Das Arbeitsverzeichnis des Prozesses muss sich ändern.

Was sind Daemon-Sitzungen?

Nach der Anmeldung am System über ein Terminal können Benutzer viele Anwendungen über das Shell-Programm ausführen. Diese Prozesse sollten geschlossen werden, wenn der Benutzer das System verlässt. Das Betriebssystem gruppiert diese Prozesse in Sitzungen und Prozessgruppen.

Jede Sitzung besteht aus Prozessgruppen. Sie können diese Situation wie folgt beschreiben:

Das Terminal, an dem die Prozesse ihre Eingaben erhalten und ihre Ausgaben tätigen, wird als Kontrollterminal bezeichnet. Ein Steuerungsterminal ist jeweils nur mit einer Sitzung verbunden.

Eine Sitzung und die darin enthaltenen Prozessgruppen haben Identifikationsnummern (ID); diese Identifikationsnummern sind die Prozessidentifikationsnummern (PID) der Sitzungs- und Prozessgruppenleiter. Ein Kindprozess hat dieselbe Gruppe wie sein Elternprozess. Wenn mehrere Prozesse über den Pipe-Mechanismus kommunizieren, wird der erste Prozess zum Prozessgruppenleiter.

Erstellen eines Daemon-Prozesses unter Linux

Hier sehen Sie, wie Sie eine Daemon-Funktion erstellen können. Zu diesem Zweck werden Sie eine Funktion namens _daemon erstellen. Sie können damit beginnen, den Anwendungscode, der als Daemon laufen soll, test.c zu nennen, und den Code, mit dem Sie die Daemon-Funktion erstellen, daemon.c.

//test.c
#include
int _daemon(int, int);
int main()
{
getchar();
_daemon(0, 0);
getchar();
return 0;
}

//daemon.c
#include <sys/types.h>
#include <sys/stat.h>
#include
#include
#include
#include
#include <linux/fs.h>
#include <linux/limits.h>
int _daemon(int nochdir, int noclose) {
pid_t pid;
pid = fork(); // Fork off the parent process
if (pid < 0) { exit(EXIT_FAILURE); } if (pid > 0) {
exit(EXIT_SUCCESS);
}
return 0;
}

Your Content Goes HereUm einen Daemon zu erstellen, benötigen Sie einen Hintergrundprozess, dessen Elternprozess init ist. Im obigen Code erzeugt _daemon einen Kindprozess und beendet dann den Elternprozess. In diesem Fall wird Ihr neuer Prozess ein Unterprozess von init sein und im Hintergrund weiterlaufen.

Kompilieren Sie nun die Anwendung mit dem folgenden Befehl und untersuchen Sie den Status des Prozesses vor und nach dem Aufruf von _deamon:

gcc -o test test.c daemon.c

Führen Sie die Anwendung aus und wechseln Sie zu einem anderen Terminal, ohne eine andere Taste zu drücken:

./test

Sie können sehen, dass die Werte, die sich auf Ihren Prozess beziehen, wie folgt sind. Hier müssen Sie den Befehl ps verwenden, um prozessbezogene Informationen zu erhalten. In diesem Fall ist die Funktion _daemon noch nicht aufgerufen worden.

ps -C test -o "pid ppid pgid sid tty stat command"
# Output
PID PPID PGID SID TT STAT COMMAND
10296 5119 10296 5117 pts/2 S+ ./test

Wenn Sie sich das Feld STAT ansehen, sehen Sie, dass Ihr Prozess läuft, aber auf ein außerplanmäßiges Ereignis wartet, das ihn im Vordergrund laufen lässt.

Abkürzung – Bedeutung

S – Wartet im Ruhezustand auf das Eintreten eines Ereignisses
T – Anwendung gestoppt
s – Sitzungsleiter
+ – Die Anwendung läuft im Vordergrund

Sie sehen, dass der übergeordnete Prozess Ihrer Anwendung wie erwartet die Shell ist.

ps -jp 5119
# Output
PID PGID SID TTY TIME CMD
5119 5119 5117 pts/2 00:00:02 zsh

Kehren Sie nun zu dem Terminal zurück, in dem Sie Ihre Anwendung ausführen, und drücken Sie die Eingabetaste, um die Funktion _daemon aufzurufen. Sehen Sie sich dann die Prozessinformationen auf dem anderen Terminal erneut an.

ps -C test -o "pid ppid pgid sid tty stat command"
# Output
PID PPID PGID SID TT STAT COMMAND
22504 1 22481 5117 pts/2 S ./test

Zunächst können Sie feststellen, dass der neue Unterprozess im Hintergrund läuft, da Sie das Zeichen + im Feld STAT nicht sehen. Untersuchen Sie nun, wer der Elternprozess des Prozesses ist, indem Sie den folgenden Befehl verwenden:

ps -jp 1
​​​​​​​# Output
PID PGID SID TTY TIME CMD
1 1 1 ? 00:00:01 systemd

Sie können nun sehen, dass der Elternprozess Ihres Prozesses der systemd-Prozess ist. Oben wurde erwähnt, dass im nächsten Schritt eine neue Sitzung geöffnet und der Prozess vom Kontrollterminal getrennt werden sollte. Hierfür verwenden Sie die Funktion setsid. Fügen Sie diesen Aufruf zu Ihrer _daemon-Funktion hinzu.

Der hinzuzufügende Code lautet wie folgt:

if (setsid() == -1)
return -1;

Da Sie nun den Zustand vor dem Aufruf von _daemon überprüft haben, können Sie die erste getchar-Funktion im test.c-Code entfernen.

Da Sie nun den Zustand vor dem Aufruf von _daemon überprüft haben, können Sie die erste getchar-Funktion im test.c-Code entfernen.

//test.c
#include
int _daemon(int, int);
int main()
{
_daemon(0, 0);
getchar();
return 0;
}

Nachdem Sie die Anwendung erneut kompiliert und ausgeführt haben, wechseln Sie zu dem Terminal, in dem Sie Ihre Überprüfungen vorgenommen haben. Der neue Status Ihres Prozesses lautet wie folgt:

ps -C test -o "pid ppid pgid sid tty stat command"
​​​​​​​# Output
PID PPID PGID SID TT STAT COMMAND
25494 1 25494 25494 ? Ss ./test

Das Zeichen ? im Feld TT zeigt an, dass Ihr Prozess nicht mehr mit einem Terminal verbunden ist. Beachten Sie, dass die PID-, PGID- und SID-Werte Ihres Prozesses gleich geblieben sind. Ihr Prozess ist jetzt ein Sitzungsleiter.

Im nächsten Schritt ändern Sie das Arbeitsverzeichnis in das Stammverzeichnis entsprechend dem Wert des übergebenen Arguments. Dazu können Sie den folgenden Ausschnitt in die _daemon-Funktion einfügen:

if (!nochdir) {
if (chdir("/") == -1)
return -1;
}

Jetzt können je nach dem übergebenen Argument alle Dateideskriptoren geschlossen werden. Fügen Sie den folgenden Code in die Funktion _daemon ein:

#define NR_OPEN 1024
if (!noclose) {
for (i = 0; i < NR_OPEN; i++)
close(i);
open("/dev/null", O_RDWR);
dup(0);
dup(0);
}

Nachdem alle Dateideskriptoren geschlossen wurden, werden neue vom Daemon geöffnete Dateien mit den Deskriptoren 0, 1 bzw. 2 angezeigt. In diesem Fall werden zum Beispiel die printf-Befehle im Code an die zweite geöffnete Datei gerichtet. Um dies zu vermeiden, zeigen die ersten drei Bezeichner auf das Gerät /dev/null.

In diesem Fall sieht der Endzustand der Funktion _daemon wie folgt aus:

#include <sys/types.h>
#include <sys/stat.h>
#include
#include
#include
#include
#include
#include
#include
int _daemon(void) {
// PID: Process ID
// SID: Session ID
pid_t pid, sid;
pid = fork(); // Fork off the parent process
if (pid < 0) { exit(EXIT_FAILURE); } if (pid > 0) {
exit(EXIT_SUCCESS);
}
// Create a SID for child
sid = setsid();
if (sid < 0) {
// FAIL
exit(EXIT_FAILURE);
}
if ((chdir("/")) < 0) {
// FAIL
exit(EXIT_FAILURE);
}
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
while (1) {
// Some Tasks
sleep(30);
}
exit(EXIT_SUCCESS);
}

Hier ist ein Beispiel für einen Codeschnipsel, der die sshd-Anwendung als Daemon ausführt:

...
if (!(debug_flag || inetd_flag || no_daemon_flag)) {
int fd;
if (daemon(0, 0) < 0) fatal("daemon() failed: %.200s", strerror(errno)); /* Disconnect from the controlling tty. */ fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); if (fd >= 0) {
(void) ioctl(fd, TIOCNOTTY, NULL);
close(fd);
}
}
...

Daemons sind wichtig für die Linux-Systemprogrammierung

Daemons sind Programme, die verschiedene Aktionen in einer vordefinierten Weise als Reaktion auf bestimmte Ereignisse ausführen. Sie laufen unbemerkt auf Ihrem Linux-Rechner. Sie stehen nicht unter der direkten Kontrolle des Benutzers und jeder Dienst, der im Hintergrund läuft, hat seinen eigenen Daemon.

Es ist wichtig, Daemons zu beherrschen, um die Kernel-Struktur des Linux-Betriebssystems zu lernen und die Funktionsweise verschiedener Systemarchitekturen zu verstehen.