读取写入管道的信息后无法退出while循环 [英] Unable to exit while loop after reading information written to pipe

查看:112
本文介绍了读取写入管道的信息后无法退出while循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TLDR:您必须关闭所有子级中所有管道的写入端.仅当没有进程的写操作端仍打开时,读操作才会检测到EOF. 归功于@Bodo

TLDR: You have to close the write end of all pipes in all children. The read will detect EOF only if no process has the write end still open. Credits to @Bodo

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

As part of an assignment for an operating systems course, I'm trying to read lines from a file which is in the format of x operand y and distribute the lines to different child processes so that each one can take those lines as input and conduct calculations and write it to one output file.

通过获得正确的结果,我感觉自己已经快到了,但是在将所有写入的行读取到管道的读取端之后,我的代码似乎导致了一个无限的while循环.

I feel like I'm almost there by getting the right results, but my code seems to lead to an endless while loop after reading all of the written lines to the read end of a pipe.

有关代码段

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.

This is the output that I am getting, which seemingly gets stuck in an infinite while loop as the process won't terminate. Also, the value of return values of read: should either be 22 or 23 based on the particular input file that I am using, but I don't know why it is incrementing for particular subsequent child processes. None of the child processes seem to be able to exit the while loop as this printf("exited while loop and reached end of child process %d\n", mypid); doesn't seem to be executed. My understanding is that if a pipe has been read, the return value will be the byte size of the line read, and if it reaches EOF or an error, the return value is 0 or -1, respectively.

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

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

I would appreciate any insight for a silly mistake I might be making. Thanks!

推荐答案

代码中有几个问题.

不幸的是,由于它不完整,我无法编译它并修复错误.

Unfortunately I cannot compile it and fix the errors because it is incomplete.

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

  1. You cannot define arrays with a size that is not constant like this.

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

我希望编译器在此行显示警告.
您应该使用动态分配(malloc)或以最大大小静态分配数组,并检查proc不大于最大值.

I would expect the compiler to show a warning at this line.
You should either use dynamic allocation (malloc) or statically allocate the arrays with a maximum size and check that proc is not greater than the maximum.

您必须关闭所有子级中所有管道的写入端. read仅在没有进程仍未打开写端的情况下才会检测到EOF.

You have to close the write end of all pipes in all children. The read will detect EOF only if no process has the write end still open.

代替

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)
{
        /*...*/
}

  • 代替

  • Instead of

        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);
    

    因为pid < 0是错误.

    代替

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

    使用

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

    使用break;,子代将在for循环之后继续执行代码,该循环将读取文件并在child_work返回时写入管道.

    With break; the child would continue with the code after the for loop that would read the file and write to the pipes when child_work returns.

    不能保证每个孩子都会在父级write接收下一个数据之前从管道转到read,因此它可能在单个read中获得两个或更多消息.在实际的应用程序中,您还应该准备处理不完整的readwrite调用,并通过其他readwrite调用继续写入/读取其余数据.

    It is not guaranteed that every child will get its turn to read from the pipe before the parent writes the next data, so it may get two or more messages in a single read. In real applications you should also be prepared to handle incomplete read or write calls and to continue writing/reading the remaining data with additional read or write calls.

    我认为处理部分readwrite的最简单方法是使用缓冲的IO.您可以将fdopen与管道的wrtite文件描述符或读取文件描述符一起使用,并将数据作为一行以换行符结尾的文本行写入/读取数据,例如分别是fprintffgets.

    I think the easiest way to handle partial read or write would be to use buffered IO. You can use fdopen with the wrtite file descriptor or the read file descriptor of the pipe and write/read the data as a line of text terminated with a newline using e.g. fprintf or fgets respectively.

    这篇关于读取写入管道的信息后无法退出while循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆