Cómo hacer un proceso daemon

6 minutos de lectura

Avatar de usuario del usuario registrado
usuario registrado

Estoy tratando de entender cómo puedo hacer que mi programa sea un demonio. Entonces, algunas cosas con las que me encontré son, en general, un programa realiza los siguientes pasos para convertirse en un demonio:

  1. Llamar fork().

  2. En el padre, llame exit(). Esto asegura que el padre original (el abuelo del daemon) esté satisfecho de que su hijo terminó, que el padre del daemon ya no se está ejecutando y que el daemon no es un líder de grupo de procesos. Este último punto es un requisito para completar con éxito el siguiente paso.

  3. Llamar setsid(), dándole al daemon un nuevo grupo de proceso y sesión, ambos lo tienen como líder. Esto también garantiza que el proceso no tenga un terminal de control asociado (ya que el proceso acaba de crear una nueva sesión y no la asignará).

  4. Cambie el directorio de trabajo al directorio raíz a través de chdir(). Esto se hace porque el directorio de trabajo heredado puede estar en cualquier parte del sistema de archivos. Los demonios tienden a ejecutarse durante el tiempo de actividad del sistema, y ​​no desea mantener abierto un directorio aleatorio y, por lo tanto, evitar que un administrador desmonte el sistema de archivos que contiene ese directorio.

  5. Cierre todos los descriptores de archivos.

  6. Abra los descriptores de archivo 0, 1 y 2 (entrada estándar, salida estándar y error estándar) y rediríjalos a /dev/null.

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fs.h>

int main (void)
{
    pid_t pid;
    int i;
    
    /* create new process */
    pid = fork ( );  
    if (pid == -1)  
        return -1;  
    else if (pid != 0)  
        exit (EXIT_SUCCESS);  

    /* create new session and process group */  
    if (setsid ( ) == -1)  
        return -1;  

    /* set the working directory to the root directory */  
    if (chdir ("https://stackoverflow.com/") == -1)  
        return -1;  

    /* close all open files--NR_OPEN is overkill, but works */  
    for (i = 0; i < NR_OPEN; i++)  
        close (i);  

    /* redirect fd's 0,1,2 to /dev/null */  
    open ("/dev/null", O_RDWR);  
    /* stdin */  
    dup (0);  
    /* stdout */  
    dup (0);  
    /* stderror */  
    
    /* do its daemon thing... */  
    
    return 0;  
}

¿Puede alguien darme un enlace al código fuente existente de algún programa como Apache para que pueda entender este proceso con más profundidad?

  • Puedes obtener fuentes de Apache aquí.

    – nmichaels

    21 de marzo de 2011 a las 21:51

  • Quizás demonio(3) podría ser útil. Básicamente dice que la funcionalidad necesaria ya está implementada.

    – Vlad

    21 de marzo de 2011 a las 21:53


  • @nmichaels gracias por su mensaje, en realidad solo estoy buscando esas líneas que lo convierten en un demonio. Apache no específicamente. Entonces, si algunos están en línea, será más fácil que descargar la fuente completa.

    – Usuario registrado

    21 de marzo de 2011 a las 21:53

  • Relacionado con stackoverflow.com/questions/3095566/linux-daemonize

    – ninjalj

    21 de marzo de 2011 a las 22:33

  • La publicación anterior es casi 100% copiar y pegar del libro. Programación del sistema Linux, 2.ª edición: hablar directamente con el kernel y la biblioteca C por Roberto amor (páginas 173, 174). Sería bueno si @RegisteredUser hubiera mencionado eso.

    – patryk.beza

    7 de agosto de 2016 a las 9:53


avatar de usuario de deadbeef
carne de res muerta

Si está buscando un enfoque limpio, considere usar una API estándar. int daemon(int nochdir, int noclose);. Página de manual bastante simple y que se explica por sí misma. página man. Una API bien probada supera con creces nuestra propia implementación en términos de portabilidad y estabilidad.

  • Me alegro de que esto sea un responder ahora, en lugar de estar oculto en los comentarios. Tal vez podría desarrollarlo un poco: enlace al estándar (o incluso mejor, un enlace a un tutorial o ejemplo).

    – RJHunter

    22 de mayo de 2016 a las 10:34

  • @deadbeef Esa respuesta es mucho mejor que la simple. solo enlace respuestas que estaban presentes antes. Pero aunque también está bastante cerca de un solo enlace (de todos modos, consideraría el enlace dado mucho más estable). Intentaría mejorar la respuesta, dando una muestra de código corto (especialmente porque no hay ninguno en la página de manual vinculada) o citando la sinopsis.

    – πάντα ῥεῖ

    23 mayo 2016 a las 17:33


  • Tenga en cuenta que demonio la función es no Compatible con POSIX.

    – patryk.beza

    7 de agosto de 2016 a las 9:56

  • En freedesktop.org/software/systemd/man/daemon.html#SysV%20Daemons está escrito que “el BSD daemon() La función no debe usarse, ya que implementa solo un subconjunto de estos [15] pasos.”

    – oli_arborum

    15 de febrero de 2017 a las 8:10

Avatar de usuario de Alok Prasad
Alok Prasad

En Linux, se puede hacer fácilmente usando:

int main(int argc, char* argv[])
{
    daemon(0,0);
    while(1)
    {
        sleep(10)
        /*do something*/
    }

    return 0;
}

Avatar de usuario de Rachid K.
Rachid K.

Con respecto a la parte que cierra los descriptores de archivo, bajo Linux, el GLIBC deamon() El servicio simplemente redirige los descriptores de archivo 0, 1 y 2 a /dev/null usando dup2(). Aquí está el fragmento de código de daemon() en GLIBC 2.34 (archivo misc/daemon.c):

    if (!noclose) {
        struct stat64 st;

        if ((fd = __open_nocancel(_PATH_DEVNULL, O_RDWR, 0)) != -1
            && (__builtin_expect (__fstat64 (fd, &st), 0)
            == 0)) {
            if (__builtin_expect (S_ISCHR (st.st_mode), 1) != 0
#if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
                && (st.st_rdev
                == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR))
#endif
                ) {
                (void)__dup2(fd, STDIN_FILENO);
                (void)__dup2(fd, STDOUT_FILENO);
                (void)__dup2(fd, STDERR_FILENO);
                if (fd > 2)
                    (void)__close (fd);

Cerrar todos los posibles descriptores de archivos abiertos usando el NR_OPEN constante (típicamente igual a 1024) no es confiable ya que el límite en el número de descriptores de archivos abiertos se puede cambiar con setrlimit(RLIMIT_NOFILE, ...).
Los descriptores de archivo actualmente abiertos están presentes como nombres de enlaces simbólicos en /proc/pid/fd directorio. Aquí está el contenido de este directorio de mi shell actual:

$ ls -la /proc/$$/fd
total 0
dr-x------ 2 xxx xxx  0 sept.   4 09:32 .
dr-xr-xr-x 9 xxx xxx  0 sept.   4 09:32 ..
lrwx------ 1 xxx xxx 64 sept.   4 09:32 0 -> /dev/pts/2
lrwx------ 1 xxx xxx 64 sept.   4 09:32 1 -> /dev/pts/2
lrwx------ 1 xxx xxx 64 sept.   4 09:32 2 -> /dev/pts/2
lrwx------ 1 xxx xxx 64 sept.   4 09:41 255 -> /dev/pts/2

Por lo tanto, una función de servicio que abre este directorio para el proceso actual podría usarse para cerrar solo los descriptores de archivos realmente abiertos:

#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void close_opened_fds(void)
{
  DIR *dp;
  char proc_fd_dirname[256];
  int my_fd;
  struct dirent *d;
  int fd;

  // Open /proc/<mypid>/fd directory
  snprintf(proc_fd_dirname, sizeof(proc_fd_dirname), "/proc/%d/fd", getpid());
  dp = opendir(proc_fd_dirname);
  if (!dp) {
    return;
  }

  // Get the file descriptor associated to the preceding open
  my_fd = dirfd(dp);

  while((d = readdir(dp))) {

    // Skip '.' and '..' directories
    if (d->d_name[0] == '.') {
      continue;
    }

    fd = atoi(d->d_name);

    // Close the file except if it is the fd of the opened directory
    if (fd != my_fd) {
      close(fd);
    }

  }

  closedir(dp);

}

¿Ha sido útil esta solución?