IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

Probleme avec le file descriptor Language C fork, exec, read


Sujet :

C

  1. #1
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Mars 2018
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2018
    Messages : 5
    Points : 3
    Points
    3
    Par défaut Probleme avec le file descriptor Language C fork, exec, read
    Bonjour tout le monde ,

    je suis debutante en C , et j'ai eu un devoir qui consiste creer deux processus père et fils...

    j'ai écrit un programme (que nous appellerons père) qui :

    -demande à l’utilisateur une commande (avec options et paramètres éventuels)

    -crée un processus fils

    -lui transmet la commande lue (+ options et paramètres bien sur).

    Le fils exécute la commande reçue et transmet les résultats à son père.

    Le père affiche à l’écran les résultats reçus.

    Quand je compile ce code, ça ne me renvoie aucune erreur... Cependant je le lance par exemple avec cette commande: ./unix ls -l /home
    (avec unix le nom du fichier compilé biensur ).

    ça ne fonctionne pas et ça me renvoie plutôt comme erreur :

    ls: write error: bad file descriptor



    Et ça ne je ne sais pourquoi...

    pourriez vous m'aider? merci d'avance



    Le code est le suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    #include<stdio.h>
     
    #include<stdlib.h>
     
    #include<unistd.h>
     
    #include<string.h>
     
    #include<sys/types.h>
     
    #include<sys/wait.h>
     
    #include<fcntl.h>
     
     
     
    int main(int argc, char *argv[]) {
     
        /* Père */
     
        char* myargv[argc];
     
        // argv du exec
     
        char* argfils[argc];
     
        int fp[2]; // file descriptor
     
        int rep = pipe(fp);
     
    // Test du pipe
     
    if(rep == -1){
     
      printf("%s\n", "Il y a une erreur dans la création du
      pipe" );
     
      exit(-1);
     
    }
     
    /* Fils */
     
    int f = fork();
     
    if(f == -1){
     
      printf("%s\n", "Erreur du fork");
     
      exit(-1);
     
     }
     
    if(f == 0){
     
    int i;
     
    for(i = 1;i < argc; i++){
     
       myargv[i - 1] = argv[i];
     
    }
     
    myargv[argc - 1] = NULL;
     
    printf("%s\n", argv[1]);
     
    printf("%s et %s et %s et %s\n", myargv[0],myargv[1],myargv[2],myargv[3]);
     
    dup2(fp[1],1); // redirection
     
            execvp(argv[1], myargv);
     
    }
     
    else{
     
    // ferme le out du pere
     
        char lu[1024];
     
       read(fp[1], lu, 1024); // le nombre de byte qui ont été lus
     
               printf("%s\n", lu);
     
      wait(NULL);
     
        // on fait attendre le processus parent
     
    }
     
    return EXIT_SUCCESS;
     
    }

  2. #2
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Tu tentes de lire depuis l'entrée du pipe, fp[1].

    Accessoirement, il me semble qu'il faudrait fermer les deux descripteurs à l'aide de close dans le code du fils après l'appel à dup2.

  3. #3
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 721
    Points : 31 044
    Points
    31 044
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par manou1998 Voir le message
    Bonjour tout le monde ,

    je suis debutante en C , et j'ai eu un devoir qui consiste creer deux processus père et fils...
    Bonjour
    C'est déjà un TP qui dépasse le domaine du débutant...

    Citation Envoyé par manou1998 Voir le message
    ça ne fonctionne pas et ça me renvoie plutôt comme erreur :

    ls: write error: bad file descriptor

    Et ça ne je ne sais pourquoi...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
       read(fp[1], lu, 1024); // le nombre de byte qui ont été lus
     
               printf("%s\n", lu);
    Me semble que dans un tube, on écrit sur le coté [1] et on lit sur le coté [0] (un peu comme avec les processus qui écrivent dans le flux 1 et lisent le flux 0). Toi, tu lis le coté [1]...!!!???

    Autre détails: le tube ayant été ouvert des deux cotés, mais chaque processus père/fils ne travaillant que sur un des deux cotés, alors chaque processus doit fermer le coté qu'il n'utilise pas et aussi, une fois qu'il a fini, fermer le coté qu'il a utilisé.

    Sinon je ne vois pas à quoi servent les tableaux "argfils" (inutilisé) et "myargv" (qui ne fait que récupérer "argv"). A mon avis, quand on a déjà la valeur dans une variable ça ne sert pas à grand chose de la recopier dans une seconde (surtout si on ne touche pas à la première)...

  4. #4
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Mars 2018
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2018
    Messages : 5
    Points : 3
    Points
    3
    Par défaut
    ok merci à vous! donc le probleme était au niveau de la fermeture des pipes et aussi du read: j'ai les modifié et ça marche maintenant bien!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
     
    	if(f == 0){
     
      	int i;
     
      	for(i = 1;i < argc; i++){
     
      		myargv[i - 1] = argv[i];
     
      	}
     
      	myargv[argc - 1] = NULL;
     
        //close(1);
      	printf("%s\n", argv[1]);
     
      	printf("%s et %s et %s et %s\n", myargv[0],myargv[1],myargv[2],myargv[3]);
     
      	dup2(fp[1],1); // redirection
        close(fp[0]);
        close(fp[1]);
      	execvp(argv[1], myargv);
     
        //close(1);
     
    	}
     
    	else{
    		close(fp[1]);
    		// ferme le out du pere
     
        char lu[10240];
     
    		int count = read(fp[0], lu, 10240); // le nombre de byte qui ont été lus
     
        lu[count] = '\0';
    		//int w = write(fp[1], lu, 255); // ecriture dans stdout
        printf("%s\n", lu);
     
    		wait(NULL);
    	    // on fait attendre le processus parent
     
    	}

  5. #5
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 721
    Points : 31 044
    Points
    31 044
    Billets dans le blog
    1
    Par défaut
    La fermeture manquante n'empêchait pas le programme de fonctionner. Mais bien évidemment il faut la mettre (quand on sort de sa maison, on éteint la lumière).
    Le read du coté [1] était lui le vrai problème.
    Mais moi j'ai une autre définition de "ça marche bien". Parce que tronquer le résultat d'une commande aux 1024 premiers octets qu'elle renvoie c'est assez moyen comme fonctionnement.
    De plus, quand on remplit l'élément [x] d'un tableau, ça impose au tableau d'avoir la place pour au-moins x+1 éléments (hé oui, un tableau commence à 0 !!!). Or toi tu lis 1024 (au plus) caractères que tu stockes dans un tableau de 1024 caractères et tu vas ensuite y rajouter un '\0'. Il se rajoute où ce '\0' ???
    Et franchement t'as vraiment une indentation de chiotte. Ecrire un code C ce n'est pas seulement écrire un code qui fonctionne mais c'est aussi écrire un code qu'on peut lire/relire/modifier facilement. Et ça, ça passe par un code correctement indenté.

  6. #6
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Mars 2018
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2018
    Messages : 5
    Points : 3
    Points
    3
    Par défaut
    message passée! merci pour la précision.

  7. #7
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 721
    Points : 31 044
    Points
    31 044
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par manou1998 Voir le message
    message passée!
    Pas vraiment. Tu as changé "1024" en "10240" dans ton code précédent mais ça ne change rien au fond du problème. Une commande peut très bien renvoyer 20000, 50000, 100000, 1000000000, 10000000000000000000 d'octets. Fatalement il y aura un moment où tu ne pourras pas stocker tout ce qu'elle renvoie.
    Imagine par exemple un script shell de ce type
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #!/bin/bash
    while true; do
        ls -Rals /
    done
    Et que je demande à ton programme de l'exécuter ???

    Comment on lit un flux d'octets ? De la même façon qu'on lit un fichier: on fait une boucle !!!

  8. #8
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Mars 2018
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2018
    Messages : 5
    Points : 3
    Points
    3
    Par défaut
    Ah oui, je vois un peu... Donc si j'ai bien compris, c'est ainsi que je devrais mieux éviter ce genre de cas:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
     
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/wait.h>
     
    int main(int argc, char *argv[]) {
     
        system("clear");
        /* Père */
        // argv du exec
        char* myargv[argc];
     
        int fp[2]; // file descriptor
        int rep = pipe(fp);
     
        // Test du pipe
    	if(rep == -1){
    		printf("%s\n", "Il y a une erreur dans la création du pipe" );
    		exit(-1);
    	}
     
    	int f = fork();
    	if(f == -1){
            printf("%s\n", "Erreur du fork");
            exit(-1);
        }
     
    	/* Fils */
    	if(f == 0){
            int i;
     
            for(i = 1;i < argc; i++){
                myargv[i - 1] = argv[i];
            }
     
            myargv[argc - 1] = NULL;
            printf("%s\n", argv[1]);
            printf("%s et %s et %s et %s\n", myargv[0],myargv[1],myargv[2],myargv[3]);
            dup2(fp[1],1); // redirection
            close(fp[0]);
            close(fp[1]);
            execvp(argv[1], myargv);
    	}
     
    	else{
    		close(fp[1]);
            char lu[1024];
            int count ;
     
            while (count = read(fp[0], lu, 1023)) { // le nombre de byte qui ont été lus
                 lu[count] = '\0';
                 printf("%s\n", lu);
            }
     
    	wait(NULL);
    	 // on fait attendre le processus parent
    	}
    	return EXIT_SUCCESS;
    }

  9. #9
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 721
    Points : 31 044
    Points
    31 044
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par manou1998 Voir le message
    Ah oui, je vois un peu... Donc si j'ai bien compris, c'est ainsi que je devrais mieux éviter ce genre de cas:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    while (count = read(fp[0], lu, 1023)) { // le nombre de byte qui ont été lus
                 lu[count] = '\0';
                 printf("%s\n", lu);
            }
    A deux détails près
    • tu ne mets pas de '\n' au printf(). Tu es sensé afficher ce que la commande renvoie sans fioritures en plus
    • tu n'es même pas obligé de convertir ce que tu reçois en chaine si tu passes par une autre commande que printf()


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    	while (count = read(fp[0], lu, 1024) {
    		write(STDOUT_FILENO, lu, count);
    	}
    De plus je ne sais pas ce qu'impliquera le close(fp[1]) avant l'appel du execvp (ok il a été dupliqué mais est-ce que c'est correct de le fermer avant ?) et tu as oublié le close(fp[0]) dans le père...

  10. #10
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Mars 2018
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2018
    Messages : 5
    Points : 3
    Points
    3
    Par défaut
    Ok merci pour cette precision, ça je n'avais pas envisagé.

  11. #11
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 721
    Points : 31 044
    Points
    31 044
    Billets dans le blog
    1
    Par défaut
    Ton code un peu plus corrigé, un peu plus aéré, un peu plus tout quoi...

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    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 <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/wait.h>
     
    int main(int argc, char *argv[]) {
     
    	system("clear");
     
    	int fp[2]; // file descriptor
     
    	// Création du pipe
    	if (pipe(fp) == -1){
    		fprintf(stderr, "Il y a une erreur dans la création du pipe - %s\n", strerror(errno));
    		exit(-1);
    	}
     
    	switch (fork()) {
    		case -1: // Erreur
    			fprintf(stderr, "Erreur du fork - %s\n", strerror(errno));
    			close(fp[0]);
    			close(fp[1]);
    			exit(-1);
    		case 0: // Fils
    			close(fp[0]);
    			printf("%s\n", argv[1]);
    			dup2(fp[1], STDOUT_FILENO); // redirection
    			close(fp[1]);
    			execvp(argv[1], &argv[1]);
    			exit(0);
    		default:
    			close(fp[1]);
    			char lu[1024];
    			int count ;
     
    			while ((count = read(fp[0], lu, 1024)) > 0) {
    				write(STDOUT_FILENO, lu, count);		// Ou bien fwrite(lu, sizeof(*lu), count, stdout) => c'est toi qui voit...
    			}
    			if (count < 0)
    				fprintf(stderr, "Erreur dans le read - %s\n", strerror(errno));
     
    			close(fp[0]);
    			wait(NULL);
    	}
    	return EXIT_SUCCESS;
    }

    J'en ai un peu rajouté avec errno.h et strerror() pour afficher les messages systèmes quand il y a une erreur mais bon, autant que tu apprennes de suite à avoir les bons réflexes. De même que pour le wait(NULL) que tu as mis inutilement (le père attendant des infos du fils se terminera fatalement après le fils) mais que j'ai laissé car d'une part c'est une double assurance (comme argc et argv[argc] qui peuvent chacun d'eux servir de test quand on traite les arguments) et d'autre part, si le fils évolue dans le futur et rajoute des actions, il peut alors devenir plus lent que le père et ça évite alors de devoir y penser...

  12. #12
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    C'est déjà un TP qui dépasse le domaine du débutant...
    Vu au milieu des années 2000 : premier TD de C qui enchaîne direct avec un TP de prog système.. pour 80% du groupe qui n'avait jamais vu de pointeur.

    Citation Envoyé par Sve@r Voir le message
    je ne sais pas ce qu'impliquera le close(fp[1]) avant l'appel du execvp (ok il a été dupliqué mais est-ce que c'est correct de le fermer avant ?)
    Il faut tout nettoyer avant l'exec. Bien qu'il en hérite, le nouveau processus n'aura pas l'usage des descripteurs a priori, ce n'est donc peut-être pas obligatoire pour obtenir le comportement escompté mais c'est plus propre. Et comme tu le dis fp[1] a été dup, c'est donc ok.

  13. #13
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 721
    Points : 31 044
    Points
    31 044
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Matt_Houston Voir le message
    Il faut tout nettoyer avant l'exec.
    Ouais t'as raison. Le exec remplace le procesus par un autre, il faut donc tout nettoyer avant qu'il disparaisse. J'aurais dû y penser

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. cleaning process fils avec waitpid() fork exec pselect
    Par thorgal99 dans le forum Réseau
    Réponses: 2
    Dernier message: 19/08/2014, 11h59
  2. exercices languages C
    Par marwane.boughanmi dans le forum Débuter
    Réponses: 2
    Dernier message: 17/01/2011, 23h29
  3. fork() exec et cie..
    Par lylo01 dans le forum Linux
    Réponses: 4
    Dernier message: 04/12/2009, 12h44
  4. fork + exec + mémoire partagée ?
    Par italiasky dans le forum POSIX
    Réponses: 2
    Dernier message: 26/08/2009, 16h18

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo