读取写入管道的信息后无法退出 while 循环

2024-03-23

TLDR:您必须关闭所有子项中所有管道的写入端。仅当没有进程的写入端仍然打开时,读取才会检测到 EOF。 感谢@Bodo

作为操作系统课程作业的一部分,我试图从格式为以下的文件中读取行x operand y并将这些行分配给不同的子进程,以便每个子进程都可以将这些行作为输入并进行计算并将其写入一个输出文件。

我觉得我几乎已经获得了正确的结果,但在将所有写入的行读取到管道的读取端后,我的代码似乎导致了无限的 while 循环。

这是相关的代码片段

int child_work(int pipes[][2], int proc, int procid, FILE * out)
{
    int i;
    pid_t mypid;
    Expression exp;
    float result;
    int procidx = procid;
    char expression[MAIN_BUF_LEN];
    int r_val;
    printf("entered while loop for child process %d\n", mypid);
    while(1)
    {
        if ( (r_val = read(pipes[procid][0], expression, MAIN_BUF_LEN)) > 0)
        {
            printf("return values of read: %d\n", r_val);
            exp_readln(&exp, expression);
            result = exp_cal(&exp);
            printf("[#%d]: %d %0.3f\n", procidx, mypid, result);
            fprintf(out, "#%d: %d %0.3f\n", procidx, mypid, result);
            fflush(out);
            procidx += proc;
        }
        else
        {
            break;
        }
    }
    printf("exited while loop and reached end of child process %d\n", mypid);
    return 0;

int main(int argc, char **argv)
{
    if (argc != 4)
    {
        printf("not enough arguments");
        return 0;
    }

    const char *infile;  // Name of infile
    const char *outfile; // Name of outfile
    int proc;            // Number of child process to fork

    // Save arguments to variables
    infile = argv[1];
    outfile = argv[2];
    sscanf(argv[3], "%u", &proc);

    int pipes[proc][2]; // Pipes to be created
    pid_t child_pids[proc]; // store all the pids of children created

    int i; // Loop counter

    char buf[MAIN_BUF_LEN];
    Expression exp;

    FILE * in_ptr, *out_ptr;
    // Open infile with read-only, outfile with write and append.
    if ((in_ptr = fopen(infile, "r")) == NULL)
    {
        printf("Error in opening file. Ending program. \n");
        return 1;
    }
    out_ptr = fopen(outfile, "a+");

    // Get parent pid and print to outfile
    int ppid = getpid();
    fprintf(out_ptr, "%d\n", ppid);
    fflush(out_ptr);

    // $proc pipes should be created and saved to pipes[proc][2]
    for (i = 0; i < proc; ++i)
    {
        // TODO
        if (pipe(pipes[i]) == -1 )
        {
            printf("Pipe failed for pipe %d\n", i);
            return 1;
        }
    }

    // $proc child processes should be created.
    // Call child_work() immediately for each child.
    for (i = 0; i < proc; ++i)
    {
        int pid;
        // create child only if in parent process
        if (getpid() == ppid)
        {
            pid = fork();
            if (pid != 0)
                printf("created child with child pid %d\n", pid);
                child_pids[i] = pid;
        }

        if (pid == 0) // in child process
        {
            child_work(pipes, proc, i, out_ptr);
            break;
        }
        else if (pid < 0) // error in forking
        {
            printf("Fork failed.\n");
        }
    }

    // Close reading end of pipes for parent
    for (i = 0; i < proc; ++i)
    {
        // TODO
        if (getpid() == ppid)
            close(pipes[i][0]);
    }

    // Read lines and distribute the calculations to children in round-robin
    // style.
    // Stop when a empty line is read.

    char* line = NULL;
    size_t len = 0;
    ssize_t read = 0;
    int j = 0;
    while ((read = getline(&line, &len, in_ptr)) != -1) {
        //printf("Retrieved line of length %zu:\n", read);
        //printf("%s", line);
        j = j % proc;
        write(pipes[j++][1], line, strlen(line)+1);
    }

    // Close all the pipes when the task ends
    for (i = 0; i < proc; ++i)
    {
    //   close(pipes[i][READ]);
       close(pipes[i][WRITE]);
    }
    printf("Task 6 complete!");

    for (i = 0; i < proc; ++i)
    {
        waitpid(child_pids[i], NULL, 0);
    }

    fprintf(out_ptr, "\n");
    fflush(out_ptr);

    return 0;
}

这是我得到的输出,它似乎陷入了无限 while 循环,因为进程不会终止。另外,值return values of read:根据我正在使用的特定输入文件,应该是 22 或 23,但我不知道为什么它会针对特定的后续子进程递增。似乎没有一个子进程能够退出 while 循环,因为这样printf("exited while loop and reached end of child process %d\n", mypid);似乎没有被执行。我的理解是,如果一个管道已被读取,则返回值将是读取的行的字节大小,如果到达EOF或发生错误,则返回值分别为0或-1。

entered while loop for child process 16016
entered while loop for child process 16017
entered while loop for child process 16018
entered while loop for child process 16020
return values of read: 22
entered while loop for child process 16019
[#0]: 16016 1.783
return values of read: 22
return values of read: 22
[#2]: 16018 0.061
[#1]: 16017 0.195
return values of read: 22
return values of read: 22
[#5]: 16016 0.269
return values of read: 46
[#10]: 16016 1.231
return values of read: 22
return values of read: 22
[#6]: 16017 0.333
return values of read: 22
return values of read: 46
[#11]: 16017 1.684
[#7]: 16018 -0.734
return values of read: 46
[#12]: 16018 0.134
[#3]: 16019 0.778
return values of read: 68
[#4]: 16020 -0.362
return values of read: 68
[#9]: 16020 0.506
[#8]: 16019 -0.450

对于我可能犯的愚蠢错误的任何见解,我将不胜感激。谢谢!


代码中有几个问题。

不幸的是,我无法编译它并修复错误,因为它不完整。

  1. 您不能像这样定义大小不恒定的数组。

    int pipes[proc][2]; // Pipes to be created
    

    我希望编译器在这一行显示警告。
    您应该使用动态分配(malloc)或静态分配具有最大大小的数组并检查proc不大于最大值。

  2. 您必须关闭所有子项中所有管道的写入端。这read仅当没有进程的写入端仍然打开时才会检测 EOF。

  3. 代替

    while(1)
    {
        if ( (r_val = read(pipes[procid][0], expression, MAIN_BUF_LEN)) > 0)
        {
            /*...*/
        }
        else
        {
            break;
        }
    }
    

    我建议

    while((r_val = read(pipes[procid][0], expression, MAIN_BUF_LEN)) > 0)
    {
            /*...*/
    }
    
  4. 代替

        pid = fork();
        if (pid != 0)
            printf("created child with child pid %d\n", pid);
    

    它应该是

        pid = fork();
        if (pid > 0)
            printf("created child with child pid %d\n", pid);
    

    because pid < 0是一个错误。

  5. 代替

    if (pid == 0) // in child process
    {
        child_work(pipes, proc, i, out_ptr);
        break;
    }
    

    use

    if (pid == 0) // in child process
    {
        child_work(pipes, proc, i, out_ptr);
        return 0;
    }
    

    With break;孩子会在之后继续执行代码for循环将读取文件并写入管道child_work返回。

  6. 不保证每个孩子都能轮到read从父级之前的管道writes 下一个数据,因此它可能会在单个消息中获取两个或多个消息read。在实际应用中,您还应该准备好处理不完整的情况read or write调用并继续写入/读取剩余数据read or write calls.

    我认为处理部分的最简单方法read or write就是使用缓冲IO。您可以使用fdopen使用管道的 wrtite 文件描述符或读取文件描述符,并将数据写入/读取为以换行符结尾的文本行,例如使用fprintf or fgets分别。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

读取写入管道的信息后无法退出 while 循环 的相关文章

随机推荐