【问题标题】:Why are my child processes being waited on by parent without any work being done?为什么我的子进程被父进程等待而没有完成任何工作?
【发布时间】:2013-02-19 21:01:11
【问题描述】:

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

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

【问题讨论】:

    标签: c process pipe children fork


    【解决方案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()
    

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

    【讨论】:

    • 啊,谢谢。为了非常清楚,您的意思是我应该在任何分叉之前创建管道,或者就此而言,解析,然后分叉,并在 switch 语句默认值下:案例,我应该为父级执行任务?我的印象是,你不能依赖父母总是先跑。
    • 创建管道,然后分叉。父进程读取文件并写入管道;子进程从管道中读取。您无需担心谁先运行——从空管道读取是一个阻塞操作。
    • 谢谢。我的问题仍然没有完全解决,但至少它们(或多或少)朝着正确的方向发展。
    【解决方案2】:

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

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

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

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

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

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

    【讨论】:

    • 看起来很公平,但我不确定将它们放在哪里。我尝试在声明输入后立即放置 fflush(input) ,但这并没有改变任何东西。写入管道后我应该这样做吗?我不能做 fclose(input) 因为我必须重用管道所以我可以发送单词循环样式,所以当我在解析器中尝试 fclose(input) 时它给了我一个段错误。
    • 您应该在 fputs() 之后立即执行 fflush(),因为它是 fputs 调用将字节放入缓冲区,并且您希望在获得这些字节后刷新。如果没有 fclose(),你仍然会泄漏,但首先要做的事情是 :)
    • 好吧,这肯定是朝着正确方向迈出的一步。似乎并不是所有的孩子都收到了管道文本,但至少在每种情况下,最后一对都是。 fflush 肯定是其中的一部分。我还将尝试解决 Sean Connor 建议的管道问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-17
    • 2016-06-13
    • 1970-01-01
    • 2013-10-28
    • 1970-01-01
    • 2013-07-07
    • 1970-01-01
    相关资源
    最近更新 更多