使用管道跨子进程通信

问题描述 投票:0回答:1

我的任务是用 c 创建自己的 shell。我将使用 fork()、pipe()、exec() 和 wait() 来实现此目的。我有一个良好的开端,但我对管道的研究越多,我就越困惑。每个通过管道传输到子进程的示例如下所示:

我完全理解这一点。我之前已经实现过。我的问题是这个例子有多简单。在创建 shell 时,我需要两个子进程通过管道相互通信,以便运行“cat file | grep hello”等命令。我可以想象几种方法来做到这一点。这是我的第一个想法:

这似乎不起作用。我可能只是我的代码有缺陷,但我怀疑我的 对管道和文件描述符的理解还不够。我认为由于在 main() 中调用了 pipeline 并且 fd[] 是一个文件变量,因此该策略应该有效。 Linux 手册指出“在 fork() 时,两个内存空间具有相同的内容”。当然,我的子进程可以通过相同的文件描述符访问管道。

我的理解有问题吗?我可以尝试让进程像这样同时运行:

但我不确定为什么会有不同的表现。

问题:如果一个进程写入管道,但没有立即的第二个进程读取该数据,数据会丢失吗?

大多数在线示例表明每个进程都需要关闭不使用的管道末端。然而,偶尔我会看到一个在两个进程中都关闭管道两端的例子:

close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
据我所知, dup2 复制了文件描述符,使 2 个打开的文件描述符指向同一个文件。如果我不关闭两者,则 execvp() 继续等待输入并且永远不会退出。这意味着当我完成阅读后,我应该关闭(stdin)。

问题:有 2 个子进程通过管道进行通信,主进程是否需要与管道相关的任何内容,例如 close(fd[0])?

c shell process pipe fork
1个回答
1
投票
如果一个进程写入管道,但没有立即的第二个进程来读取该数据,数据会丢失吗?

没有。

默认情况下,写入管道是一个阻塞操作。也就是说,写入管道将阻止调用进程的执行,直到管道中有足够的空间来写入请求的数据。

读取方有责任排空管道以腾出空间,或关闭管道一侧以表明他们不再希望接收数据。

有 2 个子进程通过管道进行通信,主进程是否需要与管道相关的任何内容,例如 close(fd[0])?

涉及的每个进程都将拥有其“自己的文件描述符副本”。

因此,父进程应该关闭管道的两端(在两个分叉之后),因为它没有理由保留这些文件描述符。如果不这样做可能会导致父进程耗尽文件描述符 (

ulimit -n)。

您对

dup2
的理解似乎是正确的。


close(fd[1]); dup2(fd[0], STDIN_FILENO); close(fd[0]);

管道的两端都被关闭,因为在
dup2

之后,通常与
stdin
关联的文件描述符现在引用与管道读取端的文件描述符相同的

文件描述

当替换过程映像(
stdin
)退出时,exec*

当然是关闭的。

你的第二个分叉两个进程的例子,它们同时运行,是正确的理解。
在典型的 shell 中,管道命令同时运行。否则,如前所述,编写器可能会在完成其任务之前填充管道和块。


通常,父进程会等待两个进程完成。

这是一个玩具示例。作为

./program FILE STRING

运行以模拟

cat FILE | grep STRING

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

int main(int argc, char **argv) {
    int fds[2];

    pipe(fds);

    int left = fork();

    if (0 == left) {
        close(fds[0]);
        dup2(fds[1], fileno(stdout));
        close(fds[1]);
        execlp("cat", "cat", argv[1], (char *) NULL);
        return 1;
    }

    int right = fork();

    if (0 == right) {
        close(fds[1]);
        dup2(fds[0], fileno(stdin));
        close(fds[0]);
        execlp("grep", "grep", argv[2], (char *) NULL);
        return 1;
    }

    close(fds[0]);
    close(fds[1]);

    waitpid(left, NULL, 0);
    waitpid(right, NULL, 0);
}

© www.soinside.com 2019 - 2024. All rights reserved.