setpgid
Esempio minimo del gruppo di processi POSIX C.
Potrebbe essere più semplice da comprendere con un esempio eseguibile minimo dell'API sottostante.
Questo illustra come il segnale viene inviato al bambino, se il bambino non ha cambiato il suo gruppo di processi con setpgid
.
main.c
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
volatile sig_atomic_t is_child = 0;
void signal_handler(int sig) {
char parent_str[] = "sigint parent\n";
char child_str[] = "sigint child\n";
signal(sig, signal_handler);
if (sig == SIGINT) {
if (is_child) {
write(STDOUT_FILENO, child_str, sizeof(child_str) - 1);
} else {
write(STDOUT_FILENO, parent_str, sizeof(parent_str) - 1);
}
}
}
int main(int argc, char **argv) {
pid_t pid, pgid;
(void)argv;
signal(SIGINT, signal_handler);
signal(SIGUSR1, signal_handler);
pid = fork();
assert(pid != -1);
if (pid == 0) {
is_child = 1;
if (argc > 1) {
/* Change the pgid.
* The new one is guaranteed to be different than the previous, which was equal to the parent's,
* because `man setpgid` says:
* > the child has its own unique process ID, and this PID does not match
* > the ID of any existing process group (setpgid(2)) or session.
*/
setpgid(0, 0);
}
printf("child pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)getpgid(0));
assert(kill(getppid(), SIGUSR1) == 0);
while (1);
exit(EXIT_SUCCESS);
}
/* Wait until the child sends a SIGUSR1. */
pause();
pgid = getpgid(0);
printf("parent pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)pgid);
/* man kill explains that negative first argument means to send a signal to a process group. */
kill(-pgid, SIGINT);
while (1);
}
GitHub a monte .
Compilare con:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -Wpedantic -o setpgid setpgid.c
Corri senza setpgid
Senza alcun argomento CLI, setpgid
non viene eseguito:
./setpgid
Possibile risultato:
child pid, pgid = 28250, 28249
parent pid, pgid = 28249, 28249
sigint parent
sigint child
e il programma si blocca.
Come possiamo vedere, il pgid di entrambi i processi è lo stesso, in quanto viene ereditato fork
.
Quindi ogni volta che colpisci:
Ctrl + C
Emette di nuovo:
sigint parent
sigint child
Questo mostra come:
- per inviare un segnale a un intero gruppo di processi con
kill(-pgid, SIGINT)
- Ctrl + C sul terminale invia un kill a tutto il gruppo di processo per impostazione predefinita
Chiudere il programma inviando un segnale diverso a entrambi i processi, ad esempio SIGQUIT con Ctrl + \
.
Corri con setpgid
Se corri con un argomento, ad esempio:
./setpgid 1
quindi il bambino cambia il suo pgid, e ora solo un singolo sigillo viene stampato ogni volta solo dal genitore:
child pid, pgid = 16470, 16470
parent pid, pgid = 16469, 16469
sigint parent
E ora, ogni volta che colpisci:
Ctrl + C
solo il genitore riceve anche il segnale:
sigint parent
Puoi ancora uccidere il genitore come prima con un SIGQUIT:
Ctrl + \
tuttavia il bambino ora ha un PGID diverso e non riceve quel segnale! Questo può essere visto da:
ps aux | grep setpgid
Dovrai ucciderlo esplicitamente con:
kill -9 16470
Questo chiarisce il motivo per cui esistono gruppi di segnali: altrimenti avremmo lasciato un mucchio di processi da pulire manualmente tutto il tempo.
Testato su Ubuntu 18.04.