如何在C中管道stdin到子节点和execl



在下面的代码中,我只是试图通过stdin将文件发送给将执行cat OS命令的子进程。代码可以很好地编译。下面是我如何从命令行调用它:

$ ./uniquify < words.txt

然而,当我运行它时,我得到一个segfault错误。我真的很难理解信息是如何通过管道传递给孩子们的。我试图使代码尽可能简单,所以我可以理解它,但它还没有意义。如有任何帮助,不胜感激。

#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NUM_CHILDREN 2
int main(int argc, char *argv[])
{          
  pid_t catPid;                        
  int   writeFds[NUM_CHILDREN];        
  int   catFds[2];                      
  int   c = 0;
  FILE  *writeToChildren[NUM_CHILDREN]; 
  //create a pipe
  (void) pipe(catFds);
  if ((catPid = fork()) < 0) {
    perror("cat fork failed");
    exit(1);
  }
  //this is the child case
  if (catPid == 0) {
    //close the write end of the pipe
    close(catFds[1]);
    //close stdin?
    close(0);
    //duplicate the read side of the pipe
    dup(catFds[0]);
    //exec cat
    execl("/bin/cat", "cat", (char *) 0);
    perror("***** exec of cat failed");
    exit(20);
  }  
  else { //this is the parent case
    //close the read end of the pipe
    close(catFds[0]);
    int p[2];
    //create a pipe
    pipe(p);
    writeToChildren[c] = fdopen(p[1], "w");
  } //only the the parent continues from here
  //close file descriptor so the cat child can exit
  close(catFds[1]);
  char words[NUM_CHILDREN][50];
  //read through the input file two words at a time
  while (fscanf(stdin, "%s %s", words[0], words[1]) != EOF) {
    //loop twice passing one of the words to each rev child
    for (c = 0; c < NUM_CHILDREN; c++) {
      fprintf(writeToChildren[c], "%sn", words[c]);
    }    
  }
  //close all FILEs and fds by sending and EOF
  for (c = 0; c < NUM_CHILDREN; c++) {
    fclose(writeToChildren[c]);
    close(writeFds[c]);
  }
  int status = 0;
  //wait on all children
  for (c = 0; c < (NUM_CHILDREN + 1); c++) {
    wait(&status);
  }
  return 0;
}

既然你的问题似乎是关于理解管道和叉子是如何工作的,我希望下面的程序可以帮助你。请注意,这只是为了说明。它不符合商业执行的条件,但我想保持简短!

你可以这样编译这两个程序:

cc pipechild.c -o pipechild
cc pipeparent.c -o pipeparent

Then execute with ./pipeparent

<<p> pipeparent.c来源/strong>
/* pipeparent.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define MESSAGE     "HELLO!n"
#define INBUFSIZE   80
#define RD      0   // Read end of pipe
#define WR      1   // Write end of pipe
int main(void)
{
    int ptocpipe[2];    // Parent-to-child pipe
    int ctoppipe[2];    // Chile-to-parent pipe
    pid_t   childpid;   // Process ID of child
    char    inbuf[80];  // Input from child
    int rd;     // read() return
    int rdup;       // dup():ed stdin for child
    int wdup;       // dup():ed stdout for child
    char    *eol;       // End of line
    // Create pipe for writing to child
    if (pipe(ptocpipe) < 0) {
        fprintf(stderr, "pipe(ptocpipe) failed!n");
        return 2;
    }
    // Create pipe for writing back to parent
    if (pipe(ctoppipe) < 0) {
        fprintf(stderr, "pipe(ctoppipe) failed!n");
        return 2;
    }
    // Verify that one of the pipes are working by filling it first
    // in one end and then reading it from the other. The OS will
    // buffer the contents for us. Note, this is not at all necessary,
    // it's just to illustrate how it works!
    write(ptocpipe[WR], MESSAGE, strlen(MESSAGE));
    read(ptocpipe[RD], inbuf, INBUFSIZE);
    if (strlen(inbuf) != strlen(MESSAGE)) {
        fprintf(stderr, "Failed to flush the toilet!n");
        return 6;
    } else {
        printf("Wrote to myself: %s", inbuf);
    }
    // Next, we want to launch some interactive program which
    // replies with exactly one line to each line we send to it,
    // until it gets tired and returns EOF to us.
    // First, we must clone ourselves by using fork(). Then the
    // child process must be replaced by the interactive program.
    // Problem is: How do we cheat the program to read its stdin
    // from us, and send its stdout back to us?
    switch (childpid = fork()) {
        case -1:    // Error
        fprintf(stderr, "Parent: fork() failed!n");
        return 3;
        case 0:     // Child process
        // Close the ends we don't need. If not, we might
        // write back to ourselves!
        close(ptocpipe[WR]);
        close(ctoppipe[RD]);
        // Close stdin
        close(0);
        // Create a "new stdin", which WILL be 0 (zero)
        if ((rdup = dup(ptocpipe[RD])) < 0) {
            fprintf(stderr, "Failed dup(stdin)n");
            return 4;
        }
        // Close stdout
        close(1);
        // Create a "new stdout", which WILL be 1 (one)
        if ((wdup = dup(ctoppipe[WR])) < 0) {
            fprintf(stderr, "Failed dup(stdout)n");
            return 5;
        }
        // For debugging, verify stdin and stdout
        fprintf(stderr, "rdup: %d, wdup %dn", rdup, wdup);
        // Overload current process by the interactive
        // child process which we want to execute.
        execlp("./pipechild", "pipechild", (char *) NULL);
        // Getting here means we failed to launch the child
        fprintf(stderr, "Parent: execl() failed!n");
        return 4;
    }
    // This code is executed by the parent only!
    // Close the ends we don't need, to avoid writing back to ourself
    close(ptocpipe[RD]);
    close(ctoppipe[WR]);
    // Write one line to the child and expect a reply, or EOF.
    do {
        write(ptocpipe[WR], MESSAGE, strlen(MESSAGE));
        if ((rd = read(ctoppipe[RD], inbuf, INBUFSIZE)) > 0) {
            // Chop off ending EOL
            if ((eol = rindex(inbuf, 'n')) != NULL)
                *eol = '';
            printf("Parent: Read "%s" from child.n", inbuf);
        }
    } while (rd > 0);
    fprintf(stderr, "Parent: Child done!n");
    return 0;
}
<<p> pipechild.c来源/strong>
/* pipechild.c
 * Note - This is only for illustration purpose!
 * To be stable, we should catch/ignore signals,
 * and use select() to read.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#define MAXCOUNT    5   // Maximum input lines toread
#define INBUFSIZE   80  // Buffer size
int main(void)
{
    char    buff[INBUFSIZE];
    int remains = MAXCOUNT;
    pid_t   mypid;
    char    *eol;
    mypid = getpid();   // Process-ID
    fprintf(stderr, "Child %d: Started!n", mypid);
    // For each line read, write one tostdout.
    while (fgets(buff, INBUFSIZE, stdin) && remains--) {
        // Chop off ending EOL
        if ((eol = rindex(buff, 'n')) != NULL)
            *eol = '';
        // Debug to console
        fprintf(stderr, "Child %d: I got %s. %d remains.n",
            mypid, buff, 1 + remains);
        // Reply to parent
        sprintf(buff, "Child %d: %d remainsn", mypid, 1 + remains);
        write(1, buff, strlen(buff));
    }
    fprintf(stderr, "Child %d: I'm done!n", mypid);
    return 0;
}

最新更新