2013-05-11 39 views
1

在下面的代碼中,我只是試圖通過stdin將文件發送到將執行cat OS命令的子進程。代碼編譯得很好。這裏是我如何從命令行調用它:如何將stdin傳遞給子進程並在C中運行cat

$ ./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; 
} 
+0

你永遠不會初始化'writeToChildren',所以你在一個糟糕的'FILE *'上調用'fprintf'。 – 2013-05-11 00:51:18

+0

所以添加這樣的東西? writeToChildren [c] = fdopen(p [1],「w」); – user2227422 2013-05-11 00:56:26

+0

我編輯了代碼,並將以下代碼添加到父代碼中: int p [2]; //創建管道 pipe(p); writeToChildren [c] = fdopen(p [1],「w」); – user2227422 2013-05-11 01:03:21

回答

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源執行

/* 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; 
} 
+0

有些事情我故意排除,以避免太複雜:1)異步通信是不可能的。您需要使用select(),並使用解鎖讀取。 2)僅支持來自孩子的低級寫入。 3)開始時不需要父母編寫和閱讀它自己的管道,但這是爲了說明可以完成,並且必須關閉一端以避免將數據發回給您。還有其他無用的垃圾代碼,你也可以刪除。 – Rein 2014-10-15 14:28:43

相關問題