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

时间:2016-08-28 20:59:36

标签: c unix ipc system-calls

#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;
}

评论输出:

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

2秒后显示下一行

child 2 read  (0), finish

2秒后显示下一行

child 1 read  (0), finish

我不写入父母的频道。关闭它,我想给将要在read等待的孩子发出信号。

似乎有以下内容。 Сhild N预计会从结果0中读取,这没关系。儿童2 (N-1)1已锁定在read儿童3已完成。那么孩子1就像等待一样。

为什么会发生锁定?

2 个答案:

答案 0 :(得分:2)

子进程从其父进程继承打开的文件描述符。您的主进程在循环中打开文件描述符(使用pipe,仅保留写入结束)。子1没有描述符(stdin / stdout / stderr除外);子2继承childs[0](描述符转到子1);子3继承childs[0]childs[1](描述符转到子1和2)。

只要任何写入描述符仍然打开(因为它可以用于发送更多数据),就会在管道块上

read。因此,子1等待(因为子2和子3仍然具有打开的写入描述符)并且子2等待(因为子3仍然具有打开的写入描述符);独生子3睡觉和退出。这导致其文件描述符关闭,唤醒子2。然后子2睡眠并退出,关闭其文件描述符,最终唤醒子1。

如果要避免此行为,则必须关闭每个子项中的打开文件描述符:

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

答案 1 :(得分:0)

管道的写入端由子节点继承。 由于filedescriptor被重新计数,因此只有在对所有引用都被关闭的情况下才会认为写入结束。

以下是您的代码,稍加重构,添加了修复程序:

#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;
}