#bash #background-process #named-pipes
#bash #фоновый процесс #именованные каналы
Вопрос:
Мне трудно понять именованные каналы. У меня есть следующий скрипт:
#!/bin/bash
function a_or_b {
echo 'func begins'
while true; do
echo 'loop begins'
read -s -n 1; echo $?
case $REPLY in
'a') return 0 ;;
'b') return 1 ;;
esac
done
echo 'func ends'
}
mkfifo pipe
a_or_b <pipe amp;
Теперь то, что я ожидал от этого скрипта, это:
- введите
a_or_b
и, следовательно, выведитеfunc begin
- войдите в цикл и, следовательно, выведите
loop begins
- прочитайте EOF из stdin и, следовательно, из
pipe
(поскольку я ничего не писал вpipe
) и, следовательно, распечатайте1
как$?
- не совпадает ни в одном из предложений case и, следовательно, вернитесь к шагу 2
Однако при запуске этого скрипта я не получаю выходных данных, и мой терминал просто печатает следующее приглашение.
Если я перенаправлю echo в pipe
перед вызовом a_or_b
:
...
mkfifo pipe
echo 'x' > pipe
a_or_b <pipe amp;
… скрипт не прекращает выполнение, и я могу продолжать вводить символы (включая a
и ‘b’) в терминале безрезультатно. Итак, я должен завершить сценарий, используя ^ C.
Если я перенаправлю echo на pipe
после вызова a_or_b
:
...
mkfifo pipe
a_or_b <pipe amp;
echo 'x' > pipe
… Я получаю следующий вывод:
func begins
loop begins
0
loop begins
0
loop begins
1
loop begins
1
loop begins
1
Это в основном поведение, которого я ожидал от того, что ничего не повторял pipe
. Функция запускается, переходит в цикл, считывает символы x
и n
из echo
(соответствующие двум 0
s в выходных данных), а затем продолжает цикл вечно, не в состоянии прочитать ни одного символа. И если я повторяю a
или b
в канал, функция завершается.
Что вызывает все эти различия в поведении?
Комментарии:
1. Простое открытие конца канала для чтения, фактически не пытаясь читать из него, блокирует процесс до тех пор, пока другой конец не будет открыт для записи;
Ответ №1:
a_or_b <pipe amp;
Перенаправления обрабатываются перед запуском команд. Оболочка блокирует попытку открытия pipe
для чтения. Со страницы руководства mkfifo (3):
Открытие FIFO для чтения обычно блокируется до тех пор, пока какой-либо другой процесс не откроет тот же FIFO для записи, и наоборот.
Оболочка не может продолжить работу, пока другой процесс не откроет FIFO для записи. Только тогда он завершит настройку перенаправления и фактически вызовет a_or_b
.
echo 'x' > pipe
a_or_b <pipe amp;
К сожалению, это имеет ту же самую, но обратную проблему. Оболочка не может продолжить > pipe
перенаправление, пока другой процесс не откроет FIFO для чтения. Он никогда не добирается ни до echo
, ни до второй строки, которая считывалась бы из канала.
a_or_b <pipe amp;
echo 'x' > pipe
Надеюсь, теперь вы можете понять, почему работает эта версия. Фоновая оболочка пытается считывать данные из канала и блокирует. Оболочка переднего плана, отдельный процесс, выполняет запись в него. Ага! Два процесса открыли канал, один для чтения, а другой для записи. Теперь они оба могут продолжить. Птицы поют, и все счастливы.
Комментарии:
1.
mkfifo
На странице руководства указано, что «[FIFO] должен быть открыт на обоих концах одновременно, прежде чем вы сможете приступить к выполнению каких-либо операций ввода или вывода на нем. Открытие FIFO для чтения обычно блокируется до тех пор, пока какой-либо другой процесс не откроет тот же FIFO для записи, и наоборот.» Почему версия the first-function-call-then-echo-to-pipe удовлетворяет этому требованию? И почему версия first-echo-to-pipe-then-function-call не работает? Наверное, я не понимаю, что вы подразумеваете под «блоками оболочки, пытающимися открытьсяpipe
для чтения».2. Из-за
amp;
. В рабочем канале первая строка выполняется в фоновом режиме, поэтому тот факт, что он блокируется, не останавливает выполнение второй строки. Фоновая обработка заблокирована, и это нормально.3. Если бы вы переместили
amp;
в первую строку в среднем регистре, то это также сработало бы.