Est-ce correct ?

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

Bonjour à tous,

Je n'ai pas de problème particulier avec le code que j'ai écrit (il compile et s'exécute correctement), mais je souhaiterais quand même avoir votre avis sur la qualité de celui-ci…

Contexte

On dispose de trois fichiers-texte A, B et C contenant respectivement les chaînes "contenu_a", "contenu_b" et "contenu_c". On passe le chemin absolu de chacun de ces fichiers à l'exécutable, grâce à la commande './exe chemin_a chemin_b chemin_c'.

Il faut que le programme affiche en parallèle sur la sortie standard leur contenu. Chaque fichier sera pris en charge par un processus distinct.

Ma solution (qui marche)

… et qui est commentée.

 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
int main(int argc, char* argv[]) {

    pid_t pid; // Contains either 0 (ie. : a process-child) or the process-ID of the last created process-child

    int i = 1; // Counts files (there are 3 files)
    for(i; i < argc; i++) {
        pid = fork(); // 3 processes are created
        if(pid == 0) { // if it's a child, pid is set to 0 and the next instruction would be a new loop without this break. So we write this break ! Thus, children won't create processes.
            break;
        }
    }

    // So now we have 4 processes : the parent + 3 children ones.

    if(pid == 0) { // If the current process is a child one, we read and print its file's content (1 child is associated with 1 file because of the variable `i`)
        FILE* file = fopen(argv[i], "r");
        if(file == NULL) {
            perror("Open");
            return -1;
        }

        int char_read = 0;
        while(char_read != EOF) {
            char_read = fgetc(file);
            printf("%c", char_read);
        }

        if(fclose(file) != 0) {
            perror("Close");
            return -2;
        }

        printf("\n\n");
    }

    if(pid != 0) { // If we are the parent process, we wait for its children ones' ending
        wait(NULL);
    }

    return 0;
}

Ma question

Donc comme je l'ai déjà dit, ce code compile, s'exécute et fait bien ce qu'on lui demande.

Cependant, avez-vous des remarques à faire ? Notamment, le break et sa condition vous paraissent-ils OK ? Est-ce que les développeurs C procéderaient bien de cette manière ou ma solution est un peu "exotique/originale" ?

Voilà voilà, merci d'avance, bonne soirée à vous. :)

Édité par The-Aloha-Protocol

Université de Bretagne-Sud <3

+0 -0

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

Globalement, ça m'a l'air plutôt correct. Le seul gros défaut que je vois est ta deuxième condition if(pid != 0). Ce que tu veux est plutôt un else. Non seulement, c'est plus clair, mais ça peut éviter des bugs particulièrement dur à comprendre si tu modifies pid dans ta première condition.

Un autre truc : quand tu affiches le contenu du fichier, tu affiches aussi EOF, ce qui n'est probablement pas ce que tu veux. Dans un cas comme ça, je te conseille d'écrire plutôt :

1
2
3
4
5
int char_read = fgetc(file);
while(char_read != EOF) {
  printf("%c", char_read);
  char_read = fgetc(file);
}

Au lieu d'utiliser printf pour afficher un unique caractère, fputc me semble plus adapté.

Une dernière remarque générale sur la manière de lire un fichier. Il est beaucoup plus efficace de lire plusieurs caractères à la fois via fgets et de l'afficher avec fputs. Cependant, cela nécessite de faire plus attention pour éviter de lire plus que ce que contient ton buffer.

Une dernière chose : dans tes commentaires, tu parles de 3 fichiers et de 4 process, sauf que ton programme peut fonctionner avec n'importe quel nombre de fichier. Pour moi, il faudrait soit vérifier que le nombre de fichier est le bon, soit éviter de donner ces nombres dans les commentaires.

+1 -0
Auteur du sujet

Merci pour tes remarques, Berdes !

Sinon j'ai une question plus générale, qui concerne un fork dans une boucle, a priori je connais déjà la réponse (d'ailleurs je suis parti de ça pour écrire le code que j'ai mis dans l'OP).

Que se passe-t-il avec le code suivant :

1
2
3
4
int i = 0;
for (i; i < 2; i++) {
 fork();
}

Si je ne me trompe pas :

  1. Le processus parent, noté A et pour lequel i = 0, crée un p-child A_B. Pour A_B, i = 0.

  2. On arrive à la fin de la boucle, donc on a i++. Ainsi pour A et A_B, i = 1.

  3. Nouvelle boucle.

3.1. Le processus A, pour lequel i = 1, crée un p-child A_C. Pour A_C, i = 1.

3.2. Le processus A_B, pour lequel i = 1, crée un p-child A_B_Z. Pour A_B_Z, i = 1.

  1. On arrive à la fin de la boucle. Ainsi :

4.1. Pour A, A_B, A_C et A_B_Z, i = 2.

EDIT : le markdown beugue pour l'avant-dernier point. J'a ibien écrit "4." et ça affiche "1.".

Édité par The-Aloha-Protocol

Université de Bretagne-Sud <3

+0 -0
Auteur du sujet

Avec un

1
2
for (int i = 0; i < N; i++)
  fork();

Tu obtiens $2^N$ processus.

(Le processus père crée un nouveau fils, puis les 2 créent chacun un nouveau fils, les 4 chacun un fils, etc.)

entwanne

Merci mais ce qui m'intéresse surtout, c'est les différentes valeurs que i prend pour chaque processus. Car le processus nouvelle créé possède la même valeur de i que son parent (par définition du fork), puis il exécutera les instructions juste après le fork (donc l'incrémentation).

Université de Bretagne-Sud <3

+0 -0

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

Je ne comprend pas bien ton problème. Désolé si je réponds à coté.

Avec cette boucle, il y a 2 processus qui se crée avec i = 0 (le processus initial et son fils).
Ensuite, pareil, 2 processus sont crée avec i = 1.
Puis ensuite, 4 processus sont crée avec i = 2.
Puis 8, …

Au final, on a bien :$ 1 + \sum_{i=0}^{n-1}2^i = 2^{n} $ processus.

Édité par ache

ache.one                                                                                   🦊

+0 -0

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

Merci mais ce qui m'intéresse surtout, c'est les différentes valeurs que i prend pour chaque processus. Car le processus nouvelle créé possède la même valeur de i que son parent (par définition du fork), puis il exécutera les instructions juste après le fork (donc l'incrémentation).

Lern-X

Sans break, tous les processus iront jusqu'à la fin de la boucle, donc i vaudra N dans chacun des processus en sortie de boucle.

Si tu veux connaître la valeur de i à la création du processus, elle sera de $log_2(\lceil p\rceil)$, avec $p$ le rang de création du processus (même si cet ordre n'est pas forcément respecté en réalité, les valeurs resteront les mêmes : $1$ pour le processus père, $2$ pour son premier fils, $3$ pour son second fils, $4$ pour le premier fils de $2$, …).

Auteur du sujet

Merci à vous deux, au final je crois que ce qui me posait problème, c'était le fait que les valeurs que prend i sont au final exactement les mêmes quel que soit le processus (père, fils, fils du fils, etc.) !

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