使用管道跨 child 个进程进行通信
Communicating across child processes with a pipe
我的任务是用 c 语言创建自己的 shell。我将使用 fork()、pipe()、exec() 和 wait() 来实现这一点。我有一个好的开始,但是我对管道的研究越多,我就越困惑。 child 进程的每个管道示例如下所示:
这个我完全理解。我以前实施过。我的问题是这个例子有多简单。在创建 shell 时,我需要两个 children 通过管道相互通信,以便 运行 像“cat file | grep hello”这样的命令。我可以想象有几种方法可以做到这一点。这是我的第一个想法:
这似乎不起作用。我可能只是因为我的代码有缺陷,但我怀疑我的
对管道和文件描述符的理解不够。我认为由于 pipe 在 main() 中被调用并且 fd[] 是一个文件变量,所以这个策略应该有效。 Linux 手册指出“在 fork() 时,两个内存空间具有相同的内容。”当然,我的 child 进程可以通过相同的文件描述符访问管道。
我的理解有问题吗?我可以尝试使进程 运行 像这样同时进行:
但我不确定为什么这会有所不同。
问题:如果一个进程写入管道,但没有第二个进程立即读取该数据,数据会丢失吗?
大多数在线示例表明每个进程都需要关闭它未使用的管道末端。但是,偶尔我会看到一个在两个进程中关闭管道两端的例子:
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
据我所知,dup2 复制了文件描述符,使 2 个打开的文件描述符指向同一个文件。如果我不关闭两者,那么 execvp() 将继续等待输入并且永远不会退出。这意味着当我完成阅读后,我应该关闭(stdin)。
问题:2 children 通过管道通信,主进程是否需要管道的任何东西,例如 close(fd[0])?
If a process writes to a pipe, but there is no immediate second process to read that data, does the data get lost?
没有
默认,写入管道是一个阻塞操作。也就是说,写入管道将阻止调用进程的执行,直到管道中有足够的空间来写入请求的数据。
读取端有责任排空管道以腾出空间,或者关闭管道的一侧以表示他们不再希望接收数据。
With 2 children communicating over a pipe, does the main process need anything with the pipe, such as close(fd[0])?
所涉及的每个进程都将有自己的文件描述符副本。
因此,父进程应该关闭管道的两端(在两个分支之后),因为它没有理由保留这些文件描述符。如果不这样做,可能会导致父进程 运行 耗尽文件描述符 (ulimit -n
)。
您对 dup2
的理解似乎是正确的。
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
管道的两端都关闭了,因为在 dup2
之后,通常与 stdin
关联的文件描述符现在指的是与文件相同的 文件描述 管道读取端的描述符。
stdin
当然是closed closed 当替换过程映像(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], 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], NULL);
return 1;
}
close(fds[0]);
close(fds[1]);
waitpid(left, NULL, 0);
waitpid(right, NULL, 0);
}
我的任务是用 c 语言创建自己的 shell。我将使用 fork()、pipe()、exec() 和 wait() 来实现这一点。我有一个好的开始,但是我对管道的研究越多,我就越困惑。 child 进程的每个管道示例如下所示:
这个我完全理解。我以前实施过。我的问题是这个例子有多简单。在创建 shell 时,我需要两个 children 通过管道相互通信,以便 运行 像“cat file | grep hello”这样的命令。我可以想象有几种方法可以做到这一点。这是我的第一个想法:
这似乎不起作用。我可能只是因为我的代码有缺陷,但我怀疑我的 对管道和文件描述符的理解不够。我认为由于 pipe 在 main() 中被调用并且 fd[] 是一个文件变量,所以这个策略应该有效。 Linux 手册指出“在 fork() 时,两个内存空间具有相同的内容。”当然,我的 child 进程可以通过相同的文件描述符访问管道。
我的理解有问题吗?我可以尝试使进程 运行 像这样同时进行:
但我不确定为什么这会有所不同。
问题:如果一个进程写入管道,但没有第二个进程立即读取该数据,数据会丢失吗?
大多数在线示例表明每个进程都需要关闭它未使用的管道末端。但是,偶尔我会看到一个在两个进程中关闭管道两端的例子:
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
据我所知,dup2 复制了文件描述符,使 2 个打开的文件描述符指向同一个文件。如果我不关闭两者,那么 execvp() 将继续等待输入并且永远不会退出。这意味着当我完成阅读后,我应该关闭(stdin)。
问题:2 children 通过管道通信,主进程是否需要管道的任何东西,例如 close(fd[0])?
If a process writes to a pipe, but there is no immediate second process to read that data, does the data get lost?
没有
默认,写入管道是一个阻塞操作。也就是说,写入管道将阻止调用进程的执行,直到管道中有足够的空间来写入请求的数据。
读取端有责任排空管道以腾出空间,或者关闭管道的一侧以表示他们不再希望接收数据。
With 2 children communicating over a pipe, does the main process need anything with the pipe, such as close(fd[0])?
所涉及的每个进程都将有自己的文件描述符副本。
因此,父进程应该关闭管道的两端(在两个分支之后),因为它没有理由保留这些文件描述符。如果不这样做,可能会导致父进程 运行 耗尽文件描述符 (ulimit -n
)。
您对 dup2
的理解似乎是正确的。
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
管道的两端都关闭了,因为在 dup2
之后,通常与 stdin
关联的文件描述符现在指的是与文件相同的 文件描述 管道读取端的描述符。
stdin
当然是closed closed 当替换过程映像(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], 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], NULL);
return 1;
}
close(fds[0]);
close(fds[1]);
waitpid(left, NULL, 0);
waitpid(right, NULL, 0);
}