CTRL+C non-attrapé

a marqué ce sujet comme résolu.

Salut à tous,

J'essaie de faire une fork bomb que je puisse arrêter avec un CTRL+C dans le terminal. Ainsi :

  1. Si je lance mon programme, le processus courant crée un processus qui en crée un autre et ainsi de suite, jusqu'à ce que la table des processus soit totalement remplie et que l'OS soit incapable d'en créer de nouveaux (c'est la définition d'une fork bomb "normale") ;

  2. Mais je dois pouvoir à tout moment faire un CTRL+C dans le terminal. Ce CTRL+C tuera tous les processus et arrêtera aussi (du coup !) la création de nouveaux processus (petite fonctionnalité supplémentaire pour ma fork bomb, par rapport à une "normale").

Plus de précisions

Je veux détailler le point n°1. En effet, mon programme n'agit pas exactement comme c'est décrit.

En réalité :

  1. Le tout premier processus, celui de mon programme (ie. : le principal) crée son seul et unique enfant direct.

  2. Ce dernier créera ses propres enfants, jusqu'à saturation de la liste des processus.

  3. Ces derniers feront des calculs, une pause, mais tourneront sans s'arrêter (pour permettre la saturation).

Les enfants de l'enfant direct du processus principal, ainsi que cet enfant direct, appartiendront à un même groupe de façon à simplifier le fait de les tuer avec CTRL+C.

Mon problème

Petit souci avec le code que j'ai écrit : le CTRL+C ne semble pas être attrapé/géré !

Du coup impossible d'arrêter mon programme, et je dois redémarrer ma machine virtuelle…

Est-ce que vous pourriez y jeter un coup d'oeil svp ? Merci ! :o

Mon code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/resource.h>

int main(int argc, char* argv[]) {
    pid_t pid_first_child = 0;
    if((pid_first_child = fork()) == -1) { // We `fork` the first child, which will always `fork` (more precisely : until the OS is glued, processes table completely filled)
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if(pid_first_child == 0) { // BEGINNING OF <FirstChild>'S CODE

        pid_t pid_session_leader = 0;
        if((pid_session_leader = setsid()) == -1) { // FirstChild is its process group's leader
            perror("setsid");
            exit(EXIT_FAILURE);
        }

        if(setpriority(PRIO_PGRP, pid_session_leader, -10) == -1) { // The priority of FirstChild (which is the group's leader)
            perror("setpriority");
            exit(EXIT_FAILURE);
        }

        unsigned children_counter = 0;
        pid_t pid_calculation_process = 0;
        while((pid_calculation_process = fork()) != -1) { // Now, FirstChild will `fork` until the limit ! When the limit is reached, -1 is returned : there isn't anymore `fork` and we exit the loop
            if(pid_calculation_process > 0) {
                children_counter++;
                fprintf(stdout, "%u\n", children_counter);
            } else { // BEGINNING OF <FirstChild's children>'s CODE (Why ? Consequently to the `while` and the `if` !)
                float j=1;
                while(1) { // Children can't die
                    int i = 0;
                    for(; i < 1000; i++) {
                        j /= 3;
                    }

                    usleep(1000);
                }
            } // END OF <FirstChild's children>'s CODE (FirstChild's children)
        }
        perror("fork"); // It's what we wanted ! This message will tell the user "OS is glued, program worked correctly"
        exit(EXIT_SUCCESS); // `EXIT_SUCCESS` ? Because we reached the limit !

    } // END OF <FirstChild>'S CODE
}
+0 -0

Salut,

Si ma mémoire est bonne, il me semble que le signal SIGINT n'est envoyé qu'au groupe actif du terminal. Or, en recourant à la fonction setsid(), tu déconnectes le premier processus fils du terminal ainsi que, du même coup, du groupe actif du terminal. Dès lors, puisqu'il n'a plus de rapport avec le terminal courant, il n'en reçoit pas les signaux.

+0 -0

Oui ce doit être pour ça effectivement. Mais du coup je me retrouve un peu bloqué. Y a pas moyen pour le processus principal de récupérer le pgid de son enfant direct (et ainsi des enfants de ce dernier), et de tuer tout ce groupe ?

Il faudrait pour ce faire que je déplace le setsid, peut-être.

Edit : le setsid donne au pgid la même valeur que le pid du processus appelant. Du coup si j'envoie un signal de mort au pid de l'enfant du processus principal, ça équivaudra strictement à l'envoyer à tout le groupe ! Et donc mon programme fonctionnera enfin !

+0 -0

Edit : le setsid donne au pgid la même valeur que le pid du processus appelant. Du coup si j'envoie un signal de mort au pid de l'enfant du processus principal, ça équivaudra strictement à l'envoyer à tout le groupe ! Et donc mon programme fonctionnera enfin !

Lern-X

Je pense que ca doit marcher.
Le bon vocabulaire associe est "foreground process group" (le groupe qui recoit les signaux de type ^C). Un "process group leader" verifie GID == PID, et apres setsid() le processus devient lui-meme un leader de son groupe. La valeur retournee par setsid() est le nouvel identifiant de session. Un leader de session verifie SID == PID, donc en particulier apres setsid(), on aura ici SID == GID. L'idee est donc d'envoyer un signal depuis un processus de premier plan vers le groupe ainsi cree.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#define _XOPEN_SOURCE 500
#include <assert.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>

enum { NB_PROCESSES = 5 }; 

static pid_t child0_pid;
void father_handler(int signum) { killpg(child0_pid, SIGINT); }

int main(void)
{
  if ((child0_pid = fork()) == 0) {
    pid_t new_sid = setsid();
    assert(new_sid == getpid());
    for (int i = 0; i < NB_PROCESSES; ++i)
      if (fork() == 0) sleep(1000);
  } else {
    struct sigaction act;
    act.sa_handler = father_handler;
    sigaction(SIGINT, &act, NULL);
    sleep(1000);
  }  
  return 0; 
}
1
2
3
4
5
$ gcc -std=c99 a.c && ./a.out
^C
$ ps aux | grep "./a.out"
md5       5945  0.0  0.0  11116  2164 pts/0    S+   18:07   0:00 grep --colour=auto ./a.out
$ 
+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte