Démonizace po startu a zrychleni bootování

Oct|25 2011

V jednom z předešlých dílů seriálu jsem se zmínil o PID a PPID nově spuštěných procesů. Pokud spouštíme démona, chceme ho vyvázat ze stromu procesů, aby nebyl nijak ovlivněn procesem, který ho spustil. Tomu říkáme démonizace, přičemž je dobré změnit i aktuální pracovní adresář a ignorovat signály týkajicí se práce v terminálu. Ukázkovou démonizaci můžeme vidět na následujícím kódu, který je vypůjčen z implementace lighttpd, jednoduchého http serveru.

signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
if (0 != fork()) exit(0);

if (-1 == setsid()) exit(0);

signal(SIGHUP, SIG_IGN);

if (0 != fork()) exit(0);

if (0 != chdir("/")) exit(0);

Jak vidíme, klasická démonizace navíc obsahuje tzv. double-fork, tedy celkem dvakrát se spouští nový proces se všemi důsledky pro výkon. Rodičovský proces vždy končí s návratovým kódem 0, což je již na první pohled neefektivní z hlediska výkonu (uvědomme si, kolikrát se daná procedura provádí během jednoho bootu), ale také nám dost znesnadňuje kontrolu inicializace démona. V mnoha případech je totiž načítání konfigurace a další inicializační činnost prováděna až v démonizovaném procesu.

Pokud inicializace selže, démon není spuštěn, nicméně rodičovský proces při démonizaci již vrátil nulový návratový kód, tedy hlásil úspěšné spuštění. Tento problém je většinou pomíjen a je pouze na init systému, aby si pomocí pid souboru dohledal, že efektivní proces daného démona již neběží a zaujal příslušná opatření.

Tam, kde by nesprávný návratový kód znamenal větší potíže, je potřeba vytvořit IPC komunikaci mezi novým procesem a původním rodičem, což přináší další nemalé zásahy do kódu démona. Využít můžeme například dbus nebo komunikaci pomocí socketů, nicméně asi cítíme, že to všechno by bylo docela zbytečné, pokud bychom měli lepší způsob, jak se vyvázat z terminálu, resp. od rodičovského procesu.

systemd a démonizace

Nejnovější implementace procesu init spatřilo světlo světa v roce 2010 a jmneuje se systemd. Označení init systém vychází z historického označení systémů sysvinit, proto se pokusím nadále používat obecného označení systémový manažer a manažer služeb (system and service manager).

systemd dokáže používat i starší skripty používané pro Upstart nebo System V. Nicméně pro plné využití je doporučeno vytvořit nativní konfigurační soubory, tzv. Unit files.

Zkusme vzít například lighttpd, jednoduchou implementaci http démona. Ten používá ukázkovou démonizaci pomocí double-fork metody. systemd dokáže takovou implementaci vzít bez změn a díky své vnitřní logice pozná hlavní proces od rodičovského (v tomto případě zůstane běžící pouze jeden, přičemž rodičovské jsou ukončeny). Tento proces potom stráží a případně restartuje službu, pokud hlavní proces neočekávaně skončí. Nativní Unit file pro lighttpd se zachovanou démonizací i s definicí konfiguračního souboru pro démona vypadá následovně:

[Unit]
Description=Lightning Fast Webserver With Light System Requirements
After=syslog.target network.target

[Service]
Type=forking
PIDFile=/var/run/lighttpd.pid
EnvironmentFile=-/etc/sysconfig/lighttpd
ExecStart=/usr/sbin/lighttpd -f /etc/lighttpd/lighttpd.conf

[Install]
WantedBy=multi-user.target

Všimněme si typu služby "forking", která právě definuje, že démon používá démonizaci pomocí double-fork. systemd nicméně dovoluje (a dokonce doporučuje, pokud je to možné) používat spouštění bez démonizace, tedy pouze v jednom procesu. lighttpd démon nám dovoluje spuštění na popředí pomocí přepínače -D, kdy se démonizace neprovede a můžeme tak démona snáze ladit. V případě systemd můžeme tento mód použít a místo "forking" definovat službu jako "simple", což je výchozí hodnota, takže to nemusíme do Unit souboru vůbec uvádět. Výsledný soubor tedy bude vypadat následovně:

[Unit]
Description=Lightning Fast Webserver With Light System Requirements
After=syslog.target network.target

[Service]
PIDFile=/var/run/lighttpd.pid
EnvironmentFile=-/etc/sysconfig/lighttpd
ExecStart=/usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf

[Install]
WantedBy=multi-user.target

Čeho jsme docílili? Tak zejména systemd nemusí zjišťovat, který proces je výkonný pomocí pid souboru nebo jiného mechanizmu, protože je ve skutečnosti spuštěný všehovšudy pouze jeden proces. Zároveň neprovádíme démonizaci, což znamená ušetření určitého výpočetního času.

Jednoduché je i reportování či případné ukončení služby a správné vrácení návratové hodnoty v případě, že inicializace démona neproběhne v pořádku. A v neposlední řadě si můžeme (pokud by systemd byl používán na všech systémech, které používají našeho démona) ušetřit čas při programování a ladění démona. Při spuštění se o veškerou démonizaci postará systemd.

Tags: Programování | Počítače | Vývoj | Linux | Fedora | Operační systém

Add New Comment

Note: Comments will be visible after manual check by admin.

* These fields have to be filled.