setpgid
Ejemplo mínimo del grupo de procesos POSIX C
Puede ser más fácil de entender con un ejemplo ejecutable mínimo de la API subyacente.
Esto ilustra cómo se envía la señal al niño, si el niño no cambió su grupo de proceso setpgid
.
C Principal
#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 aguas arriba .
Compilar con:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -Wpedantic -o setpgid setpgid.c
Correr sin setpgid
Sin ningún argumento CLI, setpgid
no se hace:
./setpgid
Posible resultado:
child pid, pgid = 28250, 28249
parent pid, pgid = 28249, 28249
sigint parent
sigint child
y el programa se cuelga.
Como podemos ver, el pgid de ambos procesos es el mismo, ya que se hereda fork
.
Luego, cada vez que golpeas:
Ctrl + C
Sale de nuevo:
sigint parent
sigint child
Esto muestra cómo:
- enviar una señal a todo un grupo de procesos con
kill(-pgid, SIGINT)
- Ctrl + C en el terminal envía un kill a todo el grupo de procesos por defecto
Salga del programa enviando una señal diferente a ambos procesos, por ejemplo, SIGQUIT con Ctrl + \
.
Corre con setpgid
Si corre con un argumento, por ejemplo:
./setpgid 1
luego el niño cambia su pgid, y ahora solo se imprime una sola señal cada vez solo del padre:
child pid, pgid = 16470, 16470
parent pid, pgid = 16469, 16469
sigint parent
Y ahora, cada vez que golpeas:
Ctrl + C
solo el padre recibe la señal también:
sigint parent
Todavía puedes matar al padre como antes con un SIGQUIT:
Ctrl + \
¡Sin embargo, el niño ahora tiene un PGID diferente y no recibe esa señal! Esto se puede ver en:
ps aux | grep setpgid
Tendrás que matarlo explícitamente con:
kill -9 16470
Esto deja en claro por qué existen grupos de señales: de lo contrario, nos quedarían un montón de procesos que se limpiarían manualmente todo el tiempo.
Probado en Ubuntu 18.04.