Pregunta Creando un daemon en Linux


En Linux, quiero agregar un daemon que no se puede detener y que monitorea los cambios en el sistema de archivos. Si se detectan cambios, debe escribir la ruta a la consola donde se inició más una nueva línea.

Ya tengo el código de cambio del sistema de archivos casi listo, pero no puedo entender cómo crear un daemon.

Mi código es de aquí http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html

¿Qué hacer después del tenedor?

int main (int argc, char **argv) {

  pid_t pID = fork();
  if (pID == 0)  {              // child
          // Code only executed by child process    
      sIdentifier = "Child Process: ";
    }
    else if (pID < 0) {
        cerr << "Failed to fork" << endl;
        exit(1);
       // Throw exception
    }
    else                                   // parent
    {
      // Code only executed by parent process

      sIdentifier = "Parent Process:";
    }       

    return 0;
}

74
2017-07-30 18:17


origen


Respuestas:


En Linux quiero agregar un daemon que no se puede detener y que monitorea los cambios en el sistema de archivos. Si se detectaran cambios, debería escribir la ruta a la consola donde se inició + una nueva línea.

Los daemons funcionan en segundo plano y (normalmente ...) no pertenecen a un TTY, por eso no puede usar stdout / stderr de la manera que probablemente quiera. Por lo general, un daemon syslog (syslogd) se usa 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 fuera del proceso principal y déjelo terminar si el bifurcación fue exitoso. -> Debido a que el proceso principal ha finalizado, el proceso hijo ahora se ejecuta en segundo plano.
  • setsid - Crea 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).
  • Señales de captura - Ignorar y / o manejar señales.
  • tenedor de nuevo y deje que el proceso principal finalice para garantizar que se deshace del proceso principal de la sesión. (Solo los líderes de la sesión pueden obtener un TTY nuevamente).
  • chdir - Cambiar el directorio de trabajo del daemon.
  • umask - Cambie la máscara de modo de archivo según las necesidades del daemon.
  • cerca - Cierre todos los descriptores de archivos abiertos que puedan heredarse del proceso principal.

Para darle un punto de partida: mire este código esqueleto que muestra los pasos básicos:

/*
 * 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
  • Comienza el daemon: ./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 debes ver aquí es:

  • El daemon no tiene terminal de control (TTY =?)
  • El ID del proceso principal (PPID) es 1 (El proceso init)
  • los 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 tomar el control de un TTY de nuevo

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]: primer daemon iniciado.
  firstdaemon [3387]: primer daemon terminado.


Una nota: En realidad, también debería implementar un manejador de señal y configurar el registro correctamente (archivos, niveles de registro ...).

Otras lecturas:


158
2017-07-30 18:57



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

Hay al menos dos tipos de daemons:

  1. tradicional SysV daemons (viejo estilo),
  2. sistemad daemons (nuevo estilo)

Daemons SysV

Si estás interesado en la tradición SysV daemon, deberías implementar el siguientes pasos:

  1. Cierre todos los descriptores de archivos abiertos, excepto el estándar entrada, saliday error (es decir, los primeros tres descriptores de archivo 0, 1, 2). Esto asegura que ningún descriptor de archivo pasado accidentalmente permanece en el proceso del daemon. En Linux, esto se implementa mejor iterando a través de /proc/self/fd, con una alternativa de iteración del descriptor de archivo 3 al valor devuelto por getrlimit() para RLIMIT_NOFILE.
  2. Reiniciar todos los controladores de señal a sus valores predeterminados. Esto se realiza mejor al iterar a través de las señales disponibles hasta el límite de _NSIG y restablecerlos a SIG_DFL.
  3. Restablezca la máscara de señal usando sigprocmask().
  4. Desinfecte el bloque de entorno, eliminando o restableciendo variables de entorno que puedan afectar negativamente el tiempo de ejecución de daemon.
  5. Llamada fork(), para crear un proceso en segundo plano.
  6. En el niño, llame setsid() para desprenderse de cualquier terminal y crear un sesión.
  7. En el niño, llame fork() nuevamente, para garantizar que el daemon nunca vuelva a adquirir un terminal nuevamente.
  8. Llamada exit() en el primer hijo, para que solo el segundo hijo (el proceso real del daemon) permanezca cerca. Esto garantiza que el proceso del daemon se vuelva a criar en init / PID 1, como deberían ser todos los daemons.
  9. En el proceso del daemon, conecta /dev/null al estándar entrada, saliday error.
  10. En el proceso del daemon, reinicie 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 del daemon, cambio el directorio actual al directorio raíz (/), para evitar que el daemon impida involuntariamente que los puntos de montaje sean desmontados.
  12. En el proceso del daemon, escribe el daemon PID (según lo devuelto por getpid()) a un archivo PID, por ejemplo /run/foobar.pid (para un daemon hipotético "foobar") para garantizar que el daemon no pueda iniciarse más de una vez. Esto debe implementarse de manera libre de competencia 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 en el extranjero.
  13. En el proceso de daemon, descarte los privilegios, si es posible y aplicable.
  14. Desde el proceso del daemon, notifique al proceso original iniciado que la inicialización se completó. Esto se puede implementar a través de una tubería sin nombre o un canal de comunicación similar que se crea antes de la primera fork() y, por lo tanto, está disponible tanto en el proceso original como en el de daemon.
  15. Llamada exit() en el proceso original. El proceso que invoca 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 utilizado, ya que implementa solo un subconjunto de estos pasos.

Un daemon que necesita proporcionar compatibilidad con los sistemas SysV debería implementar el esquema señalado anteriormente. Sin embargo, se recomienda 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 POSIX obediente.


Daemons de Nuevo Estilo

Para daemons de estilo nuevo, siguientes pasos son recomendados:

  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 principal del daemon, ya que el sistema init lo usa para detectar problemas y problemas del servicio. Se recomienda seguir el esquema de código de salida como se define en el Recomendaciones LSB para scripts de inicio SysV.
  4. Si es posible y aplicable, exponga la interfaz de control del daemon a través del D-Bus IPC sistema y tomar un nombre de bus como último paso de inicialización.
  5. Para la integración en systemd, proporcione un .Servicio  unidad archivo que contiene información sobre cómo iniciar, detener y mantener el daemon. Ver systemd.service(5) para 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 systemd's. control de límite de recursos en lugar de implementar la tuya, confía en systemd's caída de privilegios código en lugar de implementarlo en el daemon, y similar. Ver systemd.exec(5) para los controles disponibles
  7. Si D-Bus se utiliza, active su bus daemon mediante el suministro de una activación de servicio D-Bus archivo de configuración. Esto tiene múltiples ventajas: su daemon puede iniciarse de forma ocasional bajo demanda; se puede iniciar en paralelo a otros daemons que lo requieran, lo que maximiza la paralelización y velocidad de arranque; su daemon puede reiniciarse en caso de falla sin perder ninguna solicitud de bus, ya que el bus pone en cola las solicitudes de servicios activables. Ver abajo para detalles.
  8. Si su daemon proporciona servicios a otros procesos locales o clientes remotos a través de un socket, se debe hacer socket-activablesiguiendo el esquema señalado abajo. Al igual que la activación de D-Bus, esto permite el inicio de servicios a pedido y permite una paralelización mejorada del inicio del servicio. Además, para protocolos sin estado (como syslog, DNS), un daemon que implementa la activación basada en sockets puede reiniciarse sin perder una sola solicitud. Ver abajo para detalles.
  9. Si corresponde, un daemon debe notificar al sistema de inicio sobre la finalización de la puesta en marcha 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 daemon de estilo nuevo puede elegir simplemente iniciar sesión en el error estándar a través de fprintf(), que luego se reenvía a syslog por el sistema init. Si los niveles de registro son necesarios, estos pueden codificarse prefijando líneas de registro individuales con cadenas como "<4>" (para el nivel de registro 4 "ADVERTENCIA" en el esquema de prioridad syslog), siguiendo un estilo similar al núcleo de Linux printk() sistema de nivel Para más detalles, ver sd-daemon(3) y systemd.exec(5).

Para aprender más lee todo man 7 daemon.


21
2017-08-07 20:30



Puedo detenerme en el primer requisito "A daemon que no se puede detener ... "

No es posible mi amigo; sin embargo, puede lograr lo mismo con una herramienta mucho mejor, un módulo kernel.

http://www.infoq.com/articles/inotify-linux-file-system-event-monitoring

Todos los daemons pueden ser detenidos. Algunos se detienen más fácilmente que otros. Incluso un par de daemon con el compañero en espera, reapareciendo el compañero si se pierde, se puede detener. Solo tienes que trabajar un poco más duro.


5
2017-07-30 19:20



No puedes crear un proceso en Linux que no se pueda eliminar. El usuario raíz (uid = 0) puede enviar una señal a un proceso, y hay dos señales que no se pueden capturar, SIGKILL = 9, SIGSTOP = 19. Y otras señales (cuando no se detectan) también pueden dar como resultado la terminación del proceso.

Es posible que desee una función de demonización más general, donde puede especificar un nombre para su programa / daemon y una ruta para ejecutar su programa (tal vez "/" o "/ tmp"). También es posible que desee proporcionar archivos para stderr y stdout (y posiblemente una ruta de control con stdin).

Aquí están los necesarios incluye:

#include <stdio.h>    //printf(3)
#include <stdlib.h>   //exit(3)
#include <unistd.h>   //fork(3), chdir(3), sysconf(3)
#include <signal.h>   //signal(3)
#include <sys/stat.h> //umask(3)
#include <syslog.h>   //syslog(3), openlog(3), closelog(3)

Y aquí hay una función más general,

int
daemonize(char* name, char* path, char* outfile, char* errfile, char* infile )
{
    if(!path) { path="/"; }
    if(!name) { name="medaemon"; }
    if(!infile) { infile="/dev/null"; }
    if(!outfile) { outfile="/dev/null"; }
    if(!errfile) { errfile="/dev/null"; }
    //printf("%s %s %s %s\n",name,path,outfile,infile);
    pid_t child;
    //fork, detach from process group leader
    if( (child=fork())<0 ) { //failed fork
        fprintf(stderr,"error: failed fork\n");
        exit(EXIT_FAILURE);
    }
    if (child>0) { //parent
        exit(EXIT_SUCCESS);
    }
    if( setsid()<0 ) { //failed to become session leader
        fprintf(stderr,"error: failed setsid\n");
        exit(EXIT_FAILURE);
    }

    //catch/ignore signals
    signal(SIGCHLD,SIG_IGN);
    signal(SIGHUP,SIG_IGN);

    //fork second time
    if ( (child=fork())<0) { //failed fork
        fprintf(stderr,"error: failed fork\n");
        exit(EXIT_FAILURE);
    }
    if( child>0 ) { //parent
        exit(EXIT_SUCCESS);
    }

    //new file permissions
    umask(0);
    //change to path directory
    chdir(path);

    //Close all open file descriptors
    int fd;
    for( fd=sysconf(_SC_OPEN_MAX); fd>0; --fd )
    {
        close(fd);
    }

    //reopen stdin, stdout, stderr
    stdin=fopen(infile,"r");   //fd=0
    stdout=fopen(outfile,"w+");  //fd=1
    stderr=fopen(errfile,"w+");  //fd=2

    //open syslog
    openlog(name,LOG_PID,LOG_DAEMON);
    return(0);
}

Aquí hay un programa de ejemplo, que se convierte en un daemon, se cuelga y luego se va.

int
main()
{
    int res;
    int ttl=120;
    int delay=5;
    if( (res=daemonize("mydaemon","/tmp",NULL,NULL,NULL)) != 0 ) {
        fprintf(stderr,"error: daemonize failed\n");
        exit(EXIT_FAILURE);
    }
    while( ttl>0 ) {
        //daemon code here
        syslog(LOG_NOTICE,"daemon ttl %d",ttl);
        sleep(delay);
        ttl-=delay;
    }
    syslog(LOG_NOTICE,"daemon ttl expired");
    closelog();
    return(EXIT_SUCCESS);
}

Tenga en cuenta que SIG_IGN indica capturar e ignorar la señal. Puede construir un manejador de señal que pueda registrar el recibo de la señal y establecer indicadores (como un indicador para indicar un apagado correcto).


5
2018-01-28 23:36



Intenta usar el daemon función:

#include <unistd.h>

int daemon(int nochdir, int noclose);

Desde el página man:

La función daemon () es para programas que desean separarse   desde la terminal de control y se ejecuta en segundo plano como sistema   daemons.

Si nochdir es cero, daemon () cambia el actual proceso de llamada   directorio de trabajo al directorio raíz ("/"); de lo contrario, la corriente   el directorio de trabajo no se modifica.

Si noclose es cero, daemon () redirige la entrada estándar, estándar   salida y error estándar a / dev / null; de lo contrario, no hay cambios   hecho a estos descriptores de archivo.


4
2018-03-12 03:49



Si su aplicación es una de:

{
  ".sh": "bash",
  ".py": "python",
  ".rb": "ruby",
  ".coffee" : "coffee",
  ".php": "php",
  ".pl" : "perl",
  ".js" : "node"
}

y no le molesta la dependencia de NodeJS, instale NodeJS y luego:

npm install -g pm2

pm2 start yourapp.yourext --name "fred" # where .yourext is one of the above

pm2 start yourapp.yourext -i 0 --name "fred" # run your app on all cores

pm2 list

Para mantener todas las aplicaciones ejecutándose al reiniciar (y daemonise pm2):

pm2 startup

pm2 save

Ahora usted puede:

service pm2 stop|restart|start|status

(también le permite ver fácilmente los cambios de código en el directorio de su aplicación y reiniciar automáticamente el proceso de la aplicación cuando ocurre un cambio de código)


4
2018-01-29 03:17



Un daemon es solo un proceso en el fondo. Si desea iniciar su programa cuando se inicia el sistema operativo, en Linux, agregue su comando de inicio a /etc/rc.d/rc.local (ejecutar después de todos los demás scripts) o /etc/startup.sh

En Windows, realiza un servicio, registra el servicio y luego lo configura para que se inicie automáticamente en el arranque en la administración -> panel de servicios.


1
2017-07-30 18:22



Al llamar a fork (), ha creado un proceso secundario. Si la horquilla es exitosa (la horquilla devolvió un PID distinto de cero) la ejecución continuará desde este punto desde el proceso secundario. En este caso, deseamos salir del proceso padre y luego continuar nuestro trabajo en el proceso secundario.

Tal vez esto ayude: http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html


1
2017-07-30 18:32