Изменение поведения при использовании именованного канала с фоновым процессом

#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;
  

Теперь то, что я ожидал от этого скрипта, это:

  1. введите a_or_b и, следовательно, выведите func begin
  2. войдите в цикл и, следовательно, выведите loop begins
  3. прочитайте EOF из stdin и, следовательно, из pipe (поскольку я ничего не писал в pipe ) и, следовательно, распечатайте 1 как $?
  4. не совпадает ни в одном из предложений 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; в первую строку в среднем регистре, то это также сработало бы.