0

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

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

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

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

所以,我很确定它与 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 程序员,所以事情往往会变得有点混乱。

4

2 回答 2

1

主要问题在于这段代码:

// 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()

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

于 2013-02-19T23:12:16.513 回答
0

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

文件 *input = fdopen(pipefds[(j*2)+1], "w");

那么除了管道中进行的任何缓冲之外,您对 *input 所做的任何事情都将被缓冲。因此,有可能您认为您正在写入管道的任何内容实际上只是位于 FILE* 的缓冲区中而从未真正到达管道。如果您使用 fclose(input) 缓冲区将在最后被刷新,但我认为您正在关闭底层文件描述符,而不是 FILE*,这意味着 FILE* 的所有缓冲区管理都不知道它应该完成向上。

调用 fflush(input) 可能会有所帮助。

另外,更深层次的是,如果您在开始读取之前将所有数据写入管道,您应该知道管道在不再接受任何输入之前可以缓冲的内容是有限的(直到从另一端读出一些东西。)

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

于 2013-02-19T21:24:55.007 回答