2

采取以下代码:

rm -f pipe
mkfifo pipe

foo () {
    echo 1
    sleep 1
    echo 2
}

#1
exec 3< <(foo &)
cat <&3 # works

#2
foo >pipe &
cat <pipe # works

#3
exec 3<>pipe
foo >&3 &
cat <&3 # hangs

#4 -- update: this is the correct approach for what I want to do
foo >pipe &
exec 3<pipe
rm pipe
cat <&3 # works

为什么方法 #3 挂起,而其他方法则没有?有没有办法让方法#3 不挂起?

理由:我希望使用准未命名管道来连接几个异步运行的子进程,为此我需要在文件描述符指向它之后删除管道:

mkfifo pipe
exec {fd}<>pipe
rm pipe
# use &$fd only
4

1 回答 1

2

The problem in approach 3 is that the FIFO pipe then has 2 writers: The bash script (because you have opened it read/write by using exec 3<>) and the sub-shell running foo. You'll read EOF when all writers have closed the file descriptor. One writer (the sub-shell running foo) will exit fairly quickly (after roughly 1s) and therefore close the file descriptor. The other writer however (the main shell) only closes the file descriptor when it'd exit as there's no closes of file descriptor 3 anywhere. But it can't exit because it waits for the cat to exit first. That's a deadlock:

  • cat is waiting for an EOF
  • the EOF only appears when the main shell closes the fd (or exits)
  • the main shell is waiting for cat to terminate

Therefore you'll never exit.

Case 2 works because the pipe only ever has one writer (the sub-shell running foo) which exits very quickly, therefore an EOF will be read. In case 1, there's also only ever one writer because you open fd 3 read-only (exec 3<).

EDIT: Remove nonsense about case 4 not being correct (see comments). It's correct because the writer can't exit before the reader connects because it'll also be blocked when opening the file when the reader isn't opening yet. The newly added case 4 is unfortunately incorrect. It's racy and only works iff foo doesn't terminate (or close the pipe) before exec 3<pipe runs.

Also check the fifo(7) man page:

The kernel maintains exactly one pipe object for each FIFO special file that is opened by at least one process. The FIFO must be opened on both ends (reading and writing) before data can be passed. Normally, opening the FIFO blocks until the other end is opened also.

于 2015-10-15T23:02:50.073 回答