GNU/Linux >> Tutoriales Linux >  >> Linux

Crear un demonio en Linux

man 7 daemon describe cómo crear un demonio con gran detalle. Mi respuesta es solo un extracto de este manual.

Hay al menos dos tipos de demonios:

  1. demonios SysV tradicionales (estilo antiguo),
  2. demonios systemd (nuevo estilo).

Demonios SysV

Si está interesado en el demonio SysV tradicional, debe implementar los siguientes pasos:

  1. Cerrar todos los descriptores de archivos abiertos excepto la entrada estándar , salida y error (es decir, los primeros tres descriptores de archivo 0, 1, 2). Esto asegura que ningún descriptor de archivo pasado accidentalmente permanezca en el proceso del daemon. En Linux, esto se implementa mejor iterando a través de /proc/self/fd , con una alternativa de iterar desde el descriptor de archivo 3 hasta el valor devuelto por getrlimit() para RLIMIT_NOFILE .
  2. Restablece todos los controladores de señales a sus valores predeterminados. Esto se hace mejor iterando a través de las señales disponibles hasta el límite de _NSIG y restablecerlos a SIG_DFL .
  3. Restablecer la máscara de señal usando sigprocmask() .
  4. Desinfecte el bloque de entorno, eliminando o restableciendo las variables de entorno que podrían afectar negativamente el tiempo de ejecución del daemon.
  5. Llamar fork() , para crear un proceso en segundo plano.
  6. En el niño, llame al setsid() para desconectarse de cualquier terminal y crear una sesión independiente.
  7. En el niño, llame al fork() de nuevo, para asegurarse de que el daemon nunca pueda volver a adquirir una terminal.
  8. Llamar exit() en el primer hijo, de modo que solo el segundo hijo (el proceso daemon real) permanece. Esto asegura que el proceso del demonio se vuelva a vincular a init/PID 1, como deberían ser todos los demonios.
  9. En el proceso daemon, conecte /dev/null a la entrada estándar , salida y error .
  10. En el proceso del demonio, restablezca el umask a 0, para que los modos de archivo pasen a open() , mkdir() y similares controlan directamente el modo de acceso de los archivos y directorios creados.
  11. En el proceso daemon, cambie el directorio actual al directorio raíz (/ ), para evitar que el demonio bloquee involuntariamente los puntos de montaje para que no se desmonten.
  12. En el proceso del daemon, escriba el PID del daemon (tal como lo devuelve getpid() ) a un archivo PID, por ejemplo /run/foobar.pid (para un demonio hipotético "foobar") para garantizar que el demonio no se pueda iniciar más de una vez. Esto debe implementarse sin carreras para que el archivo PID solo se actualice cuando se verifique al mismo tiempo que el PID previamente almacenado en el archivo PID ya no existe o pertenece a un proceso externo.
  13. En el proceso del daemon, elimine los privilegios, si es posible y aplicable.
  14. Desde el proceso daemon, notifique al proceso original iniciado que se completó la inicialización. Esto se puede implementar a través de una canalización sin nombre o un canal de comunicación similar que se crea antes del primer fork() y, por lo tanto, disponible tanto en el proceso original como en el daemon.
  15. Llamar exit() en el proceso original. El proceso que invocó al daemon debe poder confiar en que este exit() sucede después la inicialización está completa y todos los canales de comunicación externos están establecidos y accesibles.

Tenga en cuenta esta advertencia:

El BSD daemon() función no debería ser usado, ya que implementa solo un subconjunto de estos pasos.

Un demonio que necesita proporcionar compatibilidad con sistemas SysV se debe implementar el esquema señalado anteriormente. Sin embargo, se recomienda hacer que este comportamiento sea opcional y configurable a través de un argumento de línea de comando para facilitar la depuración y simplificar la integración en los sistemas que usan systemd.

Tenga en cuenta que daemon() no es compatible con POSIX.

Demonios de nuevo estilo

Para demonios de nuevo estilo, se recomiendan los siguientes pasos:

  1. Si SIGTERM se recibe, apague el daemon y salga limpiamente.
  2. Si SIGHUP se recibe, vuelva a cargar los archivos de configuración, si corresponde.
  3. Proporcione un código de salida correcto del proceso del demonio principal, ya que el sistema init lo utiliza para detectar errores y problemas de servicio. Se recomienda seguir el esquema de código de salida como se define en las recomendaciones de LSB para los scripts de inicio de SysV.
  4. Si es posible y aplicable, exponga la interfaz de control del daemon a través del sistema D-Bus IPC y tome un nombre de bus como último paso de inicialización.
  5. Para la integración en systemd, proporcione un archivo de unidad de servicio que contenga información sobre cómo iniciar, detener y mantener el daemon. Ver systemd.service(5) para más detalles.
  6. En la medida de lo posible, confíe en la funcionalidad del sistema init para limitar el acceso del daemon a archivos, servicios y otros recursos, es decir, en el caso de systemd, confíe en el control de límite de recursos de systemd en lugar de implementar el suyo propio, confíe en el código de eliminación de privilegios de systemd en lugar de implementarlo en el daemon, y similares. Ver systemd.exec(5) para los controles disponibles.
  7. Si se utiliza D-Bus, haga que su daemon bus sea activable proporcionando un archivo de configuración de activación del servicio D-Bus. Esto tiene múltiples ventajas:su daemon puede iniciarse de forma perezosa bajo demanda; puede iniciarse en paralelo con otros demonios que lo requieran, lo que maximiza la paralelización y la velocidad de arranque; su daemon se puede reiniciar en caso de falla sin perder ninguna solicitud de bus, ya que el bus pone en cola las solicitudes de servicios activables. Vea a continuación para obtener más detalles.
  8. Si su daemon brinda servicios a otros procesos locales o clientes remotos a través de un socket, debe habilitarse como socket activable siguiendo el esquema que se indica a continuación. Al igual que la activación de D-Bus, esto permite el inicio de servicios a pedido y permite una mejor paralelización del inicio del servicio. Además, para los protocolos sin estado (como syslog, DNS), se puede reiniciar un demonio que implementa la activación basada en sockets sin perder una sola solicitud. Vea a continuación para obtener más detalles.
  9. Si corresponde, un demonio debe notificar al sistema init sobre la finalización del inicio o las actualizaciones de estado a través del sd_notify(3) interfaz.
  10. En lugar de usar el syslog() llamada para iniciar sesión directamente en el servicio syslog del sistema, un demonio de estilo nuevo puede elegir simplemente iniciar sesión en el error estándar a través de fprintf() , que luego es reenviado a syslog por el sistema init. Si los niveles de registro son necesarios, estos se pueden codificar prefijando líneas de registro individuales con cadenas como "<4>" (para el nivel de registro 4 "ADVERTENCIA" en el esquema de prioridad de syslog), siguiendo un estilo similar al printk() sistema de niveles. Para obtener más información, consulte sd-daemon(3) y systemd.exec(5) .

Para obtener más información, lea el man 7 daemon completo. .


En Linux, quiero agregar un demonio que no se puede detener y que monitorea los cambios en el sistema de archivos. Si se detectara algún cambio, debe escribir la ruta a la consola donde se inició + una nueva línea.

Los demonios funcionan en segundo plano y (generalmente...) no pertenecen a un TTY, por eso no puede usar stdout/stderr de la manera que probablemente desee. Por lo general, un demonio syslog (syslogd ) se utiliza para registrar mensajes en archivos (depuración, error,...).

Además de eso, hay algunos pasos requeridos para demonizar un proceso.

Si mal no recuerdo estos pasos son:

  • tenedor desactive el proceso principal y déjelo terminar si la bifurcación fue exitosa. -> Debido a que el proceso principal finalizó, el proceso secundario ahora se ejecuta en segundo plano.
  • setid - Crear una nueva sesión. El proceso de llamada se convierte en el líder de la nueva sesión y el líder del grupo de procesos del nuevo grupo de procesos. El proceso ahora está separado de su terminal de control (CTTY).
  • Captar señales - Ignorar y/o manejar señales.
  • bifurcación de nuevo y deje que el proceso principal finalice para asegurarse de deshacerse del proceso principal de la sesión. (Solo los líderes de sesión pueden obtener un TTY nuevamente).
  • chdir - Cambiar el directorio de trabajo del daemon.
  • máscara - Cambiar la máscara del modo de archivo según las necesidades del demonio.
  • cerrar - Cerrar todos los descriptores de archivos abiertos que puedan ser heredados del proceso padre.

Para darle un punto de partida:mire este código esqueleto que muestra los pasos básicos. Este código ahora también se puede bifurcar en GitHub:esqueleto básico de un demonio de 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 el código:gcc -o firstdaemon daemonize.c
  • Inicie el demonio:./firstdaemon
  • Comprueba si todo funciona correctamente:ps -xj | grep firstdaemon

  • La salida debería ser similar a esta:

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

Lo que debería ver aquí es:

  • El daemon no tiene terminal de control (TTY =? )
  • El ID del proceso principal (PPID ) es 1 (El proceso de inicio)
  • El PID !=SID lo que significa que nuestro proceso NO es el líder de la sesión
    (debido a la segunda bifurcación())
  • Porque PID !=SID nuestro proceso no puede volver a tomar el control de un TTY

Leyendo el syslog:

  • Ubique su archivo syslog. El mío está aquí:/var/log/syslog
  • Haz un:grep firstdaemon /var/log/syslog

  • La salida debería ser similar a esta:

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


Una nota: En realidad, también le gustaría implementar un controlador de señales y configurar el registro correctamente (archivos, niveles de registro...).

Lecturas adicionales:

  • Linux-UNIX-Programmierung - alemán
  • Programación del servidor Unix Daemon

Linux
  1. Cómo matar un proceso zombie en Linux

  2. Linux:¿un proceso de "subsegador"?

  3. Introducción a los subprocesos de Linux - Parte I

  4. Ejemplos de comandos kill en Linux

  5. ¿Daemonizar un proceso en shell?

Cómo matar un proceso en Linux

Comando Ps en Linux (Lista de Procesos)

Comando Pstree en Linux

Comando matar en Linux

Supervisión de procesos en Linux

Cómo MATAR un proceso en Linux