Je joue avec les signaux : SIGSTOP
et SIGCONT
en particulier. Voici un programme de test que j'ai écrit. L'idée est de créer une chaîne de N + 1 processus (y compris le processus principal). Chacun doit attendre que son enfant s'arrête, puis s'arrêter lui-même. Le processus principal doit réveiller son enfant lorsque ce dernier s'est arrêté.
Pour cela, la fonction f
crée de manière récursive la chaîne de processus. Chacun des processus utilise sigsuspend sur le signal SIGCHLD
sauf le dernier enfant qui s'arrête directement. Lorsque son enfant s'est arrêté, un processus recevra le signal SIGCHLD
, puis il peut s'arrêter à son tour. Quand le processus principal reçoit le signal SIGCHLD
, cela signifie que tous les processus sont dans l'état d'arrêt, donc il envoie le signal SIGCONT
à son enfant. Chaque processus envoie SIGCONT
à son propre enfant puis quitte, sauf le dernier enfant qui se contente de quitter.
J'ai essayé d'être clair : j'ai supprimé les tests de code de retour et écrit quelques commentaires.
Lors de l'exécution du programme, tout semble OK sauf la chaîne de SIGCONT
. Certains processus se réveillent mais pas tous. En regardant les programmes en cours d'exécution (avec ps par exemple) tout semble normal : aucun processus bloqué. Je ne comprends vraiment pas ce qui pourrait ne pas fonctionner dans ce programme. Toute aide ou indice serait la bienvenue.
Voici une trace d'exemple. Comme vous pouvez le voir, la "chaîne de fork" s'est bien déroulée, où les processus sont en attente sur SIGCHLD
. Ensuite, le dernier enfant apparaît et s'arrête. Ce qui crée une "chaîne de SIGCHLD
" sur les parents car chaque processus s'arrête. Lorsque le processus principal est notifié d'un SIGCHLD
, il envoie SIGCONT
à son enfant, qui se réveille et envoie à son tour SIGCONT
à son propre enfant, etc. Vous pouvez remarquer que cette chaîne n'est pas complète :
$ ./bin/trycont
n pid log
0 6257 "en attente sur SIGCHLD"
1 6258 "en attente sur SIGCHLD"
2 6259 "en attente sur SIGCHLD"
3 6260 "en attente sur SIGCHLD"
4 6261 "en attente sur SIGCHLD"
5 6262 "dernier enfant - arrêt"
4 6261 "a reçu SIGCHLD"
4 6261 "arrêt"
3 6260 "a reçu SIGCHLD"
3 6260 "arrêt"
2 6259 "a reçu SIGCHLD"
2 6259 "arrêt"
1 6258 "a reçu SIGCHLD"
1 6258 "arrêt"
0 6257 "a reçu SIGCHLD"
0 6257 "enverra SIGCONT à 6258"
1 6258 "réveillé - envoi de SIGCONT à 6259"
2 6259 "réveillé - envoi de SIGCONT à 6260"
# <- pas la trace attendue
Voici le programme : src/trycont.c
#include
#include
#include
#include
/* nombre de processus créés avec fork
*/
#define N 5
#define printHeader() printf("n\tpid\tlog\n");
#define printMsg(i, p, str, ...) printf("%d\t%d\t" #str "\n", i, p, ##__VA_ARGS__)
void f(int n);
void handler(int sig);
sigset_t set;
struct sigaction action;
int main(int argc, char *argv[])
{
/* masquer SIGCHLD
*/
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_SETMASK, &set, NULL);
/* le gestionnaire sera appelé lorsque SIGCHLD sera envoyé au processus
* pendant le gestionnaire, SIGCHLD sera masqué (sa_mask)
*/
action.sa_mask = set;
action.sa_handler = handler;
action.sa_flags = 0;
/* SIGCHLD déclenchera l'action
*/
sigaction(SIGCHLD, &action, NULL);
/* démarrage
*/
printHeader();
f(N);
exit(EXIT_SUCCESS);
}
void f(int n)
{
pid_t p, pc;
int myIndex;
myIndex = N - n;
p = getpid();
if (n == 0)
{
/* dernier enfant
*/
printMsg(myIndex, p, "dernier enfant - arrêt");
kill(p, SIGSTOP);
printMsg(myIndex, p, "FIN ATTEINTE");
exit(EXIT_SUCCESS);
}
pc = fork();
if (pc == 0)
{
/* récursion
*/
f(n - 1);
/* jamais atteint
* à cause de l'exit
*/
}
/* père
*/
/* en attente sur SIGCHLD
* besoin de démasquer le signal
* et de suspendre
*/
printMsg(myIndex, p, "en attente sur SIGCHLD");
sigfillset(&set);
sigdelset(&set, SIGCHLD);
sigsuspend(&set);
printMsg(myIndex, p, "a reçu SIGCHLD");
if (n < N)
{
/* processus enfant
* mais pas le dernier
*/
printMsg(myIndex, p, "arrêt");
kill(p, SIGSTOP);
printMsg(myIndex, p, "réveillé - envoi de SIGCONT à %d", pc);
kill(pc, SIGCONT);
}
else
{
/* processus racine
*/
printMsg(myIndex, p, "envoi de SIGCONT à %d", pc);
kill(pc, SIGCONT);
}
exit(EXIT_SUCCESS);
}
void handler(int sig)
{
switch (sig)
{
case SIGCHLD:
/* lorsque le processus reçoit SIGCHLD
* nous pouvons ignorer les prochains SIGCHLD
*/
action.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &action, NULL);
break;
default:
break;
}
}
Voici un Makefile si vous en avez besoin :
CC=gcc
DEFINES=-D_POSIX_C_SOURCE
STD=-std=c11 -Wall -Werror
OPTS=-O2
CFLAGS=$(STD) $(DEFINES) $(OPTS) -g
LDFLAGS=
SRC=src
OBJ=obj
BIN=bin
DIRS=$(BIN) $(OBJ)
.PHONY: mkdirs clean distclean
all: mkdirs $(BIN)/trycont
$(BIN)/%: $(OBJ)/%.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
$(OBJ)/%.o: $(SRC)/%.c
$(CC) $(CFLAGS) -c -o $@ $<
mkdirs:
- mkdir $(DIRS)
clean:
rm -vf -- $(OBJ)/*.o
distclean: clean
rm -vfr -- $(DIRS)