2010-06-20 12 views
3

J'ai préparé un programme qui émule l'interface shell (cmd) en utilisant des tuyaux.Il existe deux versions du programme: 1. En utilisant un tuyau (en utilisant un tuyau de parent à communication enfant) 2. En utilisant un double tuyau (en utilisant deux tuyaux de parent à enfant et d'enfant à parent pour communiquer)C: Je suis coincé avec dup2() :-(

Ainsi, le premier programme fournit l'interface désirée et fonctionne comme je veux, mais je ne peux pas atteindre le même résultat (interface) dans le deuxième programme (en utilisant dup2() et similaire)

Donc, je relaie sur votre aide et mettre les deux codes ci-dessous:

BS: Vous pouvez compiler et essayer les deux programmes avec la même manière l'utilisation de ces commandes:

$ gcc -o prog1.c prog1

Suivant exécutons:

./prog1 $

Suivant exécutons nouveau terminal et essayer d'écrire des données à iNPUT.TXT:

$ echo pwd> input.txt

Puis regardez le résultat dans le premier terminal.

(Ce beau travail pour le premier programme mais je dois obtenir ce esprit de travail de la même interface dans le deuxième programme)

CODE DU PREMIER PROGRAMME (FIN DE TRAVAIL):

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <sys/stat.h> 

void do_child(int data_pipe[]) { 
    int c; 
    int rc; 
    close(data_pipe[1]); 

    dup2(data_pipe[0], 0); /* This string provides the desired interface of the program */ 

    char* cmd[] = { "bash", (char *)0 }; 
    execvp("bash", cmd); 

    while ((rc = read(data_pipe[0], &c, 1)) > 0) 
    { 
     putchar(c); 
    } 
    exit(0); 
} 

void do_parent(int data_pipe[]) 
{ 
    int c; 
    int rc; 
    FILE *in; 

    close(data_pipe[0]); 

    while (1) 
    { 
     in = fopen("input.txt", "r"); 
     while ((c = fgetc(in)) > 0) 
     { 
      rc = write(data_pipe[1], &c, 1); 
      if (rc == -1) 
      { 
       perror("Parent: write"); 
       close(data_pipe[1]); 
       exit(1); 
      } 
     } 
     fclose(in); 
    } 
    close(data_pipe[1]); 
    exit(0); 
} 

int main(int argc, char* argv[]) 
{ 
    int data_pipe[2]; 
    int pid; 
    int rc; 

    umask(0); 
    mknod("input.txt", S_IFIFO|0666, 0); 

    rc = pipe(data_pipe); 
    if (rc == -1) 
    { 
     perror("pipe"); 
     exit(1); 
    } 
    pid = fork(); 
    switch (pid) 
    { 
    case -1: 
     perror("fork"); 
     exit(1); 
    case 0: 
     do_child(data_pipe); 
    default: 
     do_parent(data_pipe); 
    } 
    return 0; 
} 

CODE DU PROGRAMME DEUXIÈME (BESOIN À corriger UN PEU):

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <sys/stat.h> 

/* Original version got from http://www.iakovlev.org */ 

int parent_to_child[2]; 
int child_to_parent[2]; 

void do_parent() 
{ 
    int c; 
    char ch; 
    int rc; 
    FILE *in; 

    close(child_to_parent[1]); /* we don't need to write to this pipe. */ 
    close(parent_to_child[0]); /* we don't need to read from this pipe. */ 

    while (1) 
    { 
     in = fopen("input.txt", "r"); 
     while ((c = fgetc(in)) > 0) { 
      ch = (char)c; 
      /* write to child */ 
      rc = write(parent_to_child[1], &ch, 1); 
      if (rc == -1) { 
       perror("child: write"); 
       close(child_to_parent[0]); 
       close(parent_to_child[1]); 
       exit(1); 
      } 
      /* read back from child */ 
      rc = read(child_to_parent[0], &ch, 1); 
      c = (int)ch; 
      if (rc <= 0) { 
       perror("parent: read"); 
       close(child_to_parent[0]); 
       close(parent_to_child[1]); 
       exit(1); 
      } 
      putchar(c); 
     } 
     fclose(in); 
    } 
    close(child_to_parent[0]); 
    close(parent_to_child[1]); 
    exit(0); 
} 

void do_child() 
{ 
    int c; 
    char ch; 
    int rc; 

    close(parent_to_child[1]); /* we don't need to write to this pipe. */ 
    close(child_to_parent[0]); /* we don't need to read from this pipe. */ 

    //dup2(parent_to_child[0], STDIN_FILENO); 
    //dup2(child_to_parent[1], STDOUT_FILENO); 

    /* Some dup2() routines must be added here 
    to get this working as the first program above */ 

    char* cmd[] = { "bash", (char *)0 }; 
    execvp("bash", cmd); 

    while (read(parent_to_child[0], &ch, 1) > 0) { 
     c = (int)ch; 
     ch = (char)c; 
     putchar(ch); 
     rc = write(child_to_parent[1], &ch, 1); 
     if (rc == -1) { 
      perror("child: write"); 
      close(parent_to_child[0]); 
      close(child_to_parent[1]); 
      exit(1); 
     } 
    } 
    close(parent_to_child[0]); 
    close(child_to_parent[1]); 
    exit(0); 
} 

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

    umask(0); 
    mknod("input.txt", S_IFIFO|0666, 0);  

    rc = pipe(parent_to_child); 
    if (rc == -1) { 
     perror("main: pipe parent_to_child"); 
     exit(1); 
    } 

    rc = pipe(child_to_parent); 
    if (rc == -1) { 
     perror("main: pipe child_to_parent"); 
     exit(1); 
    } 

    pid = fork(); 
    switch (pid) { 
    case -1: 
     perror("main: fork"); 
     exit(1); 
    case 0: 
     do_child(); 
    default: 
     do_parent(); 
    } 
    return 0; 
} 
+0

Je me demande pourquoi intitulant comme "ANSI C" spécifiquement. Il n'y a pas de fonction comme 'dup2' dans la bibliothèque ANSI C standard. – AnT

Répondre

0

La principale différence est ici:

while ((c = fgetc(in)) > 0) { 
     ch = (char)c; 
     /* write to child */ 
     rc = write(parent_to_child[1], &ch, 1); 
     /* .... */ 
     /* read back from child */ 
     rc = read(child_to_parent[0], &ch, 1); 
     /* .... */ 
     putchar(c); 
    } 

Comme je suis paresseux pour compiler/test pour vous, je pourrais dire simplement que le parent est bloqué dans la lecture(). Parce que l'autre côté (bash dans le processus enfant) n'est pas garanti pour renvoyer tous les caractères écrits en arrière. Ou il peut même décider d'imprimer plus d'un caractère que votre code est incapable de gérer.

Dans le cas où vous devez interroger() pour voir s'il y a quelque chose à lire ou non. Ou définissez l'indicateur O_NONBLOCK sur child_to_parent [0] avec fcntl (F_SETFL) et lorsque errno == EAGAIN, ignorez simplement la branche read(). Et boucle alors qu'il reste des caractères à lire.

Édition1. BTW J'ai totalement raté la partie: vous dans do_parent() loop devez utiliser poll() sur les deux child_to_parent[0] et in, car l'autre côté pourrait écrire quelque chose (read() ne serait pas bloquer) même si vous n'écrivez pas() n'importe quel personnage.

0

Grâce à vous, il me semble que ça marche.

Donc, voici le code mis à jour de do_parent:

void do_parent() 
{ 
    int c; 
    char ch; 
    int rc; 
    FILE *in; 

    struct pollfd fds[2]; 
    int pol_ret; 

    fds[0].fd = child_to_parent[0]; 

    close(child_to_parent[1]); /* we don't need to write to this pipe. */ 
    close(parent_to_child[0]); /* we don't need to read from this pipe. */ 

    while (1) 
    { 
     in = fopen("input.txt", "r"); 
     fds[1].fd = fileno(in); 
     pol_ret = poll(fds, 2, 500); 

     while ((c = fgetc(in)) > 0) { 
      ch = (char)c; 
      /* write to child */ 
      rc = write(parent_to_child[1], &ch, 1); 
      if (rc == -1) { 
       perror("child: write"); 
       close(child_to_parent[0]); 
       close(parent_to_child[1]); 
       exit(1); 
      } 
      /* read back from child */ 
      if (fds[0].revents & POLLIN) 
      { 
       rc = read(child_to_parent[0], &ch, 1); 
       c = (int)ch; 
       if (rc <= 0) { 
        perror("parent: read"); 
        close(child_to_parent[0]); 
        close(parent_to_child[1]); 
        exit(1); 
       } 
       putchar(c); 
      } 
     } 
     fclose(in); 
    } 
    close(child_to_parent[0]); 
    close(parent_to_child[1]); 
    exit(0); 
} 

J'ai aussi ajouté ceci dans do_child():

dup2(parent_to_child[0], STDIN_FILENO); 
+0

Malheureusement, le problème est en partie résolu. Cette manière ne fonctionne pas dans Windows (compilation via Cygwin). Si cygwin1.dll existe dans le même répertoire que le programme, le résultat est le même que précédemment. Dans le shell Cygwin, tout fonctionne correctement. – 0xDEFACE