Auf Unix- und Linux-artigen Systemen laufen immer einige sogenannte Daemon-Prozesse. Diese laufen im Hintergrund, haben also keine Verbindung mit einem Terminal.
Beim Start kann man eine sogenannte Daemonisierung verwenden. Man startet von dem interaktiv gestarteten Prozess einen Child-Prozess. Dieser hat noch Verbindung zum ersten und damit zum Terminal. Nun startet man von diesem den eigentlichen Daemon-Prozess und beendet den Child-Prozess. Nun ist die Verbindung zum Parent-Prozess gekappt und der Daemon-Prozess wird damit zum Child-Prozess des Init-Prozesses. Wenn sich der Daemon beendet, wird durch init regelmäßig wait() aufgerufen und damit verhindert, dass dieser als Zombie-Prozess noch lange im System unterwegs ist. Nun muss der Daemon-Prozess aber informiert werden, wann der Child-Prozess beendet ist.
Dies kann wie folgt erfolgen:
/* (C) IT Sky Consulting GmbH 2014
* http://www.it-sky-consulting.com/
* Author: Karl Brodowsky
* Date: 2014-02-27
* License: GPL v2 (See https://de.wikipedia.org/wiki/GNU_General_Public_License )
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
int daemonized = 0;
void signal_handler(int signo) {
daemonized = 1;
}
int main(int argc, char *argv[]) {
int fork_result;
int ret_code;
int status;
int pid = getpid();
/* set pgid to pid */
setpgid(pid, pid);
signal(SIGUSR1, signal_handler);
/* first fork() to create child */
fork_result = fork();
if (fork_result == 0) {
printf("In child\n");
/* second fork to create grand child */
fork_result = fork();
if (fork_result != 0) {
/* exit child, make grand child a daemon */
printf("exiting child\n");
exit(0);
}
/* in daemon (grand child) */
pid = getpid();
while (! daemonized) {
pause();
}
printf("daemon has pid=%d pgid=%d ppid=%d\n", pid, getpgid(pid), getppid());
/* do daemon stuff */
sleep(30);
printf("done with daemon\n");
exit(0);
}
printf("parent waiting for child\n");
wait(&status);
printf("child terminated\n");
kill(-getpid(), SIGUSR1);
printf("parent done\n");
}
Durch das Setzen einer Prozessgruppen-ID ist es möglich, den Parent-Prozess, den Child-Prozess und den Daemonprozess auch über diese gemeinsame Gruppen-Id anzusprechen, ohne die eigentliche pid zu kennen. Negative Werte als Funktionsparameter für Funktionenen, die dort eine Prozess-Id (pid) erwarten, werden oft als pgid (Prozessgruppen-ID) interpretiert. Wenn der Child-Prozess beendet ist, wird das wait im Parent-Prozess beendet und dieser schickt ein Signal an den Daemon-Prozess, das von diesem ignoriert wird, aber zur Beendigung des Wait-Prozesses führt.
Für ein kleines Beispiel mag es noch akzeptabel sein, nach stdout zu schreiben, aber die Ausgaben eines Daemons sollten natürlich am Ende in einer Log-Datei landen. Das kann durch Ausgabeumleitung oder noch schöner durch Verwendung von syslog geschehen, ist aber ein Thema für sich.
