Перенаправление Bash: именованные каналы и EOF

Возьмите следующий код:

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

1 ответ

Решение

Проблема в подходе 3 заключается в том, что FIFO pipe затем имеет 2 писателя: скрипт bash (потому что вы открыли его для чтения / записи с помощью exec 3<>) и суб-оболочка работает foo, Вы прочтете EOF, когда все авторы закроют дескриптор файла. Один писатель (суб-оболочка работает foo) выйдет довольно быстро (примерно через 1 с) и, следовательно, закроет дескриптор файла. Другой писатель (основная оболочка) закрывает файловый дескриптор только при выходе из него, так как файловый дескриптор не закрывается 3 в любом месте. Но он не может выйти, потому что ждет cat выйти первым. Это тупик

  • cat ждет EOF
  • EOF появляется только тогда, когда основная оболочка закрывает FD (или выходит)
  • основная оболочка ждет cat прекратить

Поэтому ты никогда не выйдешь.

Случай 2 работает, потому что канал всегда имеет только один пишущий (под-оболочка работает foo), который выходит очень быстро, поэтому EOF будет прочитан. В случае 1 есть только один писатель, потому что вы открываете fd 3 только для чтения (exec 3<).

РЕДАКТИРОВАТЬ: Удалить бессмысленно о случае 4 не является правильным (см. Комментарии). Это правильно, потому что писатель не может выйти до того, как читатель подключится, потому что он также будет заблокирован при открытии файла, когда читатель еще не открывается. Недавно добавленный случай 4, к сожалению, неверен. Это круто и работает только тогда, когда foo не завершает (или закрывает трубу) раньше exec 3<pipe пробеги.

Также проверьте fifo(7) справочная страница:

Ядро поддерживает ровно один объект канала для каждого специального файла FIFO, который открывается как минимум одним процессом. FIFO должен быть открыт с обоих концов (чтение и запись), прежде чем данные могут быть переданы. Как правило, открытие блоков FIFO до открытия другого конца также.

Другие вопросы по тегам