我正在编写一个处理管道的简单外壳。我有工作代码,但我不太明白它是如何在幕后工作的。这是我需要帮助理解的修改后的代码片段(我删除了错误检查以缩短它):
int fd[2];
pipe(fd);
if (fork()) { /* parent code */
close(fd[1]);
dup2(fd[0], 0);
/* call to execve() here */
} else { /* child code */
close(fd[0]);
dup2(fd[1], 1);
}
我对我的问题有猜测,但仅此而已 - 猜测。以下是我的问题:
- 阻塞在哪里执行?在我见过的所有示例代码中,
read()
并write()
提供了阻塞,但我不需要在这里使用它们。我只是复制 STDIN 指向管道的读取端和 STDOUT 指向管道的写入端。我猜正在发生的是 STDIN 在执行后dup2(fd[0], 0)
正在执行阻塞。这个对吗? - 据我了解,每个正在运行的进程都有一个描述符表,指向文件表中打开的文件。当进程重定向 STDIN、STDOUT 或 STDERR 时会发生什么?这些文件描述符是否在所有进程的描述符表中共享?还是每个流程都有副本?重定向一个是否会导致更改在所有这些中反映出来?
在调用
pipe()
和随后的调用之后fork()
,管道有 4 个“端”打开:父级访问的读取和写入端以及子级访问的读取和写入端。在我的代码中,我关闭了父母的写入端和孩子的读取端。但是,在完成管道后,我不会关闭剩余的两端。代码工作正常,所以我假设某种隐式关闭已经完成,但这都是猜测工作。我应该像这样添加显式调用来关闭剩余的两端吗?int fd[2]; pipe(fd); if (fork()) { /* parent code */ close(fd[1]); dup2(fd[0], 0); /* call to execve() here */ close(fd[0]); } else { /* child code */ close(fd[0]); dup2(fd[1], 1); close(fd[1]); }
这更多是关于管道过程如何工作的概念性问题。有管道的读取端,由文件句柄引用,
fd[0]
管道的写入端,由文件句柄引用fd[1]
。管道本身只是一个字节流表示的抽象。文件句柄代表打开的文件,对吗?那么这是否意味着在系统的某个地方,有一个文件(由 指向fd[1]
),其中包含我们想要通过管道发送的所有信息?并且在通过字节流推送该信息之后,有一个文件(由 指向fd[0]
),其中也写入了所有信息,从而创建了管道的抽象?