我试图了解在管道中运行时启动子进程的程序的行为。
这个 bash 程序 fork.sh 立即打印并返回:
(sleep 1) &
echo 'here'
但是当连接到管道时,读取端似乎等待睡眠完成。
$ time bash fork.sh | wc
1 1 5
real 0m1.014s
我也在 Ruby 中尝试过此操作,并进行了一些额外的调用来尝试防止睡眠阻塞:
Process.detach(fork { sleep 1 })
puts 'here'
fork {
sleep 1
Process.daemon
}
puts 'here'
但他们的行为是一样的。
我想知道是什么原因导致这个问题(就 Unix、文件描述符等而言),以及是否有办法重写其中任何一个,以便管道在一秒钟内返回。
编辑:下面的答案帮助我注意到 Ruby 示例的问题:调用daemon()
必须首先进行。我原以为它以某种方式应用于整个过程。
答案1
- 子进程从其父进程继承所有文件描述符。
- 执行命令时(就像您
sleep
这里假设您的 shell 没有内置命令一样),只有标有执行时关闭标志已关闭,但 shell 从未在 stdout (fd 1) 上设置该标志。 - 管道读者仅当以下情况时才会得到 EOF全部指向其写入端的文件描述符已被关闭。
time bash fork.sh | wc
您应该让您的sleep
进程(从 开始fork.sh
)放弃其标准输出,该输出指向wc
正在读取的管道的写入端;在fork.sh
:
(sleep 1 >/dev/null) &
echo 'here'
在这种情况下sleep .. >&-
(关闭标准输出,而不将其重定向到其他地方)也可以工作,但我一般不建议这样做,因为如果进程随后打开一些文件,返回的文件描述符将为 1 = 标准输出,这可能会中断假设并触发错误。