Demonii, documentația de la A la Z

Demonii din lumea Unix sunt în mod tradițional numiți procese care nu interacționează direct cu utilizatorul. Procesul daemon nu are un terminal de control și nu există interfață de utilizator, respectiv. Pentru a gestiona daemoanele, trebuie să utilizați alte programe. Numai numele "demonilor" a apărut din faptul că multe procese de acest tip petrec în majoritatea timpului așteptând un eveniment. Când vine acest eveniment, demonul se activează (sare ca un diavol dintr-un cartuș), își face treaba și din nou adoarme așteaptă un eveniment. Trebuie remarcat faptul că mulți demoni, cum ar fi, de exemplu, un server Web sau un server de baze de date, pot selecta aproape toate resursele procesorului și alte resurse ale sistemului. Astfel de demoni lucrează mult mai mult decât ei dorm.







Acum vom trece peste blocul dacă declarațiile (argc> 1) (vom reveni la el mai târziu) și vom lua în considerare principalele etape ale funcționării daemonului. Funcția BecomeDaemonProcess () transformă procesul normal al consolei Linux într-un proces de daemon. ConfigureSignalHandlers () funcție reglează procesul de semnal daemon stivuitoare, și funcția BindPassiveSocket () pentru a deschide anumite porturi TCP / IP pentru a asculta cererile primite. Apoi urmează buclă în care serverul procesează cererile. Multe servere de rețea, după ce au primit solicitarea, creează un proces copil pentru procesarea acestora. Astfel, se realizează posibilitatea procesării paralele a cererilor. Unele servere utilizează fluxuri pentru procesarea paralelă a cererilor. În ceea ce privește serverul nostru, din motive de simplitate, procesează cererile în modul secvențial (blocat). Nu ne așteptăm ca serverul demo să primească multe cereri, nu?

Ieșirea normală din ciclul de procesare a interogării are loc atunci când procesul primește semnalul SIGUSER1. După ieșirea din buclă, procesul cheamă funcția TidyUp () și iese. Putem finaliza cu siguranță daemon prin trimiterea un semnal SIGKILL (SIGTERM și altele), dar semnalul SIGUSER1 utilizator asigura finalizarea demoga nostru prietenos. "Completare politică" înseamnă că serverul va răspunde cererii curente înainte de a termina și șterge fișierul pid.

Facem directorul rădăcină directorul curent al procesului daemon. Odată lansat, daemonul nostru poate funcționa până când sistemul este repornit, așa că directorul său curent ar trebui să aparțină unui sistem de fișiere care nu poate fi demontat. Următorul este apelul la blocareFD = deschis (lockFileName, O_RDWR | O_CREAT | O_EXCL, 0644);

Fiecare proces daemon creează un așa-numit fișier pid (sau un fișier de blocare). Acest fișier este de obicei găsite în directorul / var / run și este numit daemon.pid, în cazul în care „daemon“ este numele unui demon. Fișierul de blocare conține valoarea PID a procesului daemon. Acest fișier este important din două motive. În primul rând, prezența sa face posibilă stabilirea faptului că o instanță a daemonului este deja rulată în sistem. Cele mai multe daemon, inclusiv ale noastre, au vypolnyayatsya nu mai mult de o copie (acest lucru este logic, având în vedere că demonii se dovedesc adesea la resurse nonsharable, cum ar fi porturi de rețea). După ce se termină, procesul daemon șterge fișierul pid, indicând că puteți porni o altă instanță a procesului. Cu toate acestea, daemon nu este întotdeauna completează în mod normal, iar apoi pe disc este un proces inexistent BIP-fișier. Acest lucru pare a fi un obstacol de netrecut pentru a reporni daemon, dar într-adevăr, demonii face față cu succes cu astfel de situații. În timpul procesului de pornire, daemonul verifică un fișier pid cu numele corespunzător de pe disc. În cazul în care există un astfel de fișier, se citește valoarea daemon PID și folosind ucide (2) verifică dacă există un proces cu un sistem PID specificat. Dacă procesul există, utilizatorul încearcă din nou să pornească daemonul. În acest caz, programul afișează un mesaj corespunzător și se termină. Dacă nu există nici un proces cu PID specificat în sistem, atunci fișierul pid a aparținut daemonului prăbușit. În această situație, programul recomandă, de obicei, utilizatorul pentru a elimina pid-file (răspunderea în astfel de cazuri, este întotdeauna mai bine să treacă pe la utilizator) și să încerce să-l rulați din nou. Se poate, desigur, se întâmplă, de asemenea, că, după accident demonul va ramane sa-BIP fișier de pe disc, și apoi un alt proces va primi același PID, care a fost un demon. În această situație pornește din nou demonul va arata ca si cum o copie este deja rulează în sistem, și re-porni demonul nu poți. Din fericire, situația descrisă este extrem de puțin probabilă.

Al doilea motiv pentru care fișierul de blocare este considerat util este faptul că, cu acest fișier, putem afla rapid PID-ul daemonului fără a folosi comanda ps.

Apoi, daemonul nostru cheamă funcția furcă (3), care creează o copie a procesului său. Procesul parental este finalizat:

Acest lucru se face astfel încât procesul demonic se deconectează de la terminalul de comandă. Un set de grupuri de procese, numite sesiuni, este asociat cu fiecare terminal Unix. În orice moment, numai unul din grupurile de proces care intră în sesiune are acces la terminal (adică poate efectua I / O cu terminalul). Acest grup se numește prim plan (prioritate). În fiecare sesiune există un progenitor de proces, numit liderul sesiunii. Dacă procesul daemon este pornit din consola, acesta devine, firesc, parte din grupul prioritar de procese care intră în sesiunea terminalului corespunzător.

Pentru a se deconecta de la terminal, daemonul trebuie să înceapă o sesiune nouă, care nu este conectată la niciun terminal. Pentru ca un demon să înceapă o nouă sesiune, el însuși nu ar trebui să fie conducătorul oricărei alte sesiuni. Apelul furcă () creează un proces copil despre care nu se știe că este liderul sesiunii. Apoi, procesul copil, obtinut cu furca (), incepe o noua sesiune prin apelarea functiei setsid (2). În același timp, procesul devine lider (și singurul participant) al noii sesiuni.

Deci, în acest stadiu avem un proces care nu este conectat la niciun terminal. Mai mult, unele linii directoare recomanda cauza furcii () din nou, la un nou proces a încetat să mai fie liderul noii sesiuni (lider de sesiune System V se poate obține în mod automat terminalul de control în anumite condiții). În Linux, apelul către furcă () nu este necesar și nu o vom face.







Este demn de remarcat faptul că acum daemonul nostru a primit un PID nou, pe care trebuie să-l scriem din nou în dosarul daemonului. Vom scrie valoarea PID într-un fișier într-o formă de șir (și nu ca o variabilă de tip pid_t). Acest lucru este făcut pentru comoditatea utilizatorului, astfel încât valoarea PID din fișierul pid poate fi citită cu ajutorul pisicii. De exemplu:

demonul a reușit să rupă legătura cu terminalul de la care a fost lansat, dar poate fi în continuare asociat cu alte procese și sisteme de fișiere prin descriptori de fișier moștenite de la procesul părinte. Pentru a rupe această conexiune, închidem de asemenea toate descriptorii de fișiere care sunt deschise în procesul nostru:

Funcția sysconf () cu parametrul _SC_OPEN_MAX returnează numărul maxim de descriptori pe care programul nostru le poate deschide. Se numește funcția close () pentru fiecare descriptor (indiferent dacă este deschisă sau nu), cu excepția descriptorului fișierului pid, care trebuie să rămână deschis.

În timpul funcționării daemonului, descriptorii fluxurilor standard de intrare, ieșire și erori ar trebui de asemenea să fie deschise, deoarece acestea sunt cerute de multe funcții ale bibliotecii standard. În același timp, acești descriptori nu ar trebui să indice la nici un flux real de intrare / ieșire. Pentru a rezolva această problemă, închidem primii trei descriptori și apoi le deschidem din nou, specificând / dev / null ca nume de fișier:

Acum putem fi siguri că daemonul nu va avea acces la niciun terminal. Cu toate acestea, demonul ar trebui să poată transmite mesaje despre munca sa. În mod tradițional, fișierele jurnal sunt utilizate în acest scop. Fișierele de jurnal pentru daemon sunt similare cu cutiile negre ale aeronavelor. Dacă există o eroare în funcționarea daemonului, utilizatorul poate analiza fișierul jurnal și (cu un anumit noroc) poate determina cauza eșecului. Nimic nu împiedică demonul nostru să-și deschidă propriul dosar de jurnal, dar acest lucru nu este foarte convenabil. Cei mai mulți demoni folosesc serviciile utilitarului syslog, care păstrează un jurnal al mai multor evenimente de sistem. Deschidem accesul la jurnalul syslog folosind funcția openlog (3):

Primul parametru al funcției openlog () este prefixul care va fi adăugat la fiecare intrare din jurnalul de sistem. Aceasta este urmată de diferitele opțiuni syslog. Funcția setlogmask (3) vă permite să setați nivelul de prioritate al mesajelor care sunt scrise în jurnalul de evenimente. Când apelăm funcția BecomeDaemonProcess (), trecem valoarea LOG_DEBUG în parametrul logLevel. În combinație cu macrocomanda LOG_UPTO, aceasta înseamnă că toate mesajele cu prioritate, de la cea mai înaltă la LOG_DEBUG, vor fi înregistrate.

Ultimul lucru pe care trebuie să-l facem pentru a "demoniza" procesul este de a apela funcția setpgrp ();

Acest apel creează un nou grup de procese, identificatorul căruia este ID-ul curent al procesului. Aceasta finalizează activitatea funcției BecomeDaemonProcess (), deoarece procesul nostru a devenit un adevărat daemon.

Funcția ConfigureSignalHandlers () configurează gestionarea semnalelor. Semnalele pe care demonul nostru le vor primi pot fi împărțite în trei grupe: ignorate, "fatale" și prelucrate. Apelarea semnalului funcției (SIGUSR2, SIG_IGN);

Funcția Handler FatalSigHandler () scrie informații despre semnalul recepționat în jurnalul de evenimente și apoi termină procesul, apelează înainte de funcțiile closelog () și TidyUp (), care eliberează toate resursele ocupate de proces:

Managerul TermHandler () cheamă funcția TidyUp () și termină procesul. Handler Usr1Handler () face ca înregistrarea jurnalul de sistem de la sfârșitul politicoasă procesului și atribuie valoarea gGracefulShutdown 1 (care, după cum vă amintiți, iese din ciclul de procesare a cererii, în cazul în care ciclul este gata pentru ea). semnal procesor HupHandler () face, de asemenea, o intrare în jurnalul de sistem, apoi atribuie valoarea variabilei 1 și gGracefulShutdown gCaughtHupSignal. În viața reală semnal SIGHUP primirea conduce la repornirea Daemon, care este însoțită de re-citirea fișierului de configurare (care este de obicei citit daemon în timpul pornirii) și resetarea valorile stocate în acesta parametrii. Este nevoie să citiți din nou fișierul de configurare, este motivul cel mai frecvent pentru a reporni daemoanele. Daemonul nostru nu are un fișier de configurare, deci în timpul repornii nu are nimic de a face.

Observați tipul variabilelor gGracefulShutdown și gCaughtHupSignal. Nu am mai întâlnit cu tipul sig_atomic_t înainte. Utilizarea acestui tip asigură că citirea și scrierea datelor către gGracefulShutdown și gCaughtHupSignal se va face în mod atomic, cu o singură instrucțiune procesor care nu poate fi întreruptă. Atomicitatea atunci când se lucrează cu variabilele gGracefulShutdown și gCaughtHupSignal este importantă pentru că pot accesa simultan ambele dispozitive de gestionare a semnalului și funcția principală a programului. Din același motiv, marcați aceste variabile cu cuvântul cheie volatil.

Funcția BindPassiveSocket () deschide portul de server (în acest caz portul 30333) pentru ascultarea pe toate interfețele de rețea disponibile și returnează soclul corespunzător:

Cei care citesc articolul din seria dedicată socket-urilor trebuie să înțeleagă ce se întâmplă aici. Să notăm doar un detaliu interesant. În cazul în care anterior, deja închis, priza asociat cu acest port se află în starea TIME_WAIT, întârzierea este egală cu două perioade ale segmentului de viață (întârzierea poate fi de până la două minute) poate să apară între închiderea vechi și deschiderea noii soclu. Pentru ca noi să nu așteptăm repornirea daemonului, vom folosi funcția setsockopt () cu parametrul SO_REUSEADDR.

Funcția AcceptConnections () procesează solicitările succesive folosind apelul accept () de blocare:

Aceasta nu este cea mai bună modalitate de a se comporta demonul, dar dacă începem să descriem procesarea paralelă a cererilor, echipa editorială nu va supraviețui. Variabila de lucru, împreună cu variabila gGracefulShutdown, indică dacă programul ar trebui să continue să proceseze cererile. Dacă următorul apel pentru conectare () sau HandleConnection () returnează un mesaj de eroare, această variabilă este setată la 0 și procesarea cererii se oprește. Noul socket primit ca rezultat al apelului accept () este transmis funcției HandleConnection ().

Funcția HandleConnection () citește șirul trecut de client și îl returnează imediat clientului. Funcția AcceptConnections () închide atunci conexiunea care a fost deschisă ca urmare a apelului accept (). Funcțiile ReadLine () și WriteToSocket () sunt triviale și nu le vom lua în considerare. Dacă undeva în apelurile AcceptConnections lanț (), HandleConnection (), readline () și WriteToSocket () eroare apare, informațiile de eroare vor fi transmise până lanțul de până, până când ajunge la funcția principală (). În funcția principal (), această informație va termina imediat daemonul cu intrarea corespunzătoare din jurnalul de mesaje sistem.

În final, ia în considerare funcția TidyUp (), la care sunt accesate multe funcții ale serverului înainte de a fi terminată.

Sarcina funcției TidyUp () este de a "curăța gunoiul" din spatele procesului demonic. În principiu, puteți face acest lucru fără această funcție deoarece, după terminarea procesului, sistemul va închide toate descriptorii, dar regulile de bună practică necesită eliberarea explicită a tuturor resurselor alocate în mod explicit.

Dacă compilați un daemon utilizând comanda

Apoi puteți porni daemonul cu

Deoarece daemonul are nevoie de acces la directorul / var / run, trebuie să îl rulați în modul root. Imediat după pornirea programului, veți vedea din nou un prompt de comandă, care este perfect normal pentru demoni. Dacă serverul aahzd a făcut ceva util, comanda de pornire ar putea fi setată într-unul din scripturile de pornire ale sistemului din directorul /etc/init.d, dar nu vom face asta. După pornirea serverului, puteți da comanda

Ca urmare, se va stabili o conexiune la server. Introduceți o succesiune de caractere în consola telnet și apăsați Enter. Ca răspuns, serverul va returna șirul tastat și va închide conexiunea.

Să revenim acum la liniile inițiale ale funcției principal (). Deși putem obține PID-ul daemonului din fișierul pid și controlam daemonul cu comanda kill, această opțiune nu poate fi numită foarte convenabilă. Deseori, fișierul daemon executabil în sine, care este rulat cu argumente speciale de linie de comandă, este folosit pentru a controla daemonul. Demonul nostru înțelege două comenzi: oprirea (oprirea daemonului) și repornirea (reporniți). Să vedem cum rulează demonul cu argumentele liniei de comandă. În acest caz, la începutul programului, daemonul încearcă să citească valoarea PID din fișierul pid. Dacă nu puteți deschide fișierul pid, cel mai probabil, daemonul nu rulează și modul de control nu are nimic de făcut. Dacă se primește valoarea PID, procesul de control al daemonului trimite semnalul corespunzător către daemon folosind funcția kill ().

Demonii nu sunt proiectați să predea nici o informație din partea utilizatorului. Ei transmit propriile lor informații altor programe sau le scriu în jurnalele de evenimente ale sistemului. În următorul articol, ne vom concentra asupra programelor care se comportă foarte diferit și iau în considerare consola I / O.







Articole similare

Trimiteți-le prietenilor: