Почему труба "да | спать 10" не выходит из строя - PullRequest
0 голосов
/ 22 сентября 2018

Размышляя о том, как реализовать определенную функцию в одной из моих собственных программ, мне было интересно, как bash внутренне обрабатывает каналы следующего характера:

yes | sleep 10

Это, очевидно, ничего не делает, но я не делаю 'не понимаю, как это не приводит к ошибке.Я бы подумал, что либо:

  • , поскольку sleep не читает из stdin, канал, соединяющий оба процесса, заполняется и заставляет yes блокироваться бесконечно при попытке записи.к теперь заполненному каналу

  • , если используется неблокирующий ввод-вывод, ошибки должны возникать, если сначала выполняется yes и выполняется запись в канал до того, как процесс sleep будет запущен итаким образом, ни один процесс не связан с читаемым концом канала

Я полагаю, это мое серьезное недоразумение.Я пытался посмотреть исходный код bash, но это не помогло мне.

1 Ответ

0 голосов
/ 22 сентября 2018

Вот что на самом деле происходит, когда вы запускаете команду оболочки yes | sleep 10.

Сначала оболочка создает анонимный канал , используя системный вызов pipe .Системный вызов pipe открывает два файловых дескриптора, которые являются концом чтения и концом записи канала.Все, что записывается в конец записи, становится доступным для чтения из конца чтения.

После этого оболочка создает два дочерних процесса, используя системный вызов fork .Два дочерних элемента работают параллельно.

  • В одном дочернем объекте оболочка подключает конец записи канала к стандартному выводу и закрывает конец чтения.Затем оболочка вызывает системный вызов execve , чтобы заменить изображение кода в этом процессе на кодовое изображение yes.
    Программа yes записывает в канал столько времени, сколько может,Если на конце чтения канала нет активного read вызова, вызов write просто блокируется.(На самом деле есть небольшой буфер, который write будет заполняться перед блокировкой, но здесь это не имеет значения.)
  • В другом дочернем объекте оболочка соединяет конец чтения канала канала со стандартным вводом и закрываетконец записи.Затем оболочка вызывает системный вызов execve, чтобы заменить изображение кода в этом процессе на изображение кода sleep.Программа sleep ничего не делает в течение 10 секунд.
  • Исходный процесс оболочки закрывает оба конца канала и ожидает выхода обоих дочерних элементов (используя системный вызов wait ).

По истечении 10 секунд процесс, запущенный sleep, завершается.На этом этапе конец чтения канала больше не открыт ни в одном процессе.Когда процесс пытается выполнить запись в канал, у которого конец чтения не открыт ни в одном процессе, ядро ​​отправляет в процесс записи сигнал SIGPIPE.Таким образом, процесс, выполняющий yes, прерывается сигналом SIGPIPE.

В этот момент оболочка обнаруживает, что ее дочерние процессы по обе стороны конвейера завершены.Команда конвейера возвращает состояние правой стороны, которое равно 0 (sleep успешно завершается).

, поскольку sleep не читает из stdin, канал, соединяющий оба процесса, заполняется ивызвать yes, чтобы блокировать на неопределенный срок, когда он пытается выполнить запись в теперь заполненный каналвыполняется первым и записывает данные в канал до того, как процесс ожидания будет запущен, и, таким образом, ни один процесс не будет подключен к концу чтения канала

Это неверно в нескольких местах.yes не использует неблокирующий ввод-вывод.Он выполняется параллельно с sleep, а не первым.Никогда не наступает момент времени, когда ни один процесс не будет подключен к концу чтения канала, только до выхода sleep.В зависимости от времени, возможно, что yes начнет запись до того, как sleep начнет выполняться, возможно, даже до того, как дочерний процесс для программы sleep будет разветвлен, но конец чтения стал открытым, когда вернулся вызов pipe,в то время как конец записи стал открытым.

...