简体   繁体   English

密切写入后,管道读取被阻止

[英]read from a pipe is blocked after close writer

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char **argv) {

    int childs[3];
    for (int i = 0; i < 3; ++i) {
        int p[2];
        if (pipe(p) == -1) { perror("pipe"); exit(1); }

        pid_t pid = fork();
        if (pid) {
            close(p[0]);
            childs[i] = p[1];
        }
        else {
            close(p[1]);
            printf("child %d start\n", i + 1);

            char buf[10];
            buf[0] = 0;
            int r;
            if ((r = read(p[0], buf, 9)) == -1) { ... }

            printf("child %d read %s (%d), finish\n", i + 1, buf, r);

            sleep(2);
            exit(0);
        }
    }

    for (int i = 0; i < 3; ++i) {
    //        if (argc > 1) {
    //            write(childs[i], "42", 2);
    //        }
    // ============== HERE >>>
        close(childs[i]);
    }

    pid_t pid;
    while ((pid = waitpid(-1, NULL, 0)) > 0) {
         printf("child %d exited\n", pid);
    }

    return 0;
}

Output with comment: 输出评论:

child 1 start
child 2 start
child 3 start
child 3 read  (0), finish

The next line is displayed after 2 seconds 2秒后显示下一行

child 2 read  (0), finish

The next line is displayed after 2 seconds 2秒后显示下一行

child 1 read  (0), finish

I do not write to the channel in the parent. 我不写信给父母的频道。 Closing it, I want to give a signal to the child that will be waiting in the read . 关闭它,我想给那个将在read等待的孩子发出信号。

It seems that there is a following. 似乎有以下几点。 Сhild N expected finishes reading from the result 0 , it's ok. Сhild N预计从结果0读完,没关系。 Children 2 (N-1) and 1 are locked in a read to a child 3 is completed. 儿童2 (N-1)1被锁定在给孩子read 3完成。 Then the child 1 is similar will wait. 那么孩子1就差不多了。

Why lock occur? 为什么发生锁定?

Child processes inherit open file descriptors from their parent. 子进程从其父进程继承打开的文件描述符。 Your main process opens file descriptors in a loop (using pipe , keeping only the write ends). 您的主进程在循环中打开文件描述符(使用pipe ,仅保留写入结束)。 Child 1 inherits no descriptors (except for stdin/stdout/stderr); 子1没有描述符(stdin / stdout / stderr除外); child 2 inherits childs[0] (the descriptor going to child 1); child 2继承childs[0] (描述符转到子1); child 3 inherits childs[0] and childs[1] (the descriptors going to child 1 and 2). child 3继承了childs[0]childs[1] (描述符转到了孩子1和2)。

read on a pipe blocks as long as any write descriptor is still open (because it could be used to send more data). 只要任何写入描述符仍然打开(因为它可以用于发送更多数据), read管道块。 So child 1 waits (because child 2 and child 3 still have an open write descriptor) and child 2 waits (because child 3 still has an open write descriptor); 因此,子1等待(因为子2和子3仍然具有打开的写入描述符)并且子2等待(因为子3仍然具有打开的写入描述符); only child 3 sleeps and exits. 独生子3睡觉和退出。 This causes its file descriptors to close, which wakes up child 2. Then child 2 sleeps and exits, closing its file descriptors, which finally wakes up child 1. 这导致其文件描述符关闭,唤醒子2。然后子2睡眠并退出,关闭其文件描述符,最终唤醒子1。


If you want to avoid this behavior, you have to close the open file descriptors in each child: 如果要避免此行为,则必须关闭每个子项中的打开文件描述符:

    else {
        for (int j = 0; j < i; j++) {
            close(childs[j]);
        }
        close(p[1]);
        printf("child %d start\n", i + 1);

The write ends of the pipes are getting inherited by the children. 管道的写端由孩子们继承。 Since filedescriptor are ref-counted, the write end is only considered closed if all references to it are closed. 由于filedescriptor被重新计数,因此只有在关闭它的所有引用时才认为写结束是关闭的。

Below is your code, slightly refactored, with a fix added: 以下是您的代码,略有重构,添加了修复:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char **argv) {

    int children_w[3];

    for (int i = 0; i < 3; ++i) {
        int p[2];

        if (0>pipe(p)) 
            { perror("pipe"); exit(1); }

        pid_t pid;
        if(0> (pid= fork()))
            { perror("fork"); exit(1); }

        if(pid==0) {
            /* Fix -- close the leaked write ends  */
            int j;
            for(j=0; j<i; j++)
                close(children_w[j]);
            /* end fix*/ 
            close(p[1]);
            printf("child %d start\n", i + 1);

            char buf[10];
            buf[0] = 0;
            int r;
            if ((r = read(p[0], buf, 9)) == -1) { perror("read");/*...*/ }

            printf("child %d read %s (%d), finish\n", i + 1, buf, r);

            sleep(2);
            exit(0);
        }
        children_w[i] = p[1];
        close(p[0]);
    }

    for (int i = 0; i < 3; ++i) {
    //        if (argc > 1) {
    //            write(childs[i], "42", 2);
    //        }
    // ============== HERE >>>
        close(children_w[i]);
    }

    pid_t pid;
    while ((pid = waitpid(-1, NULL, 0)) > 0) {
         printf("child %d exited\n", pid);
    }

    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM