GNU/Linux >> Linux Esercitazione >  >> Linux

Creazione di un demone in Linux

man 7 daemon descrive in dettaglio come creare un demone. La mia risposta è solo un estratto di questo manuale.

Esistono almeno due tipi di demoni:

  1. demoni SysV tradizionali (vecchio stile),
  2. demoni systemd (nuovo stile).

Demoni SysV

Se sei interessato al demone SysV tradizionale, dovresti implementare i seguenti passaggi:

  1. Chiudi tutti i descrittori di file aperti tranne l'input standard , output e errore (ovvero i primi tre descrittori di file 0, 1, 2). Ciò garantisce che nessun descrittore di file passato accidentalmente rimanga nel processo del demone. Su Linux, questo è meglio implementato ripetendo /proc/self/fd , con un fallback dell'iterazione dal descrittore di file 3 al valore restituito da getrlimit() per RLIMIT_NOFILE .
  2. Ripristina tutti i gestori di segnale ai valori predefiniti. È meglio farlo iterando i segnali disponibili fino al limite di _NSIG e reimpostandoli su SIG_DFL .
  3. Ripristina la maschera del segnale usando sigprocmask() .
  4. Ripulisci il blocco dell'ambiente, rimuovendo o reimpostando le variabili di ambiente che potrebbero avere un impatto negativo sul runtime del daemon.
  5. Chiama fork() , per creare un processo in background.
  6. Nel bambino, chiama setsid() per staccarsi da qualsiasi terminale e creare una sessione indipendente.
  7. Nel bambino, chiama fork() di nuovo, per garantire che il demone non possa mai riacquisire nuovamente un terminale.
  8. Chiama exit() nel primo figlio, in modo che rimanga solo il secondo figlio (l'effettivo processo daemon). Ciò garantisce che il processo daemon sia nuovamente associato a init/PID 1, come dovrebbero essere tutti i daemon.
  9. Nel processo del demone, connetti /dev/null all'input standard , output e errore .
  10. Nel processo del demone, reimposta umask a 0, in modo che le modalità del file passassero a open() , mkdir() e simili controllano direttamente la modalità di accesso dei file e delle directory creati.
  11. Nel processo del demone, cambia la directory corrente nella directory root (/ ), al fine di evitare che il demone blocchi involontariamente i punti di montaggio dallo smontaggio.
  12. Nel processo del demone, scrivi il PID del demone (come restituito da getpid() ) in un file PID, ad esempio /run/foobar.pid (per un ipotetico demone "foobar") per garantire che il demone non possa essere avviato più di una volta. Questo deve essere implementato in modo race-free in modo che il file PID venga aggiornato solo quando viene verificato contemporaneamente che il PID precedentemente memorizzato nel file PID non esiste più o appartiene a un processo esterno.
  13. Nel processo del demone, elimina i privilegi, se possibile e applicabile.
  14. Dal processo daemon, notifica al processo originale avviato che l'inizializzazione è completa. Questo può essere implementato tramite una pipe senza nome o un canale di comunicazione simile creato prima del primo fork() e quindi disponibile sia nel processo originale che in quello demone.
  15. Chiama exit() nel processo originario. Il processo che ha invocato il demone deve poter contare su questo exit() accade dopo l'inizializzazione è completa e tutti i canali di comunicazione esterni sono stabiliti e accessibili.

Nota questo avviso:

Il BSD daemon() la funzione non dovrebbe essere utilizzato, in quanto implementa solo un sottoinsieme di questi passaggi.

Un demone che deve fornire compatibilità con i sistemi SysV dovrebbe implementare lo schema sopra indicato. Tuttavia, si consiglia di rendere questo comportamento facoltativo e configurabile tramite un argomento della riga di comando per facilitare il debug e semplificare l'integrazione nei sistemi che utilizzano systemd.

Nota che daemon() non è conforme a POSIX.

Demoni di nuovo stile

Per i demoni di nuovo stile si consigliano i seguenti passaggi:

  1. Se SIGTERM viene ricevuto, chiudi il demone ed esci in modo pulito.
  2. Se SIGHUP viene ricevuto, ricaricare i file di configurazione, se applicabile.
  3. Fornire un codice di uscita corretto dal processo del demone principale, poiché viene utilizzato dal sistema init per rilevare errori e problemi del servizio. Si consiglia di seguire lo schema del codice di uscita come definito nelle raccomandazioni LSB per gli script SysV init.
  4. Se possibile e applicabile, esponi l'interfaccia di controllo del demone tramite il sistema IPC D-Bus e prendi un nome bus come ultimo passaggio dell'inizializzazione.
  5. Per l'integrazione in systemd, fornire un file di unità .service che contenga informazioni sull'avvio, l'arresto e la manutenzione del demone. Vedi systemd.service(5) per i dettagli.
  6. Per quanto possibile, affidati alla funzionalità del sistema init per limitare l'accesso del demone a file, servizi e altre risorse, ad esempio nel caso di systemd, affidati al controllo del limite delle risorse di systemd invece di implementare il tuo, affidati a il privilegio di systemd che elimina il codice invece di implementarlo nel demone e simili. Vedi systemd.exec(5) per i controlli disponibili.
  7. Se si utilizza D-Bus, rendere il demone attivabile tramite bus fornendo un file di configurazione per l'attivazione del servizio D-Bus. Ciò ha molteplici vantaggi:il tuo demone può essere avviato pigramente su richiesta; può essere avviato in parallelo ad altri demoni che lo richiedono, il che massimizza la parallelizzazione e la velocità di avvio; il tuo demone può essere riavviato in caso di errore senza perdere alcuna richiesta di bus, poiché il bus accoda le richieste di servizi attivabili. Vedi sotto per i dettagli.
  8. Se il tuo demone fornisce servizi ad altri processi locali o client remoti tramite un socket, dovrebbe essere reso attivabile tramite socket seguendo lo schema indicato di seguito. Come l'attivazione di D-Bus, ciò consente l'avvio su richiesta dei servizi e consente una migliore parallelizzazione dell'avvio del servizio. Inoltre, per i protocolli senza stato (come syslog, DNS), un demone che implementa l'attivazione basata su socket può essere riavviato senza perdere una sola richiesta. Vedi sotto per i dettagli.
  9. Se applicabile, un demone dovrebbe notificare al sistema init il completamento dell'avvio o gli aggiornamenti dello stato tramite sd_notify(3) interfaccia.
  10. Invece di usare il syslog() chiamata per accedere direttamente al servizio syslog di sistema, un demone di nuovo stile può scegliere di accedere semplicemente all'errore standard tramite fprintf() , che viene quindi inoltrato a syslog dal sistema init. Se i livelli di log sono necessari, questi possono essere codificati anteponendo alle singole righe di log stringhe come "<4>" (per il livello di log 4 "WARNING" nello schema di priorità syslog), seguendo uno stile simile al printk() sistema di livelli. Per i dettagli, vedi sd-daemon(3) e systemd.exec(5) .

Per saperne di più leggi tutto man 7 daemon .


In Linux voglio aggiungere un demone che non può essere fermato e che monitora le modifiche al filesystem. Se vengono rilevate modifiche, dovrebbe scrivere il percorso della console in cui è stato avviato + una nuova riga.

I demoni funzionano in background e (di solito...) non appartengono a un TTY, ecco perché non puoi usare stdout/stderr nel modo che probabilmente vorresti. Di solito un demone syslog (syslogd ) viene utilizzato per registrare i messaggi nei file (debug, errore,...).

Oltre a ciò, ci sono alcuni passaggi obbligatori per demonizzare un processo.

Se non ricordo male questi passaggi sono:

  • forchetta fuori dal processo genitore e lasciarlo terminare se il fork ha avuto successo. -> Poiché il processo padre è terminato, il processo figlio ora viene eseguito in background.
  • setsid - Crea una nuova sessione. Il processo chiamante diventa il leader della nuova sessione e il leader del gruppo di processi del nuovo gruppo di processi. Il processo è ora scollegato dal suo terminale di controllo (CTTY).
  • Segnali di cattura - Ignora e/o gestisci i segnali.
  • biforca di nuovo &lascia terminare il processo genitore per assicurarti di sbarazzarti del processo principale della sessione. (Solo i leader di sessione possono ottenere di nuovo un TTY.)
  • chdir - Cambia la directory di lavoro del demone.
  • umask - Modificare la maschera della modalità file in base alle esigenze del demone.
  • chiudi - Chiude tutti i descrittori di file aperti che possono essere ereditati dal processo padre.

Per darti un punto di partenza:guarda questo codice scheletro che mostra i passaggi di base. Questo codice può ora essere biforcato anche su GitHub:scheletro di base di un demone Linux

/*
 * 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;
}


  • Compila il codice:gcc -o firstdaemon daemonize.c
  • Avvia il demone:./firstdaemon
  • Controlla se tutto funziona correttamente:ps -xj | grep firstdaemon

  • L'output dovrebbe essere simile a questo:

+------+------+------+------+-----+-------+------+------+------+-----+
| PPID | PID  | PGID | SID  | TTY | TPGID | STAT | UID  | TIME | CMD |
+------+------+------+------+-----+-------+------+------+------+-----+
|    1 | 3387 | 3386 | 3386 | ?   |    -1 | S    | 1000 | 0:00 | ./  |
+------+------+------+------+-----+-------+------+------+------+-----+

Quello che dovresti vedere qui è:

  • Il demone non ha un terminale di controllo (TTY =? )
  • L'ID del processo principale (PPID ) è 1 (Il processo init)
  • Il PID !=SID il che significa che il nostro processo NON è il leader della sessione
    (a causa del secondo fork())
  • Poiché PID !=SID il nostro processo non può riprendere il controllo di un TTY

Lettura del registro di sistema:

  • Individua il tuo file syslog. Il mio è qui:/var/log/syslog
  • Fai un:grep firstdaemon /var/log/syslog

  • L'output dovrebbe essere simile a questo:

  firstdaemon[3387]: First daemon started.
  firstdaemon[3387]: First daemon terminated.


Una nota: In realtà vorresti anche implementare un gestore di segnale e configurare correttamente la registrazione (file, livelli di registro...).

Ulteriori letture:

  • Linux-UNIX-Programmierung - Tedesco
  • Programmazione Unix Daemon Server

Linux
  1. Linux:bloccare l'accesso alla rete di un processo?

  2. Linux:un processo "subreaper"?

  3. Introduzione ai thread di Linux – Parte I

  4. Servizio del sistema operativo Linux 'sshd'

  5. Processo di avvio di Linux

Come uccidere un processo in Linux

Comando Ps in Linux (Elenca processi)

Comando Pstree in Linux

Kill Command in Linux

Monitoraggio dei processi su Linux

Come KILL un processo su Linux