Forum Programmation.c Implémentations de plusieurs pipes

Posté par . Licence CC by-sa
Tags :
-2
16
avr.
2016

Bonjour,

Je rencontre un problème, j'ai implémenté un shell simple qui tourne avec les pipes mais seulement pour deux commandes. J'aimerais le modifier afin qu'il réussisse à tourner avec plusieurs pipes enchainés comme "echo test | cat | cat | cat | wc" par exemple mais je manque énormément d'inspiration et je bloque depuis.
Voici le bout de mon code de la partie concernée:

        if (pid == 0) // in the child process 
        {  
           for (i = 0; i < command; i++)  // for each cmd
           {
             if (mot[i][0] == '|') 
             {
               j = i; 
             }
           }
           if (j > 0) 
           {
             if (pipe(p)) 
             {
               fprintf(stderr, "pipe");
               exit(1);
             }
             mot[j] = NULL;
             if (fork() == 0)  //enfant
             { 
                j = -1;
                close(p[0]);
                dup2(p[1],1);
                close(p[1]);
             } 

             close(p[1]);  //parent
             dup2(p[0], 0);
             close(p[0]);                              
           }
           for (i = 0; dirs[i] != 0; i++) 
           {
             snprintf(pathname, sizeof(pathname), "%s/%s", dirs[i], mot[j+1]);
             execv(pathname, &mot[j+1]);
           }
         }
        else 
        {  
           while (wait(0) != pid) // parent: wait child
        }

Merci d'avance pour l'aide !

  • # Question ?

    Posté par . Évalué à 1.

    Bonjour,

    quelle est la question précisément ?

    Tu dis que ça tourne avec deux commandes qu'est-ce qui fait que ça ne marche pas avec plus ?

    Pour te donner quelques pistes, je doute que ton code marche :

    • man fork ;
    • on ne sait pas trop ce que sont mot[] et dirs[], mais qu'est-ce que la 2e boucle for est censée faire ?
    • [^] # Re: Question ?

      Posté par . Évalué à 0.

      Bonjour kp,

      Merci pour ton retour. Bien sur que mon code tourne, par exemple pour "ls | echo foo" le résultat est foo et ainsi de suite. Le programme je l'ai testé donc je ne peux pas me permettre d'inventer ;)
      Alors ce que je cherche concrètement comme je l'ai dit dans mon premier post c'est d'adapter le programme pour plusieurs pipes comme par exemple "echo foo | cat | cat | cat | wc".
      mot[] comme son nom l'indique est le tableau qui représente chaque mot entré en ligne de commande et dirs c'est pour le répertoire afin d'exécuter chaque commande. Voilà en gros pour les précisions.

      • [^] # Re: Question ?

        Posté par . Évalué à 2.

        Salut,

        Difficile sans description de problème exact ou de code complet, mais j'ai l'impression que ton premier for va chercher le dernier pipe. D'où le problème ?

        Il faudrait peut-être un break dès le premier pipe trouvé afin de forker et continuer d'avancer.

        • [^] # Re: Question ?

          Posté par . Évalué à -1.

          Salut kaos
          Alors je pense que jusque là je ne me suis pas encore fait comprendre.
          Ce bout de code que j'ai posté n'a aucun problème il fonctionne parfaitement donc le problème ne se trouve pas dans ce code.
          D'autre part, ce bout de code fonctionne pour deux commandes seulement donc par exemple: "echo foo | cat ".
          Par ailleurs, le but de mon post c'est chercher de l'aide pour pouvoir adapter ce bout de code fonctionnel afin d'exécuter non pas 2 commandes seulement mais plutot plusieurs.
          Par exemple: "echo test | cat | cat" qui ne marche pas avec le code publié.

          • [^] # Re: Question ?

            Posté par . Évalué à 0.

            On ne sait pas ce que ton bout de code doit faire, donc c'est compliqué de t'aider.

            Si je reprends ton exemple pour essayer de débrouiller ce que tu peux faire:

            "echo foo | cat | cat"

            Si je comprends bien, tu as ton process qui va faire "echo foo" et passer la sortie à un enfant qui va faire "cat | cat" (et du coup, cet enfant devrait va faire cat et passer la sortie à un autre enfant qui va faire cat).
            Ce que je veux dire, c'est que tu dois pouvoir faire en sorte que l’enchaînement soit transparent (l'enfant va traiter le pipe exactement comme le parent l'aurait fait, tu dois pouvoir te passer de code spécifique pour ça).

            • [^] # Re: Question ?

              Posté par . Évalué à 0.

              Voilà c'est ce que je cherche à faire justement en partant du code posté mais j'ai du mal justement pour faire cet enchaînement d'enfant. C'est cela le but de mon post.
              Tu pourrais me suggérer quelque chose ?

          • [^] # Re: Question ?

            Posté par . Évalué à 3.

            Salut,

            Alors je ne comprend pas.

            Ce bout de code que j'ai posté n'a aucun problème il fonctionne parfaitement donc le problème ne se trouve pas dans ce code.

            Comment veux-tu qu'on t'aide si tu ne poste pas ce qui ne va pas ?

            Par ailleurs, le but de mon post c'est chercher de l'aide pour pouvoir adapter ce bout de code fonctionnel afin d'exécuter non pas 2 commandes seulement mais plutot plusieurs

            As-tu bien lu ma remarque sur le fait que la variable j correspond au dernier pipe ?

      • [^] # Re: Question ?

        Posté par . Évalué à 1.

        Je ne doute pas que les cas que tu as testés te donnent l'impression que ça marche.

        Pour expliciter un peu plus mes remarques :
        Le bout de code suivant est exécuté par le parent et par l'enfant (après le fork), ça n'a pas l'air voulu.

           close(p[1]);  //parent
             dup2(p[0], 0);
             close(p[0]);

        Ensuite :

        dirs c'est pour le répertoire afin d'exécuter chaque commande.

        Hum, quel répertoire ? Il y en a plusieurs ? et du coup, ça exécute plusieurs fois la commande ?? Quelle commande d'ailleurs ?

        • [^] # Re: Question ?

          Posté par . Évalué à 3. Dernière modification le 17/04/16 à 17:53.

          Amha dirs c'est pour rechercher dans PATH…

          Sinon ça parait normal que son code ne fonctionne pas.
          En gros, il faut créer un pipe pour chaque | (logique…), puis faire un tracking correcte des programmes lancés pour le(s) waitpid(s) (amha, l'utilisation d'une liste pour cela serait naturelle…).
          Deusio, comme je lui avais déjà expliqué dans une précédente question, il fait son fork beaucoup trop tôt… amah le fork doit venir au niveau de l'exec et la logique (== le calcul de comment on va faire, pas l'exécution) des pipes et des fd doit venir au dessus et créer une fonction helper fork+setup des fds+exec qui retourne un élément à mettre dans la listes des processus lancés. En gros, découper la phase d'analyse de la ligne de(s) commande(s) de la phase d'exécution(s).

          • [^] # Re: Question ?

            Posté par . Évalué à 2.

            Bien vu aussi pour le "bug", bien que ça n'aie aucune influence… (ben ouais, il ne vérifie pas les retours des close()…)

          • [^] # Re: Question ?

            Posté par . Évalué à 1.

            Comme fk l'a compris, je cherche à faire justement l'enchaînement d'enfants à partir du code posté pour gérer plusieurs commandes comme dans "echo foo | cat | cat | cat | wc". Tu aurais une suggestion ?

            • [^] # Re: Question ?

              Posté par . Évalué à 1.

              je cherche à faire justement l'enchaînement d'enfants

              Ma suggestion serait de ne pas vouloir faire de l'enchaînement d'enfant dans le sens ou l'enfant s'occupe de lancer lui-même même la commande liée, car du coups ton enfant est obligé d'attendre lui-même le pid, ce qui t'impose de faire un double fork (et ainsi de suite pour chaque commande enchaînée), ça va devenir vite compliqué pour pouvoir gèrer correctement les redirections (i.e. dans cmd1|cmd2|cmd3, le stdout de cmd3 c'est celui du shell).

              En pseudo-code ça donnerait:

              tant que commande à lancer (1)
              avancer le scanner
              doit être pipée en sortie ?
              doit être pipiée en entrée (== précédent doit être pipé en sortie) ?
              (2)
              forker, pipe/duper les fds et/ou redirection (< ou > ?), exec
              ajouter pid à la liste d'attente
              fini
              tant que liste d'attente est non vide
              waitpid()
              pid est dans la liste d'attente, le retirer

              Je te suggèrais aussi de faire cela en deux temps, c'est-à-dire à la place du (2): créer une liste d'exécution ou chaque élément serait le tuple (in redir_spec, out redir_spec, command_argv) où redir_spec := pipe | file_redirection, ensuite tu parcours une fois la liste pour créer les pipes et/ou ouvrir les fichiers de redirections, et une seconde fois pour lancer les commandes (ou les deux d'un coups mais bon j'aime pas trop, c'est mon côté programmation fonctionnelle).

            • [^] # Re: Question ?

              Posté par . Évalué à 3.

              je cherche à faire justement l'enchaînement d'enfants

              Sale esclavagiste !!!

        • [^] # Re: Question ?

          Posté par . Évalué à 1.

          En fait dirs c'est pour rechercher dans PATH comme benja a su le dire…

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.