Shell如何通过管道(pipe)传输子进程?

3

最近我在学习 Linux 进程间通信。但是我对管道机制的理解有些问题。

我知道管道是由父进程创建的一对文件,然后父进程将文件描述符传递给子进程,子进程就可以操作它们了。

但是由于子进程在 fork() 后调用 exec() 时会拥有一个全新的虚拟内存,那么为什么父进程可以把信息传递给子进程呢?我有什么遗漏的吗?


1
文件描述符指向内核中的公共文件数据结构,因此内核可以将同一文件暴露给多个进程。 - Kerrek SB
请查看www.aosabook.org(bash)和Jessi Storimer(http://www.jstorimer.com/blogs/workingwithcode/7766107-a-unix-shell-in-ruby)。不确定这是否能回答您的问题,但可以给您一些见解。 - Felix
3个回答

2
当一个进程exec()到另一个进程时,子进程通常会继承父进程的标准文件路径:stdin(0),stdout(1),stderr(2)。当shell创建管道时,它使用dup2()调用将路径复制到所需的路径号,以强制子进程的标准路径正确。
// pseudo-code:
// create the pipe
int pipe_end[2];
pipe(pipe_end);

// "back up" stdin
int save_in = dup(0);

// position the pipe to stdin for the benefit of the child
dup2(pipe_end[0], 0);

// start the child
fork() && exec();

// restore stdin
close(0);
dup2(save_in, 0);

// write to the child
write(pipe_end[1], ...);

2
文件描述符是操作系统(内核)管理的资源句柄。当您创建一个管道时,内核会创建设施,以便可以将数据从管道的一端发送到另一端。
这些数据通过内核发送。
当您调用fork()时,子进程继承所有文件描述符,这意味着它们继承了由文件描述符引用的内核管理的数据结构。 因此,现在文件描述符在子进程和父进程中都引用相同的内核资源。由于内核资源位于内核中,因此该部分在2个进程之间共享,而不像用户空间内存那样被复制。
基本上,您将数据写入管道的一端,该数据被复制到内核中的缓冲区中。然后,您可以读取该数据,并将其从内核缓冲区复制到读取进程的内存空间中。在fork()之后,子进程和父进程都引用由pipe()创建的相同内核缓冲区。

由内核管理的资源称为“文件描述符”。来自openopen()函数应建立文件和__文件描述符__之间的连接。它将创建一个打开的__文件描述符__,该描述符引用文件和引用该打开的__文件描述符__。其他I/O函数使用文件描述符来引用该文件。 - Maxim Egorushkin

0

信息不会传递给子进程 - 它是通过隐式约定完成的。父进程知道它应该将 fds 复制到 0、1、2 号槽中,而子进程知道要从这些描述符读取/写入。你说得对,在 exec 过程中确实没有任何魔法涉及其中,子进程确实没有从其父进程获取任何信息,除了参数和环境向量。只是 Unix 平台有这些约定,所以子进程知道它要使用的相关 fds,而父进程知道要为 fds 选择哪些数字。

对于需要传递两个或三个以上 fds 的进程,父进程确实必须显式传递编号。以下是我机器上一些明显发生这种情况的进程(在其他地方可能被塞进环境变量中):

  • klauncher --fd=8
  • /bin/dbus-daemon --fork --print-pid 5 --print-address 7 --session

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接