abclinuxu.cz AbcLinuxu.cz itbiz.cz ITBiz.cz HDmag.cz HDmag.cz abcprace.cz AbcPráce.cz
Inzerujte na AbcPráce.cz od 950 Kč
Rozšířené hledání
×
    dnes 14:22 | IT novinky

    VASA-1, výzkumný projekt Microsoftu. Na vstupu stačí jediná fotka a zvukový záznam. Na výstupu je dokonalá mluvící nebo zpívající hlava. Prý si technologii nechá jenom pro sebe. Žádné demo, API nebo placená služba. Zatím.

    Ladislav Hagara | Komentářů: 1
    dnes 04:44 | Nová verze

    Nová čísla časopisů od nakladatelství Raspberry Pi: MagPi 140 (pdf) a HackSpace 77 (pdf).

    Ladislav Hagara | Komentářů: 0
    dnes 01:00 | Nová verze

    ESPHome, tj. open source systém umožňující nastavovat zařízení s čipy ESP (i dalšími) pomocí konfiguračních souborů a připojit je do domácí automatizace, například do Home Assistantu, byl vydán ve verzi 2024.4.0.

    Ladislav Hagara | Komentářů: 0
    včera 22:11 | IT novinky Ladislav Hagara | Komentářů: 0
    včera 20:55 | Nová verze

    Neziskové průmyslové konsorcium Khronos Group vydalo verzi 1.1 specifikace OpenXR (Wikipedie), tj. standardu specifikujícího přístup k platformám a zařízením pro XR, tj. platformám a zařízením pro AR (rozšířenou realitu) a VR (virtuální realitu). Do základu se z rozšíření dostalo XR_EXT_local_floor. Společnost Collabora implementuje novou verzi specifikace do platformy Monado, tj. open source implementace OpenXR.

    Ladislav Hagara | Komentářů: 2
    včera 17:22 | Nová verze

    Byla vydána nová verze 0.38.0 multimediálního přehrávače mpv (Wikipedie) vycházejícího z přehrávačů MPlayer a mplayer2. Přehled novinek, změn a oprav na GitHubu. Požadován je FFmpeg 4.4 nebo novější a také libplacebo 6.338.2 nebo novější.

    Ladislav Hagara | Komentářů: 13
    včera 17:11 | Nová verze

    ClamAV (Wikipedie), tj. multiplatformní antivirový engine s otevřeným zdrojovým kódem pro detekci trojských koní, virů, malwaru a dalších škodlivých hrozeb, byl vydán ve verzích 1.3.1, 1.2.3 a 1.0.6. Ve verzi 1.3.1 je mimo jiné řešena bezpečnostní chyba CVE-2024-20380.

    Ladislav Hagara | Komentářů: 2
    včera 12:11 | IT novinky

    Digitální a informační agentura (DIA) oznámila (PDF, X a Facebook), že mobilní aplikace Portál občana je ode dneška oficiálně venku.

    Ladislav Hagara | Komentářů: 10
    včera 05:11 | Komunita

    #HACKUJBRNO 2024, byly zveřejněny výsledky a výstupy hackathonu města Brna nad otevřenými městskými daty, který se konal 13. a 14. dubna 2024.

    Ladislav Hagara | Komentářů: 2
    17.4. 17:55 | IT novinky

    Společnost Volla Systeme stojící za telefony Volla spustila na Kickstarteru kampaň na podporu tabletu Volla Tablet s Volla OS nebo Ubuntu Touch.

    Ladislav Hagara | Komentářů: 3
    KDE Plasma 6
     (68%)
     (10%)
     (2%)
     (20%)
    Celkem 563 hlasů
     Komentářů: 4, poslední 6.4. 15:51
    Rozcestník

    Systemd – psaní unixových démonů

    15. 6. 2011 | Michal Vyskočil | Systém | 16454×

    V dnešním díle se zaměříme na unixové démony a proč má systemd vlastní sadu doporučení. Systemd je nejnovější hvězda v tak poklidné a konzervativní oblasti, jako jsou init systémy. V následující sérii článků si probereme jeho vlastnosti.

    Obsah

    Unixový démon

    link

    V tradiční unixové terminologii se objevuje slovo démon (respektive daemon, ale to je v češtině zlá, nepěkná věc). Démoni mají zvláštní postavení. Chovají se jinak než ostatní aplikace a ve většině případů jsou tím, co spouští příslušný init skript. V Unixu je každý program připojen k (pseudo)terminálu, pomocí něhož komunikuje s okolím. Na terminál se zapisuje, přijímají se z něj data a po jeho uzavření je daný program ukončen. Znamená to tedy, že spouštět klasické programy init skriptem není možné, protože ten pouze vydá povel ke spuštění a potom se ukončí, kdežto démon pokračuje na pozadí.

    Existuje sice možnost spustit program s nohup a na pozadí, takže nedojde k jeho ukončení, ale jak uvidíme dále, není to úplně korektní postup. Přesto se v praxi vyskytuje. Například pro servery napsané v Javě je to jediná rozumná možnost.

    Jak se stát démonem

    link

    Slovo démon je neodbytně spjato s magií a unixoví démoni musí ve skutečnosti udělat řadu v pravdě „magických“ kroků k tomu, aby se z běžného programu mohl stát démon.

    1. Zavřít všechny otevřené popisovače souborů mimo standardních vstupů a výstupů. Obvyklým způsobem je iterovat do getrlimit(RLIMIT_NOFILE) a volat close(2) na každý z nich a na konci vynulovat errno, protože chyba zavírání neexistujícího popisovače nás nezajímá. V Linuxu můžeme vzít v potaz /proc/self/fd a zavírat pouze ty skutečně otevřené.
    2. Nastavit obsluhy všech signálů, až na SIGKILL a SIGSTOP na SIG_DFL. Opět můžeme iterovat přes všechny signály až po NSIG (nebo _NSIG, pokud hodláme na glibc pracovat i real-time signály – v tom případě musíme ignorovat první dva rt signály).
    3. Nastavit masku blokovaných signálů pomocí sigprocmask, aby nás v démonizaci nerušily zbloudilé signály.
    4. Zavolat první fork(2) pro vytvoření procesu na pozadí.
    5. V potomkovi tak můžeme bezpečně zavolat setsid().
    6. Zavolat druhý fork(2) a ukončit prvního potomka. Ukončením zajistíme, že rodičem bude PID 1.
    7. V démonizovaném procesu potom nastavit umask(2) na 0.
    8. Změnit pracovní adresář na /, aby démon nebránil případnému odpojení oddílů.
    9. Dále připojit standardní vstupy a výstupy na /dev/null, protože démoni komunikují přes sockety a informace zapisují přes syslog. Tímto se vyhneme selháním v operacích printf, které nikdo obvykle nečeká.
    10. Poznamenat PID démona – obvykle do souboru /var/run/démon.pid. Tato operace obvykle zahrnuje ještě zjištění, zda už náhodou neběží další instance, takže to celé musí probíhat atomicky.
    11. Snížit si práva na co nejmenší možnou míru.
    12. Dát původnímu procesu najevo, že inicializace byla ukončena – to je možné provést přes nepojmenovanou rouru, případně využitím signálů, ale první řešení je jednodušší.
    13. V původním procesu zavolat exit() – ten musí přijít až v okamžiku, kdy se ukončí inicializace původního procesu a měl by návratovým kódem naznačit, kterak skončil démonizovaný proces.

    A to je, mimo „drobností“ jako odmaskovat signály, nastavit reakce na HUP, otevření socketu, načtení konfigurace a podobných záležitostí, vše.

    A jelikož je ladění takovýchto démonů z příkazové řádky dosti nepohodlné, obvyklým postupem je mít možnost přepínat se mezi režimem démona a klasickým programem běžícím na popředí, který se od terminálu neodpojuje.

    Jak se stát démonem – vysvětleno

    link

    Tento postup není ve skutečnosti nijak magický. V zásadě jde o směs funkčních a bezpečnostních opatření. Kostrou démona je posloupnost fork(2), setsid()2 a fork(2). Což je kód sloužící k tomu, aby se program vyvázal z případné uživatelské relace (session), pokud je v ní spuštěn a aby neměl přiřazeny žádné prostředky, které by potom nebylo možné uvolnit. Případně aby jejich uvolnění nemělo vliv na náš kód.

    Důvodem existence fork-setsid-fork je způsob organizace procesů v unixovém shellu. Každý trochu pokročilejší uživatel ví o PID – unikátním identifikátoru procesu. Ti ještě pokročilejší vědí, že unixové procesy mohou patřit do skupin procesů (process groups), které mají svoje číslo PGID a rovněž do relací – sessions - které mají opět své identifikační číslo a navíc kontrolní terminál.

    Že tomu tak skutečně je, se můžeme snadno přesvědčit příkazem ps

    ps -eo pid,ppid,pgid,sess,comm,tty

    Po jeho spuštění se objeví hromada čísel následovaná názvem spustitelného procesu. A taky vidíme, že spousta procesů má stejné čísla skupin procesů nebo relací. Tento způsob organizace vznikl a je používán především v interaktivním shellu. Omezme výpis pouze na aktuální relaci (vynecháním argumentu -e).

    $ ps -o pid,ppid,pgid,sess,tty,comm --sort pid
      PID  PPID  PGID  SESS TT       COMMAND
    12375 12368 12375 12375 pts/0    bash
    12510 12375 12510 12375 pts/0    ps
    

    Spustíme pár příkazů

    $ sleep 200 &
    [1] 12549
    $ sleep 200 &
    [2] 12571
    $ bash
    $ ps -o pid,ppid,pgid,sess,tty,comm --sort pid | cat
      PID  PPID  PGID  SESS TT       COMMAND
    12375 12368 12375 12375 pts/0    bash
    12549 12375 12549 12375 pts/0    sleep
    12571 12375 12571 12375 pts/0    sleep
    12664 12375 12664 12375 pts/0    bash
    12693 12664 12693 12375 pts/0    ps
    12694 12664 12693 12375 pts/0    cat
    

    Jak vidíme, tak všechny spuštěné programy mají stejné číslo uživatelské relace (což je logické, protože to právě vynechání argumentu -e dělá). Číslo skupiny procesů je u všech stejné, s výjimkou posledních ps a cat. A rodičovské čísla ukazují, kdo spustil co – příkaz pstree je používá pro kreslení grafu procesů.

    Spusťme teď váš oblíbený emulátor terminálu a v něm znovu ps

    $ ps -o pid,ppid,pgid,sess,tty,comm --sort pid | cat
      PID  PPID  PGID  SESS TT       COMMAND
    13808 13807 13808 13808 pts/3    bash
    13860 13808 13860 13808 pts/3    ps
    13861 13808 13860 13808 pts/3    cat
    

    Vidíte, že příkaz vypisuje nejen jiné číslo relace, ale i odlišný terminál.

    Z výše uvedených příkazů lze odvodit následující pravidla:

    • všechny procesy na jednom (pseudo) terminálu mají stejné číslo relace a stejný kontrolní terminál
    • procesy spuštěné v koloně patří do stejné skupiny procesů

    Začněme druhým odstavcem. Unixový shell používá skupiny procesů pro práci s úlohami, které spouští. Každý spuštěný příkaz vytvoří novou skupinu procesů, pokud je spuštěn pouze jeden, pak je hodnota PPID rovna hodnotě PID. Pouze jedna skupina procesů může běžet v popředí, zbytek musí běžet na pozadí. A nakonec to hlavní – existuje systémové volání – zašli signál všem procesům z dané skupiny. Takže, pokud napíšeme kill %2, shell to převede na volání killpg(SIGTERM,skupina-odpovídající-%2).

    Existují systémová volání setpgid(2) a setssid(2), kterými proces může změnit svoji skupinu a relaci. Pro démonizaci nás zajímá to druhé – setsid(2), které vytvoří novou relaci pro proces, který ji zavolá. Tato funkce má jedno zásadní omezení, nesmí být zavolána z takzvaného process group leader – čili hlavního procesu skupiny. Je tomu tak proto, že nelze určit, který ze zbývajících procesů má být ve skupině novým hlavním procesem.

    A převod všech procesů skupiny pod novou relaci není možný, protože jak čísla skupin, tak čísla relací musí být unikátní a odvozují se od PID příslušného vůdcovského procesu.

    Řešením je tak první fork(2), kdy s určitostí víme, že náš potomek není hlavním procesem skupiny, tudíž může bezpečně zavolat setsid(2). Navíc, ukončením původního procesu si uvolníme prompt shellu.

    Otázkou je, proč potřebujeme volat ještě druhý fork(2)? Tím se dostáváme k relacím. Co nebylo přímo uvedeno, i když je to vidět z výpisů příkazu ps je to, že procesy jedné relace sdílejí stejný terminál (v našem případě pseudoterminál). Ten se nazývá kontrolní terminál a je základním komunikačním prostředkem mezi uživatelem a procesy. Voláním setsid(2) se vyvážeme z aktuálního terminálu. Ovšem stejně jako existuje process group leader, tak existuje i session leader. Ten má obvykle připojený terminál, ale v případě fork-setsid proces žádný terminál nemá, ale jako vůdce může otevřít nový.

    Otázkou tedy je, jak zajistit, aby náš proces nemohl dostat nový terminál?

    • volat všechna open s O_NOCTTY – nepraktické už kvůli knihovnám
    • používat BSD systémy, kde se kontrolní terminál získá přes speciální ioctl()
    • anebo, prostě zavolat nový fork(), kde je jasné, že potomek není session leader a tudíž už nemůže v žádném případě získat kontrolní terminál

    Potíže tradičních démonů

    link

    Právě forkovací magie dělá problémy nejen autorům těchto démonů, tak i z mnoha důvodů autorům (dobrých ™) init systémů. Jak Lennart ve svém původním článku napsal, důležitou částí takového init systému je i sledování stavu spuštěných procesů, což není u tradičního démona spuštěného init skriptem nijak přímočaré a spolehlivé.

    Tak třeba klasický démon provede několik forků, takže je bez PID souboru prakticky nemožné rozumně zjistit číslo hlavního procesu. A to potřebujeme pro komunikaci s démonem.

    Ale double fork způsobuje z hlediska sledování i jiné problémy. Klasicky, pokud totiž rodič zavolá fork(), dostane SIGCHLD v případě, že běh potomka skončil. Tímto způsobem může jednoduše reagovat na neočekávané ukončení potomků. Ovšem pokud takto spouštíme démona, tak SIGCHLD nic o ukončení hlavního procesu nevypovídá – ten důležitý proces běží s jinou identitou a jiným rodičem dále.

    Vyvázáním se z konzole ztrácí proces místo, kam může posílat zprávy o svém stavu. Naivním řešením je, aby si každý proces otevřel logovací soubor a ten spravoval. Z mnoha důvodů to není nejlepší řešení, takže většina slušných démonů zapisuje do systémového logu reprezentovaného /dev/log a démonem syslog. Ten obvykle zapisuje všechny zprávy předané voláním syslog(2) do /var/log/messages, ale některé z implementací démona syslog podporují nejrůznější nastavení, typu zprávy tohoto démona zapisuj sem, tento typ zpráv sem a zbytek posílej po síti do vzdáleného logu a podobně.

    Nový typ démona

    link

    Lennart, mimochodem autor multiplatformní knihovny libdaemon usnadňující psaní démonů, vydal vlastní sadu doporučení, jak psát démony v novém stylu.

    1. Pokud proces obdrží SIGTERM, démon se čistě a bezpečně ukončí.
    2. Pokud proces obdrží SIGHUP, démon znovu načte svoji konfiguraci, pokud to dává smysl, jinak tento signál ignoruje.
    3. Démon by měl vracet správné návratové kódy, které používá init systém pro detekci stavu. Je dobré implementovat doporučení LSB pro SysV init skripty.
    4. Pokud je to možné, vystav rozhraní démona přes D-BUS a zaber sběrnici jako poslední krok inicializace.
    5. Můžeš se spolehnout na prostředky systemd pro ovládání limitů v systému.
    6. Napiš příslušnou .service, .socket, nebo .path jednotku.
    7. Pokud démon používá D-BUS, potom nechť je démon aktivovatelný právě přes něj. Jednak to umožní start na požádání a navíc i restarty při případném pádu démona.
    8. Pokud démon poskytuje služby přes socket, napište příslušnou .socket jednotku pro start na požádání.
    9. Pokud je to možné, démon by měl oznámit init systému dokončení startu, nebo změny stavu prostřednictvím sd_notify, nebo podobného volání.
    10. Namísto používání volání syslog(), které zprávy posílá logovacímu démonu, nový typ může jednoduše zapisovat zprávy do stderr a nechat init systém je forwardovat do systémového logu. Priority jsou kódovány na začátku řetězcem „<číslo>“, kde číslo odpovídá prioritám použitým v démonu syslog. Tato technika umožní systemd přesměrovat celý systémový log do kmsg, což může být výhodné pro embeded systémy.

    Tyto požadavky, až na první dva týkající se reakce na signály, se od tradičního doporučení liší a nový typ démona se nijak neliší od standardního programu. Základním rozdílem je totiž skutečnost, že systemd (to jest proces s PID 1) spouští procesy přímo, tudíž ty mají správného rodiče, nepotřebují se vyvazovat ze skupin a podobně.

    A jelikož systemd umí přesměrovat standardní výstup do systémového logu, mohou být všechny zprávy logovány právě na standardní výstup, takže odpadá nutnost používat speciální příkaz pro psaní zpráv. Na všechny ostatní kroky, které démoni mohou implementovat, jako omezení práv, nastavení limitů a podobně je rovněž možné a vhodné se spolehnout na systemd a jeho funkci exec_spawn.

    Závěrem

    link

    Dnešní díl se zabýval obecnou problematikou démonů. Ukázal kroky nutné k tomu, aby se z klasického procesu stal démon běžící na pozadí a také ukázal, jak systemd mění náhled na démony, které jsou v jeho podání podobné běžným programům. Mimochodem podobné požadavky vydal Apple ve spojitosti s launchd.

    V příštím díle se podíváme na zoubek .service jednotkám, což je přímá náhrada za init skripty a ukážeme si, jak triviální je se systemd napsat démona v shellu.

           

    Hodnocení: 100 %

            špatnédobré        

    Nástroje: Tisk bez diskuse

    Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

    Komentáře

    Vložit další komentář

    15.6.2011 00:59 Ivan
    Rozbalit Rozbalit vše Diky
    za pekne shrnuti. Obvykle jsem konzerva a k novym vecem nemam duveru. Jestli opravdu ma systemd takovahle doporuceni, tak to ho vezmu na milost. Skoda, ze tohle nikdy necetli autori rsyslogd (Ubuntu nahrada syslogd) popr. network-manageru.
    15.6.2011 14:05 Michal Vyskočil | skóre: 60 | blog: miblog | Praha
    Rozbalit Rozbalit vše Re: Diky
    Jaké jsou problémy s rsyslogd? Kay Sievers jej doporučoval, jako vhodnější pro systemd, než například syslog-ng.
    When your hammer is C++, everything begins to look like a thumb.
    michich avatar 15.6.2011 14:23 michich | skóre: 51 | blog: ohrivane_parky
    Rozbalit Rozbalit vše Re: Diky
    Jo, rsyslogd je jednoznačně nejlépe otestovaný syslog se systemd. I soketovou aktivaci podporuje, čehož se ve Fedoře 15 využívá.
    Bystroushaak avatar 15.6.2011 01:14 Bystroushaak | skóre: 36 | blog: Bystroushaakův blog | Praha
    Rozbalit Rozbalit vše Re: Systemd – psaní unixových démonů
    Díky za parádní článek, dost jsem se toho z něj dozvěděl.
    15.6.2011 10:54 disorder | blog: weblog
    Rozbalit Rozbalit vše Re: Systemd – psaní unixových démonů
    Takže, pokud napíšeme kill %2, shell to převede na volání killpg(SIGKILL,skupina-odpovídající-%2).
    nebude to SIGTERM?
    15.6.2011 13:48 Michal Vyskočil | skóre: 60 | blog: miblog | Praha
    Rozbalit Rozbalit vše Re: Systemd – psaní unixových démonů
    Ano, přesně tak, SIGTERM, nechápu, jak jsem to mohl tolikrát přehlédnout.
    When your hammer is C++, everything begins to look like a thumb.
    15.6.2011 12:16 loki
    Rozbalit Rozbalit vše Re: Systemd – psaní unixových démonů
    Clanek je super. Dekuji za nej. Jen bych se chtel zeptat, co jsou ty zbloudile signaly??? To jako ignorovat, kdyz by mi nahodou nekdo poslal jakykoli signal? :-)
    15.6.2011 14:04 Michal Vyskočil | skóre: 60 | blog: miblog | Praha
    Rozbalit Rozbalit vše Re: Systemd – psaní unixových démonů
    Zbloudilé signály jsou úplně normální, akorát odeslané v okamžiku démonizace. Takže je zablokujeme, aby byly zpracovány až po tom, co démon už běží, takže jej neovlivňovaly.
    When your hammer is C++, everything begins to look like a thumb.
    15.6.2011 16:48 Woky
    Rozbalit Rozbalit vše Re: Systemd – psaní unixových démonů
    Moc hezkej clanek....
    15.6.2011 19:49 Kvakor
    Rozbalit Rozbalit vše Re: Systemd – psaní unixových démonů
    Ten druhý fork() je zajímavý, něco takového jsem sice už viděl, ale nikdy v běžných démonech (tj. v takových, kteří nespouštějí žádné externí programy, které by mohly způsobit uzmutí nového terminálu). Osobně jsem pár démonů napsal a žádný nedělá dvojtý fork(), dokonce ani v běžných návodech na unixové démony se tato operace nevyskytuje (viz. např. Unix Daemon Server Programming) a ani v démonech, které jsem si kdysi prohlížel jako vzory.

    Jediné místo, kdy jsem na to narazil před tím, bylo v UNIX Programming FAQ, v části 1.7 How do I get my program to act like a daemon?, ale tam se vyskytují i speciality jako doporučení nepoužít po prvním fork()u volání exit(), ale _exit() (při kterém se nespouští registrované uživatelské "úklidové" rutiny), nebo rozdíly mezi fork() a vfork() (v Linuxu je to putna). A ano, démoní jsou klasická unixová magie :-)
    Luboš Doležel (Doli) avatar 15.6.2011 21:09 Luboš Doležel (Doli) | skóre: 98 | blog: Doliho blog | Kladensko
    Rozbalit Rozbalit vše Re: Systemd – psaní unixových démonů
    Já osobně volám daemon().
    16.6.2011 09:19 Michal Vyskočil | skóre: 60 | blog: miblog | Praha
    Rozbalit Rozbalit vše Re: Systemd – psaní unixových démonů
    Je pravda, že většina materiálů, které jsem četl používá pouze jeden fork. Nicméně The Linux Programming Interface, nebo právě man 7 daemon. Třeba libdaemon používá double fork, ale to není zase takové překvapení, protože tu knihovnu napsal Lennart.
    When your hammer is C++, everything begins to look like a thumb.
    18.6.2011 23:30 honza
    Rozbalit Rozbalit vše Re: Systemd – psaní unixových démonů
    Moc pěkný článek. Jen bych si nedovolil souhlasit s tím, že daemon je zlá, nepěkná věc. Tou je naopak demon, evil spirit, zatímco daemon je good spirit (viz třeba dictionary.com). Myslím, že původní tvůrci unix systémů si tento rozdíl velmi uvědomovali, proto jsou systémové služby 'daemons' nikoliv 'demons'.
    Josef Kufner avatar 7.2.2015 01:16 Josef Kufner | skóre: 70
    Rozbalit Rozbalit vše Re: Systemd – psaní unixových démonů
    Jen malé upřesnění: Daemon je z řeckého δαίμων, což je něco jako (dobrý) duch (spirit). Oproti tomu demon je z latiny a tedy cca křesťanské/židovské mytologie, kde to označuje ďábla a jemu blízké entity.
    Hello world ! Segmentation fault (core dumped)
    20.10.2021 21:55 Jack Wood
    Rozbalit Rozbalit vše Re: Systemd – psaní unixových démonů

    Založit nové vláknoNahoru

    ISSN 1214-1267   www.czech-server.cz
    © 1999-2015 Nitemedia s. r. o. Všechna práva vyhrazena.