为什么父母在没有任何工作的情况下等待我的孩子进程?

时间:2013-02-19 21:01:12

标签: c process pipe children fork

我确信这是相当简单的事情,但对于我的生活,我无法弄清楚为什么我的孩子进程没有工作,等待,然后最后一个暂停(就像我没有关闭管道正确)。无论如何,我会发布一堆代码,但这就是它正在做的事情:

  1. 程序解析txt文档并获取所有单个单词,然后沿管道循环方式将它们发送到指定数量的子进程。我有一个1维数组,它保存管道FD,每个偶数索引都是一个读数,每个奇数索引都是一个写管道。

  2. 解析完成后,程序会在分叉子项之前关闭读/写管道(用父项关闭管道)。然后,在for循环中,生成指定数量的子进程,并在子进程中关闭相应管道的写入结束,并打开读取结束。必须使用fgets从管道中获取输入(我知道,很烦人,但这是一个要求)。

  3. 子进程完成后,父进程等待它。有一些注释和调试器行,我试图帮助我,从它们看起来像子进程正在分叉并正确输入,写管道关闭,读管道打开,但当我做fgets()函数,它会立即退出并由父级等待。有趣的是,并非所有的孩子都等待。如果我希望子项数为3,则等待2个进程,第3个进程挂断。如果我想要10个进程,5个等待,第6个被挂起。

  4. 所以,我很确定它与fgets()有关但我无法弄明白为什么!我有一个预感,当它们沿着管道被发送时,可能与换行符在错误的位置有关(fgets读取到换行符,对吧?)但是基于编写的代码和一些额外的调试语句输入到来自父进程的管道似乎是换行符正确终止。

    无论如何,这里是解析器的代码,然后是创建子项的位 -

    分析器:

    char buf[PIPE_BUF];     
    int wordCount;
    
    char buffer[PIPE_BUF];
    char *temp;
    char word[50];
    FILE* inputFile = fopen(fileName,  "r"); //OPENS file
    //Parsing and distributing words round robin to pipes
    while(fgets(buffer, (sizeof buffer), inputFile)){
        //remove all non-alpha chars in buffer and converts to lowercase
        int i;
        for(i = 0; i < strlen(buffer); i++){
            if(isalpha(buffer[i]) == 0){ //0 means it is not a letter
                buffer[i] = ' ';
            }
            else{
                buffer[i] = tolower(buffer[i]); //turn the current word to lower case
            }
        }
        //parse words and sends them to the sort processes in a round-robin fashion
        temp = strtok(buffer, " "); //splits along spaces
        if(temp != NULL){
            strcpy(word, temp);
            strcat(word, "\n"); //puts newline at the end
        }
        int j = 0;
        while(temp != NULL){
            FILE *input = fdopen(pipefds[(j*2)+1], "w");
            //close(pipefds[j*2]); //closing read pipes in parent
            fputs(word, input); //puts into write pipe
            printf("fputs done successfully with pipe %d with contents: %s\n", pipefds[(j*2)+1], word);
            //close(pipefds[(j*2)+1]); //closing write pipe after write is done
            temp = strtok(NULL, " ");
            if(temp != NULL){
                strcpy(word, temp);
                strcat(word, "\n");
            }
            if(j == (numChildren - 1)){
                j = 0;
            }
            else{
                j++;
            }
        }
    }
    //need to close all parent writes, and parent reads (it's done with everything)
    for(i = 0; i < numChildren; i++){
        close(pipefds[i]);
    }
    

    父分叉和获取管道数据:

    //Collection of children need to be created specified by numChildren
    int count;
    for(count = 0; count < numChildren; count++){
        printf("Count: %d\n", count);
    
        switch((p = fork())){
    
        case -1:
            perror("Could not create child");
            exit(-1);
    
        case 0:
            printf("Entering child\n");
            //child case, GET INPUT FROM PARENT TO SORT!!! SEND TO SUPPRESSOR (Uses both input and output)
            //count[0] = read, count[1] = write
            close(pipefds[(count*2)+1]); //closing write pipes in child
            printf("write pipe closed in child\n");
            FILE *output = fdopen(pipefds[count*2], "r"); //opening the read pipe from the parent write pipe
            printf("read pipe opened in child\n");
            fgets(buf, PIPE_BUF, output); //gets data from read pipe
            printf("child read pipe contents read (fgets) with buf contents: %s\n", buf);
            printf("child read pipe closed (%d)\n", getpid());
            //execlp("sort", "sort", sortStuff,(char*)NULL);
            close(pipefds[count*2]); //closing read pipe after reading is done
            count = numChildren;
            break;
    
        default:
            //parent case -- p holds pid of child
    
            printf("I am the parent, PID: %d\n", getpid());
            child = wait(&status);
            printf("Waited on child %d\n", child);
    
            break;
        }
    
    }
    

    我提前为代码道歉,我不是最好的C程序员,所以事情往往会有些混乱。

2 个答案:

答案 0 :(得分:0)

使用此函数将FILE *与描述符关联时:

FILE * input = fdopen(pipefds [(j * 2)+1],“w”);

然后,除了管道中发生的任何缓冲之外,您对* input执行的任何操作都将被缓冲。因此,无论你想到的是什么你正在写入管道,实际上只是坐在FILE *的缓冲区中,而实际上并没有真正到达管道。如果您使用fclose(输入),缓冲区将在最后刷新,但我认为您正在关闭基础文件描述符,而不是FILE *,这意味着FILE *的所有缓冲区管理都不知道它应该完成起来。

调用fflush(输入)可能有所帮助。

单独地,更深入地说,如果你在开始阅读之前将所有数据写入管道,你应该意识到管道之前可以缓冲的数据是有限的它不会再接受任何输入(直到从另一端读出某些内容。)

编辑:总结,我认为你的数据被卡在FILE *缓冲区中,甚至从未进入管道。没有脸红。此外,您应该在某个地方调用fclose(input),这将影响您关闭()底层文件描述符的需要。

答案 1 :(得分:0)

主要问题在于此代码:

// need to close all parent writes, 
// and parent reads (it's done with everything)

for(i = 0; i < numChildren; i++){
  close(pipefds[i]);

在创建子进程之前执行此操作(显示),通过这样做,您基本上删除了管道。他们走了。它们不再存在。儿童过程没有任何内容可供阅读。我的猜测是这一行:

FILE *output = fdopen(pipefds[count*2], "r");

失败(输出为NULL),因为文件描述符已经关闭,因此,就系统而言,它是一个无效的描述符。

另一个问题是您的步骤顺序。通常,您创建一个管道,然后创建一个子进程,它只在子进程完成后才关闭管道。我不认为我曾经见过写入管道的实现,然后会创建子进程来读取它,因为这有一个很大的问题:管道的大小有限,并且父进程可以阻止写入管道(我怀疑你有一个小的文件,你正在测试,因此,没有达到管道的大小限制)。

我建议的步骤顺序为:

create the pipes
create the child processes
in the parent process
    close the read end of each pipe (only the read end)
    read the text file
    write words to the pipes
    when done reading text file, close the write end of each pipe
    wait for each child 
in the child process(es)
    close the write end of the pipe its using
    while there is input from the pipe
      read the input
      do whatever
    close the read end of the pipe
    _exit()

这样,您实际上可以获得多处理的好处,并且您不必担心父母在写作时无限期地阻止(因为总是有一个子进程读取)。