因此,对于重定向方面,您需要在文件句柄的 fork 中使用 dup2()。要设置管道,您需要在主线程中使用 pipe() 创建句柄,然后在 fork 中使用 dup2()。
请注意,您必须非常小心 pipe() 不会为您提供低编号的文件句柄。您可以使用 fcntrl(F_DUPFD, ...) 来欺负他们。另外,使用 CLOEXEC 去除所有漂浮在周围的垃圾二级句柄。
伪代码:
fin = open("input", read)
pipeIO = pipe()
fout = open("output", write)
fin2 = fcntrl(F_DUPFD_CLOEXEC, fin, 3); close fin
pipeI2 = fcntrl(F_DUPFD_CLOEXEC, pipeI, 3); close pipeI
pipeO2 = fcntrl(F_DUPFD_CLOEXEC, pipeO, 3); close pipeO
fout2 = fcntrl(F_DUPFD_CLOEXEC, fout, 3); close fout
fork
{
dup(fin2, 0)
dup(pipeO2, 1)
exec Command1
}
close fin2
close pipeO2
fork
{
dup(pipeI2, 0)
dup(out2, 1)
exec Command2
}
close fout2
close pipeI2
wait
或者,第二个 fork 可以链式执行:
伪代码:
fin = open("input", read)
pipeIO = pipe()
fout = open("output", write)
fin2 = fcntrl(F_DUPFD_CLOEXEC, fin, 3); close fin
pipeI2 = fcntrl(F_DUPFD_CLOEXEC, pipeI, 3); close pipeI
pipeO2 = fcntrl(F_DUPFD_CLOEXEC, pipeO, 3); close pipeO
fout2 = fcntrl(F_DUPFD_CLOEXEC, fout, 3); close fout
fork
{
dup(fin2, 0)
dup(pipeO2, 1)
exec Command1
}
dup(pipeI2, 0)
dup(out2, 1)
exec Command2
您清楚地知道,当您 fork 时,子进程有效地复制了所有句柄,并且在代码实际执行时,只需要打开一个版本。父进程或子进程必须关闭它不感兴趣的每个句柄。通过使用 CLOEXEC,我们减少了这种需求,但如果您想将父进程保持为监视器,那么它需要关闭所有这些句柄的所有副本.
dup 函数的简要介绍:
0 = 管道(句柄 [2])
这将创建一对作为输入和输出相互连接的句柄。经典地,您可以通过一个进程写入 fork 的一个句柄和另一个进程从另一个句柄中读取来与 fork 通信,无论您想使用哪种方式。在这种情况下,我们使用管道在两个子进程之间进行通信,而不涉及父进程。
句柄 = dup(句柄)
这会在最低的未使用插槽处创建一个重复的句柄。如果您的程序已关闭 stdin/stdout/stderr,它将重用它们,这将是有问题的。此外,这些句柄在 exec 调用中保持活动状态。
句柄 = dup2(句柄,trghandle)
这会在您指定的索引处创建一个重复的句柄。它会默默地关闭任何现有的 trghandle 使用,这既有用又烦人!
handle = dup3(handle,trghandle, flags)
这会在您指定的索引处创建一个重复的句柄。它将默默地关闭任何现有的 trghandle 使用,这既有用又烦人!您可以指定诸如 CLOEXEC 之类的标志,它会在您调用 exec 时自动关闭句柄。 CLOEXEC 在避免显式关闭文件方面非常有用。
int fcntl(int fd, int cmd, ... /* arg */ );
F_DUPFD (int) 使用大于或等于 arg 的最小编号可用文件描述符复制文件描述符 fd。
F_DUPFD_CLOEXEC (int; since Linux 2.6.24) 与 F_DUPFD 相同,但另外为重复文件描述符设置 close-on-exec 标志。
...这使得避免标准输入/标准输出变得容易。