Controllo del processo daemon da un altro processo in C
-
Usa le funzioni
fork
esetsid
per creare un processo daemon -
Usa la funzione
daemon
per creare un processo daemon
Questo articolo introdurrà più metodi su come controllare il processo daemon da un altro processo in C.
Usa le funzioni fork
e setsid
per creare un processo daemon
I processi daemon hanno diverse caratteristiche, come ad esempio che sono processi a esecuzione prolungata e che i daemon possono essere avviati all’avvio del sistema. Il processo del demone può essere controllato dai comandi dell’utente che lo costringono a terminare o mettere in pausa o addirittura disabilitare all’avvio. Tuttavia, lo scenario comune comporterebbe la terminazione di un demone all’arresto del sistema utilizzando alcuni script specifici del sistema. I demoni sono generalmente in esecuzione in background senza un terminale di controllo, ei programmi che impiegano tali caratteristiche dovrebbero implementare vari gestori di segnali per gestire gli interrupt esterni.
Esistono diversi metodi per creare un processo daemon e monitorarlo, ma in questo esempio, mostriamo la fase di creazione, dove chiamiamo una funzione fork
per creare un processo figlio. Quindi, il processo genitore esce e il figlio continua l’esecuzione mentre diventa figlio del processo init
(sui sistemi Linux, il processo init
è il primo processo all’avvio). Il processo figlio chiama la funzione setsid
per avviare una nuova sessione e rimuovere il processo dal terminale di controllo. Infine, chiamiamo fork
ancora una volta ed usciamo dal processo genitore per assicurarci che il nostro demone non acquisisca un terminale di controllo. Ora stiamo eseguendo il processo daemon; è importante registrare un gestore di segnali SIGTERM
per eseguire qualsiasi ripulitura delle risorse e un’uscita graduale quando il sistema o l’utente fornisce l’interrupt corrispondente.
#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);
}
Usa la funzione daemon
per creare un processo daemon
Anche se l’esempio precedente dimostra un codice apparentemente corretto, mancano i passaggi per garantire che tutti i descrittori di file aperti ereditati dal genitore siano chiusi; la directory di lavoro è cambiata, reindirizza lo standard input a /dev/null
e così via. Questi passaggi garantiscono che alcune funzioni non falliranno se il demone le invoca, e anche alcuni comportamenti strani non vengono osservati.
Ad esempio, se hai avviato il programma precedente dalla finestra del terminale e poi invii il segnale SIGTERM
al processo daemon, la funzione write
dal gestore del segnale cleanupRoutine
stamperà sullo stesso terminale anche dopo che il nuovo prompt è stato visualizzato. Pertanto, la funzione daemon
è fornita dalla libreria GNU C. Si occupa automaticamente dei passaggi precedenti per garantire un contesto pulito per il processo daemon appena creato. La funzione demone
accetta due argomenti interi: il primo (se uguale a zero) che specifica se la directory di lavoro corrente deve essere modificata nella directory root. Il secondo numero intero (se uguale a zero) che indica se i flussi di I/O standard devono essere reindirizzati a /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