"Gluer" une machine : redémarrage système nécessaire

Création de processus jusqu'à saturation

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Salut,

Dans le cadre d'un TP, je dois écrire un code correspondant aux consignes suivantes :

"Le programme doit créer un ensemble de processus de calculs jusqu'à remplir la table des processus. La machine sera "gluée" lorsque l'appel à fork échouera (le système ne pouvant plus créer de nouveau processus). Le programme doit afficher le nombre de processus créés, attend une saisie-clavier et détruit tous les processus de calcul. Les processus de calcul doivent appartenir à un même groupe de processus pour faciliter leur destruction."

Ma solution

Pour saturer la table des processus, j'ai créé une boucle while(fork() != -1). Puis dans chaque processus-enfant, qui effectue comme calcul l'incrémentation d'une variable (i), je boucle à l'infini et je ne fait aucun exit ou return (je me situe dans le main) : ainsi aucun processus-enfant ne meurt de lui-même et ça permet de saturer la table des processus du système.

Le processus-père, quant à lui, incrémente une variable number_of_children_processes à chaque appel à fork. Quand le système ne peut plus créer de processus-fils (fork() == -1), on laisse l'utilisateur-final taper "ENTREE". Si c'est le cas, on termine tous les processus dont le groupe est le même que celui du parent.

Problèmes

Clairement je vois deux soucis :

  1. Le fait que les processus-enfants bouclent à l'infini me force à redémarrer le système (Linux sous VirtualBox sous Windows) et ce n'est pas normal…

  2. Toujours à cause de ces boucles infinies, le scanf du processus-père serait noyé par d'éventuels affichages des processus-fils (bon ce n'est pas demandé dans mon TP mais ça pourrait être un souci s'il devait y en avoir).

Voilà du coup je crois que ma solution est pour le moins bancale, puisque je dois redémarrer mon KUbuntu chaque fois que j'exécute mon programme… Je m'y suis mal pris et je voulais savoir si quelqu'un savait comment faire ?

Merci d'avance !

Source

 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
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(int argc, char* argv[]) {
    int pid = 0;

    int number_of_created_child_processes = 0;
    while((pid = fork()) != -1) {
        if(pid != 0) {
            number_of_created_child_processes++;
        } else {
            int i = 0;
            while(1) {
                i++;
            }
        }
    }

    // NOW it's impossible to create new processes children
    perror("Fork");
    char cmd = 0;
    scanf("%c", &cmd);
    if(cmd == '\n') {
        kill(0, SIGKILL);
    }

    while(wait(NULL) > 0) {;;}
    exit(EXIT_SUCCESS);
}

Édité par The-Aloha-Protocol

Université de Bretagne-Sud <3

+0 -0

Cette réponse a aidé l'auteur du sujet

bonjours,

je ne suis pas sur que ce soit la meilleur manière de faire, seulement celle qui me vient tout de suite à l'esprit mais a défaut de mieux tu pourrais utiliser un sleep() sur tes processus pour les empêcher de se terminer sans consommer trop de ressource ?

Édité par pantadora

+0 -0
Auteur du sujet

bonjours,

je ne suis pas sur que ce soit la meilleur manière de faire, seulement celle qui me vient tout de suite à l'esprit mais a défaut de mieux tu pourrais utiliser un sleep() sur tes processus pour les empêcher de se terminer sans consommer trop de ressource ?

pantadora

Heum, et je mettrais quoi comme valeur en paramètre ? Un nombre de secondes assez grand ?

Lu'!

Penser au primitives de synchronisations. Notamment aux condition-variables.

Ksass`Peuk

Je ne comprends pas ?

Université de Bretagne-Sud <3

+0 -0

Lu'!

Penser au primitives de synchronisations. Notamment aux condition-variables.

Ksass`Peuk

Je ne comprends pas ?

Lern-X

Peut être qu'une petite recherche pourrait t'amener vers des pages où l'on explique comment utiliser des primitives de synchronisation entre processus. Où le principe serait par exemple de se mettre en attente d'un signal de fin. Comme par exemple en utilisant des condition variables.

Édité par Ksass`Peuk

First : Always RTFM - "Tout devrait être rendu aussi simple que possible, mais pas plus." A.Einstein

+0 -0
Auteur du sujet

Lu'!

Penser au primitives de synchronisations. Notamment aux condition-variables.

Ksass`Peuk

Je ne comprends pas ?

Lern-X

Peut être qu'une petite recherche pourrait t'amener vers des pages où l'on explique comment utiliser des primitives de synchronisation entre processus. Où le principe serait par exemple de se mettre en attente d'un signal de fin. Comme par exemple en utilisant des condition variables.

Ksass`Peuk

Tu parles de wait non ?

Université de Bretagne-Sud <3

+0 -0

Cette réponse a aidé l'auteur du sujet

À lire ton code, je me pose quelques questions accessoires :

  • Quelle est l'utilité de ta variable i définie ligne 14 ?
  • Pourquoi {;;} ligne 29 ?

Et je rejoins les commentaires précédents, la boucle infinie dans les processus fils est une mauvaise idée, puisqu'elle demande à ce que le processus soit constamment actif. sleep, pause, ou une variable conditionnelle seraient bien plus adaptées.

Auteur du sujet

La variable i est juste utilisée pour effectuer des calculs (car les processus à créer sont dits "de calculs").

Concernant {;;} c'est une habitude que j'ai prise à l'IUT, un prof de Java mettait ça pour signifier l'absence d'instructions dans le code d'une méthode.

Ok je vais essayer avec sleep, je vous tiens au courant, merci !

Edit : on n'a pas vu la notion de variable conditionnelle. Du coup a priori il n'y a pas besoin d'en utiliser (sinon le TP serait plus ou moins "infaisable").

Édité par The-Aloha-Protocol

Université de Bretagne-Sud <3

+0 -0
Auteur du sujet

Alors, en effet avec sleep, plus besoin de rebooter ! C'est déjà ça. De plus j'obtiens bien, au bout d'un certain temps, le message d'erreur : "Fork : ressource temporarily unavailable".

Par contre, il semblerait qu'il y ait un souci du côté du scanf : que je tape ou non '\n', toute instruction en-dessous de cet appel est ignorée et le processus-parent se termine avec le code de sortie 9.

Voici le nouveau source en question :

 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
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(int argc, char* argv[]) {
    int pid = 0;

    int number_of_created_child_processes = 0;
    while((pid = fork()) != -1) {
        if(pid != 0) {
            number_of_created_child_processes++;
        } else {
            int i = 0;
            while(i < 10000) {
                i++;
            }
            sleep(600);
            exit(EXIT_FAILURE);
        }
    }

    // NOW it's impossible to create new processes children
    perror("Fork");
    char cmd = 0;

    if(scanf("%c", &cmd) < 0) {
        perror("Scanf");
        exit(EXIT_FAILURE);
    }

    if(cmd == '\n') {
        if(kill(0, SIGKILL) == -1) {
            perror("Kill");
            exit(EXIT_FAILURE);
        } else {
            fprintf(stdout, "Tous les processus créés ont bien été détruits.");
        }
    }

    while(wait(NULL) > 0) {;;}
    exit(EXIT_SUCCESS);
}

Université de Bretagne-Sud <3

+0 -0
Auteur du sujet

Alors maintenant mon programme compile, s'exécute correctement et semble bien faire ce qui est demandé dans le TP, merci !

J'ai donc utilisé sleep , récupéré le group-ID du processus-parent et j'ai affecté {ce dernier + 1} au group-ID de chaque processus-enfant.

Ainsi chaque processus-enfant a le même group-ID, mais ce dernier est différent du group-ID du processus-parent (grâce au +1).

J'ai donc pu utiliser kill(-(group_ID+1), SIGKILL) pour tuer tous les processus-enfants tout en laissant en vie le processus-parent, comme demandé dans le TP.

Est-ce que vous trouvez cette solution élégante/convenable ? ^^

 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
49
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(int argc, char* argv[]) {
    int pid = 0;
    pid_t parent_gpid = getpgid(getpid());

    int number_of_created_child_processes = 0;
    while((pid = fork()) != -1) {
        if(setpgid(pid, parent_gpid+1) == -1) {
            perror("ProcessGroupID");
            exit(EXIT_FAILURE);
        }

        if(pid != 0) {
            number_of_created_child_processes++;
        } else {
            int i = 0;
            while(i < 10000) {
                i++;
            }
            sleep(600);
            exit(EXIT_FAILURE);
        }
    }

    // NOW it's impossible to create new processes children
    perror("Fork");
    char cmd = 0;

    if(scanf("%c", &cmd) < 0) {
        perror("Scanf");
        exit(EXIT_FAILURE);
    }

    if(cmd == '\n') {
        if(kill(-(parent_gpid+1), SIGKILL) == -1) {
            perror("Kill");
            exit(EXIT_FAILURE);
        } else {
            fprintf(stdout, "Tous les processus créés ont bien été détruits.");
        }
    }

    while(wait(NULL) > 0) {;;}
    exit(EXIT_SUCCESS);
}

Édité par The-Aloha-Protocol

Université de Bretagne-Sud <3

+0 -0

Est-ce que vous trouvez cette solution élégante/convenable ? ^^

Lern-X

Pas trop, non. La boucle while dans les processus fils n'a aucun intérêt en l'état : elle est quasi-instantanée. Le but était de mettre le sleep à l'intérieur du while, pour être sûr de boucler jusqu'à la fin (et non mettre une grande valeur comme argument du sleep pour cela…). Ainsi que supprimer le compteur i qui est inutile.

Édité par entwanne

Auteur du sujet

Du coup je mettrais quoi comme calculs, si je dois supprimer le compteur ?

Pour le sleep, j'avoue ne pas avoir compris. Je pensais qu'il était nécessaire, à la place de la boucle infinie, pour être sûr que le processus-enfant ne se tue pas trop vite. "Trop" = avant que la table des processus ne soit saturée.

Université de Bretagne-Sud <3

+0 -0
Staff

Sinon, comme l'a suggéré entwanne, il y a fonction pause() qui rempli parfaitement le boulot ici.

Édit : note que tu peux également stopper l'exécution de chaque fils en leur faisant chacun envoyer un signal SIGTSTP à eux-même.

1
kill(0, SIGTSTP);

Édité par Taurre

+0 -0
Auteur du sujet

sleep n'est pas suffisant comme calcul ? L'intérêt est juste que le fils soit en vie, non ? Ton sleep actuel est en dehors de la boucle et sa valeur est arbitraire, tu n'as donc aucune garantie que ça se passe correctement.

entwanne

Je pense que le prof, par "calculs", entend "opérations mathématiques" non ? En quoi un sleep serait un "calcul" sinon ?

Ok sa valeur est arbitraire, ça je comprends bien que ça puisse poser problème. Par contre le fait que ce sleep soit en-dehors del a boucle while du i là je ne comprends pas du tout en quoi ce serait gênant ?

Sinon, comme l'a suggéré entwanne, il y a fonction pause() qui rempli parfaitement le boulot ici.

Édit : note que tu peux également stopper l'exécution de chaque fils en leur faisant chacun envoyer un signal SIGTSTP à eux-même.

1
kill(0, SIGTSTP);

Taurre

Hmmm, je ne suis pas sûr de vouloir modifier la façon dont je tue les fils (le prof parle bien de "groupes" dans son TP). Et concernant pause, je ne l'ai pas croisée dans le reste du TP (par contre sleep, oui). Du coup je ne sais pas si je dois vraiment utiliser pause.

Université de Bretagne-Sud <3

+0 -0

Mais ton calcul actuel n'a aucun intérêt, il est quasi immédiat. Par « processus de calcul », j'ai plutôt pensé à un processus qui devait rester en vie très longtemps, pour simuler un calcul, mais tu peux ajouter des opérations dans ta boucle si ça te fait plaisir.

Le sleep en dehors de la boucle indique que le programme se terminera au bout de 10 minutes. Mais qu'est-ce qui t'indique que le programme père aura alors fini d'instancier l'ensemble des processus fils ?

Avec le sleep déplacé dans une boucle infini, tu serais sûr que ton programme ne serait jamais arrêté avant de recevoir le signal qui le demande.

Auteur du sujet

Mais ton calcul actuel n'a aucun intérêt, il est quasi immédiat. Par « processus de calcul », j'ai plutôt pensé à un processus qui devait rester en vie très longtemps, pour simuler un calcul, mais tu peux ajouter des opérations dans ta boucle si ça te fait plaisir.

Le sleep en dehors de la boucle indique que le programme se terminera au bout de 10 minutes. Mais qu'est-ce qui t'indique que le programme père aura alors fini d'instancier l'ensemble des processus fils ?

Avec le sleep déplacé dans une boucle infini, tu serais sûr que ton programme ne serait jamais arrêté avant de recevoir le signal qui le demande.

entwanne

Heum oui c'est bien ça, il faut que le processus-fils dure longtemps, et au final en effet je pense qu'on peut juste virer la boucle de calculs.

Sinon c'est pas bête en effet. Du coup je peux très bien mettre un sleep(1) non ? Jusqu'à présent je mettais une grande valeur justement pour m'assurer à peu près que tous les processus-fils étaient créés (= pour saturer la table).

Université de Bretagne-Sud <3

+0 -0
Staff

Hmmm, je ne suis pas sûr de vouloir modifier la façon dont je tue les fils (le prof parle bien de "groupes" dans son TP). Et concernant pause, je ne l'ai pas croisée dans le reste du TP (par contre sleep, oui). Du coup je ne sais pas si je dois vraiment utiliser pause.

Lern-X

Il ne s'agit pas de tuer autrement les processus fils, mais de faire en sorte de suspendre leur exécution en attendant que le père ait créé suffisamment de processus fils. Cela donnerait en fait ceci :

1
2
3
4
5
while ((pid = fork()) != -1)
    if (pid != 0)
        number_of_created_child_processes++;
    else
        kill(getpid(), SIGTSTP); /* arrêt du fils par lui-même */

Édit : utilisation de getpid().

Édité par Taurre

+0 -0
Auteur du sujet

Heumm oui je pense que ce serait pas mal d'utiliser SIGSTP. Après je ne pense pas que le prof y ait pensé puisqu'il parle de "groupes de processus". Mais bon c'est sûr que c'est plus élégant.

Université de Bretagne-Sud <3

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

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