man 7 daemon
velmi podrobně popisuje, jak vytvořit démona. Moje odpověď je pouze výňatek z této příručky.
Existují alespoň dva typy démonů:
- tradiční démoni SysV (starý styl),
- daemoni systemd (nový styl).
Démoni SysV
Pokud máte zájem o tradičního démona SysV, měli byste implementovat následující kroky:
- Zavřete všechny otevřené deskriptory souborů kromě standardního vstupu , výstup a chyba (tj. první tři deskriptory souborů 0, 1, 2). To zajišťuje, že v procesu démona nezůstane žádný náhodně předaný deskriptor souboru. V Linuxu se to nejlépe implementuje iterací přes
/proc/self/fd
, s možností iterace z deskriptoru souboru 3 na hodnotu vrácenougetrlimit()
proRLIMIT_NOFILE
.- Obnovte všechny obslužné programy signálů na výchozí hodnoty. Toho lze nejlépe provést iterací dostupných signálů až do limitu
_NSIG
a jejich resetování naSIG_DFL
.- Resetujte masku signálu pomocí
sigprocmask()
.- Vyčistěte blok prostředí, odstraňte nebo resetujte proměnné prostředí, které by mohly negativně ovlivnit běh démona.
- Zavolejte na číslo
fork()
, vytvořit proces na pozadí.- V podřízeném prvku zavolejte
setsid()
odpojit se od libovolného terminálu a vytvořit nezávislou relaci.- V podřízeném prvku zavolejte
fork()
znovu, abychom zajistili, že démon už nikdy nebude moci znovu získat terminál.- Zavolejte na číslo
exit()
v prvním potomkovi, takže kolem zůstane pouze druhé dítě (skutečný proces démona). Tím je zajištěno, že proces démona bude přeměněn na init/PID 1, jak by měli být všichni démoni.- V procesu démona připojte
/dev/null
na standardní vstup , výstup a chyba .- V procesu démona resetujte
umask
na 0, takže režimy souborů přejdou naopen()
,mkdir()
a podobně přímo řídit režim přístupu k vytvořeným souborům a adresářům.- V procesu démona změňte aktuální adresář na kořenový adresář (
/
), aby se zabránilo tomu, že démon nedobrovolně zablokuje odpojení přípojných bodů.- V procesu démona zapište PID démona (vrácené
getpid()
) do souboru PID, například/run/foobar.pid
(pro hypotetického démona "foobar"), aby bylo zajištěno, že démon nemůže být spuštěn více než jednou. Toto musí být implementováno způsobem bez závodů, takže soubor PID je aktualizován pouze tehdy, když je současně ověřeno, že PID dříve uložený v souboru PID již neexistuje nebo patří cizímu procesu.- V procesu démona zrušte oprávnění, pokud je to možné a použitelné.
- Z procesu démona upozorněte původní spuštěný proces, že inicializace je dokončena. To lze implementovat prostřednictvím nepojmenovaného kanálu nebo podobného komunikačního kanálu, který je vytvořen před prvním
fork()
a tudíž k dispozici v původním procesu i v procesu démona.- Zavolejte na číslo
exit()
v původním procesu. Proces, který démona vyvolal, musí být schopen se na to spolehnoutexit()
stane po inicializace je dokončena a všechny externí komunikační kanály jsou vytvořeny a přístupné.
Všimněte si tohoto varování:
BSD
daemon()
funkce by neměla použít, protože implementuje pouze podmnožinu těchto kroků.Démon, který musí poskytovat kompatibilitu se systémy SysV by měly implementovat schéma uvedené výše. Doporučuje se však, aby toto chování bylo volitelné a konfigurovatelné pomocí argumentu příkazového řádku, aby se usnadnilo ladění a také se zjednodušila integrace do systémů pomocí systemd.
Všimněte si, že daemon()
není kompatibilní s POSIX.
Démoni nového stylu
Pro démony nového stylu se doporučují následující kroky:
- Pokud
SIGTERM
je přijat, vypněte démona a ukončete čistě.- Pokud
SIGHUP
je přijat, znovu načtěte konfigurační soubory, pokud to platí.- Zadejte správný výstupní kód z hlavního procesu démona, protože jej používá systém init k detekci chyb a problémů služby. Doporučuje se dodržovat schéma výstupního kódu, jak je definováno v doporučeních LSB pro iniciační skripty SysV.
- Pokud je to možné a použitelné, zpřístupněte ovládací rozhraní démona prostřednictvím systému D-Bus IPC a jako poslední krok inicializace zjistěte název sběrnice.
- Pro integraci do systemd poskytněte soubor .service unit, který obsahuje informace o spouštění, zastavování a jiné údržbě démona. Viz
systemd.service(5)
podrobnosti.- Pokud je to možné, spoléhejte se na funkcionalitu systému init, abyste omezili přístup démona k souborům, službám a dalším zdrojům, tj. v případě systemd se spoléhejte na kontrolu omezení zdrojů systemd namísto implementace své vlastní, spoléhejte na privilegium systemd vynechává kód místo jeho implementace v démonu a podobně. Viz
systemd.exec(5)
pro dostupné ovládací prvky.- Pokud používáte D-Bus, zajistěte aktivaci vaší démonové sběrnice poskytnutím konfiguračního souboru pro aktivaci služby D-Bus. To má několik výhod:váš démon může být spuštěn líně na vyžádání; může být spuštěn paralelně s jinými démony, které to vyžadují — což maximalizuje paralelizaci a rychlost spouštění; vašeho démona lze při selhání restartovat, aniž by došlo ke ztrátě požadavků na sběrnici, protože sběrnice řadí do fronty požadavky na aktivovatelné služby. Podrobnosti naleznete níže.
- Pokud váš démon poskytuje služby jiným místním procesům nebo vzdáleným klientům prostřednictvím soketu, mělo by být možné jej aktivovat pomocí soketu podle níže uvedeného schématu. Stejně jako aktivace D-Bus umožňuje spouštění služeb na vyžádání a také umožňuje lepší paralelizaci spouštění služeb. U bezstavových protokolů (jako je syslog, DNS) lze také restartovat démona implementující aktivaci založenou na soketu bez ztráty jediného požadavku. Podrobnosti naleznete níže.
- Pokud je to možné, měl by démon informovat init systém o dokončení spouštění nebo aktualizacích stavu prostřednictvím
sd_notify(3)
rozhraní.- Místo použití
syslog()
volání pro přihlášení přímo do systémové služby syslog, démon nového stylu se může rozhodnout jednoduše přihlásit ke standardní chybě přesfprintf()
, který je poté předán do syslog systémem init. Pokud jsou nutné úrovně protokolů, lze je zakódovat předřazením jednotlivých řádků protokolu řetězci jako "<4>" (pro úroveň protokolu 4 "WARNING" ve schématu priority syslog), podle podobného stylu jakoprintk()
systém úrovní. Podrobnosti vizsd-daemon(3)
asystemd.exec(5)
.
Chcete-li se dozvědět více, přečtěte si celý man 7 daemon
.
V Linuxu chci přidat démona, který nelze zastavit a který sleduje změny souborového systému. Pokud by byly zjištěny nějaké změny, měl by zapsat cestu ke konzoli, kde byl spuštěn, + nový řádek.
Démoni pracují na pozadí a (obvykle...) nepatří k TTY, proto nemůžete použít stdout/stderr tak, jak pravděpodobně chcete. Obvykle jde o démona syslog (syslogd ) se používá pro protokolování zpráv do souborů (ladění, chyba,...).
Kromě toho existuje několik povinných kroků k démonizaci procesu.
Pokud si dobře pamatuji, tyto kroky jsou:
- rozvětvení vypnout nadřazený proces a nechat jej ukončit, pokud bylo rozvětvení úspěšné. -> Protože nadřazený proces byl ukončen, podřízený proces nyní běží na pozadí.
- setsid - Vytvořte novou relaci. Volající proces se stává vedoucím nové relace a vedoucím procesní skupiny nové procesní skupiny. Proces je nyní odpojen od svého řídicího terminálu (CTTY).
- Zachyťte signály - Ignorujte a/nebo zvládejte signály.
- znovu rozvětvení &nechte nadřazený proces ukončit, abyste zajistili, že se zbavíte procesu vedoucího relace. (TTY mohou znovu získat pouze vedoucí relace.)
- chdir - Změňte pracovní adresář démona.
- umask - Změňte masku režimu souboru podle potřeb démona.
- zavřít - Zavřete všechny otevřené deskriptory souborů, které mohou být zděděny z nadřazeného procesu.
Pro začátek:Podívejte se na tento kód kostry, který ukazuje základní kroky. Tento kód lze nyní také rozdělit na GitHub:Základní kostra linuxového démona
/*
* 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;
}
- Zkompilujte kód:
gcc -o firstdaemon daemonize.c
- Spusťte démona:
./firstdaemon
-
Zkontrolujte, zda vše funguje správně:
ps -xj | grep firstdaemon
-
Výstup by měl být podobný tomuto:
+------+------+------+------+-----+-------+------+------+------+-----+ | PPID | PID | PGID | SID | TTY | TPGID | STAT | UID | TIME | CMD | +------+------+------+------+-----+-------+------+------+------+-----+ | 1 | 3387 | 3386 | 3386 | ? | -1 | S | 1000 | 0:00 | ./ | +------+------+------+------+-----+-------+------+------+------+-----+
Zde byste měli vidět:
- Démon nemá žádný ovládací terminál (TTY =? )
- ID nadřazeného procesu (PPID ) je 1 (Proces init)
- PID !=SID což znamená, že náš proces NENÍ vedoucím relace
(kvůli druhému fork()) - Protože PID !=SID, náš proces nemůže znovu převzít kontrolu nad TTY
Čtení syslog:
- Vyhledejte soubor syslog. Můj je zde:
/var/log/syslog
-
Proveďte:
grep firstdaemon /var/log/syslog
-
Výstup by měl být podobný tomuto:
firstdaemon[3387]: First daemon started. firstdaemon[3387]: First daemon terminated.
Poznámka: Ve skutečnosti byste také chtěli implementovat obslužný program signálu a správně nastavit protokolování (soubory, úrovně protokolů...).
Další čtení:
- Linux-UNIX-Programmierung – němčina
- Programování serveru Unix Daemon