如何将stdin传递给一个孩子并用C语言执行cat

时间:2013-05-11 00:44:28

标签: c unix fork pipe

在下面的代码中,我只是尝试通过stdin将文件发送到子进程,该进程将执行cat OS命令。代码编译得很好。以下是我从命令行调用它的方法:

$ ./uniquify < words.txt

然而,当我运行它时,我得到一个seg错误。我真的很难理解如果信息应该通过管道传给儿童的流程。我试图使代码尽可能简单,所以我可以理解它,但它还没有意义。任何帮助将不胜感激。

#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], "%s\n", 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;
}

1 个答案:

答案 0 :(得分:0)

由于您的问题似乎是了解管道和叉子的工作方式,我希望以下程序可以帮助您。请注意,这仅供参考。它没有资格进行商业实施,但我想保持简短!

您可以按如下方式编译这两个程序:

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

然后使用./pipeparent

执行

pipeparent.c来源

/* 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 %d\n", 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 = '\0';
            printf("Parent: Read \"%s\" from child.\n", inbuf);
        }
    } while (rd > 0);

    fprintf(stderr, "Parent: Child done!\n");

    return 0;
}

pipechild.c source

/* 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 = '\0';
        // 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 remains\n", mypid, 1 + remains);
        write(1, buff, strlen(buff));
    }
    fprintf(stderr, "Child %d: I'm done!\n", mypid);

    return 0;
}