Processo de controle daemon de outro processo em C
-
Use as funções
fork
esetsid
para criar um processo Daemon -
Use a função
daemon
para criar um processo daemon
Este artigo irá apresentar vários métodos sobre como controlar o processo daemon de outro processo em C.
Use as funções fork
e setsid
para criar um processo Daemon
Os processos daemon têm várias características, como o fato de serem processos de longa execução e os daemons podem ser iniciados na inicialização do sistema. O processo daemon pode ser controlado a partir dos comandos do usuário que o forçam a encerrar, pausar ou até mesmo desabilitar na inicialização. Embora, o cenário comum implicaria em um daemon encerrando no desligamento do sistema usando alguns scripts específicos do sistema. Os daemons geralmente são executados em segundo plano, sem um terminal de controle, e os programas que empregam tais recursos devem implementar vários manipuladores de sinal para lidar com interrupções externas.
Existem vários métodos para criar um processo daemon e monitorá-lo, mas neste exemplo, demonstramos o estágio de criação, onde chamamos uma função fork
para criar um processo filho. Em seguida, o processo pai sai e o filho continua a execução à medida que se torna filho do processo init
(em sistemas Linux, o processo init
é o primeiro processo na inicialização). O processo filho chama a função setsid
para iniciar uma nova sessão e remover o processo do terminal de controle. Finalmente, chamamos fork
mais uma vez e saímos do processo pai para garantir que nosso daemon não adquira um terminal de controle. Agora estamos executando o processo daemon; é importante registrar um manipulador de sinal SIGTERM
para conduzir qualquer limpeza de recursos e saída normal quando o sistema ou o usuário entregar a interrupção correspondente.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define errExit(msg) \
do { \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
void cleanupRoutine(int signal_number) {
write(STDERR_FILENO, "hello", 5);
_exit(EXIT_SUCCESS);
}
int main(int argc, char *argv[]) {
fprintf(stderr, "[pid - %d] running...\n", getpid());
switch (fork()) {
case -1:
errExit("fork");
case 0:
break;
default:
_exit(EXIT_SUCCESS);
}
fprintf(stderr, "[pid - %d] running...\n", getpid());
if (setsid() == -1) errExit("setsid");
switch (fork()) {
case -1:
errExit("fork");
case 0:
break;
default:
_exit(EXIT_SUCCESS);
}
fprintf(stderr, "[pid - %d] running...\n", getpid());
struct sigaction sigterm_action;
memset(&sigterm_action, 0, sizeof(sigterm_action));
sigterm_action.sa_handler = &cleanupRoutine;
sigterm_action.sa_flags = 0;
// Mask other signals from interrupting SIGTERM handler
if (sigfillset(&sigterm_action.sa_mask) != 0) {
perror("sigfillset");
exit(EXIT_FAILURE);
}
// Register SIGTERM handler
if (sigaction(SIGTERM, &sigterm_action, NULL) != 0) {
perror("sigaction SIGTERM");
exit(EXIT_FAILURE);
}
while (1) {
getpid();
}
exit(EXIT_SUCCESS);
}
Use a função daemon
para criar um processo daemon
Mesmo que o exemplo anterior demonstre um código aparentemente correto, ele não possui as etapas para garantir que todos os descritores de arquivo abertos herdados do pai sejam fechados; o diretório de trabalho mudou, redirecione a entrada padrão para /dev/null
e assim por diante. Essas etapas garantem que certas funções não falharão se o daemon as invocar e também que algum comportamento estranho não seja observado.
Por exemplo, se você iniciou o programa anterior a partir da janela do terminal e, em seguida, enviar o sinal SIGTERM
para o processo daemon, a função write
do manipulador de sinal cleanupRoutine
será impressa no mesmo terminal, mesmo após o novo prompt ser exibido. Portanto, a função daemon
é fornecida pela biblioteca GNU C. Ele automaticamente cuida das etapas acima para garantir um contexto limpo para o processo daemon recém-criado. A função daemon
leva dois argumentos inteiros: o primeiro (se igual a zero) especificando se o diretório de trabalho atual deve ser alterado para o diretório raiz. O segundo inteiro (se igual a zero) indicando se os fluxos de E/S padrão devem ser redireccionados para /dev/null
.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define errExit(msg) \
do { \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
void cleanupRoutine(int signal_number) {
write(STDERR_FILENO, "hello", 5);
_exit(EXIT_SUCCESS);
}
int main(int argc, char *argv[]) {
fprintf(stderr, "[pid - %d] running...\n", getpid());
if (daemon(0, 0) == -1) errExit("daemon");
fprintf(stderr, "[pid - %d] running...\n", getpid());
struct sigaction sigterm_action;
memset(&sigterm_action, 0, sizeof(sigterm_action));
sigterm_action.sa_handler = &cleanupRoutine;
sigterm_action.sa_flags = 0;
// Mask other signals from interrupting SIGTERM handler
if (sigfillset(&sigterm_action.sa_mask) != 0) errExit("sigfillset");
// Register SIGTERM handler
if (sigaction(SIGTERM, &sigterm_action, NULL) != 0) errExit("sigaction");
while (1) {
getpid();
}
exit(EXIT_SUCCESS);
}
Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.
LinkedIn Facebook