Введение
Во-первых, объяснение можно найти здесь .
В любом случае, вот моя мысль ...
Когда используется трубакак это:
a | b
И a, и b выполняются одновременно .b ожидает стандартного ввода от.
Говоря о Errno::EPIPE
, справочная страница Linux с записью говорит:
EPIPE fd подключен к каналуили сокет, конец чтения которого закрыт.Когда это происходит, процесс записи также получит сигнал SIGPIPE.(Таким образом, возвращаемое значение записи отображается только в том случае, если программа перехватывает, блокирует или игнорирует этот сигнал.)
Говоря о проблеме в вопросе: при запуске программы whoami
она завершаетсяи больше не принимает стандартные входные данные, которые отправляет программа ruby hello.rb
, что приводит к разрыву канала.
Здесь я написал 2 программы ruby с именами p.rb и q.rb, чтобы проверить, что:
#!/usr/bin/env ruby
print ?* * 100_000
#!/usr/bin/ruby
exit! 0
Бег:
bash[~] $ ruby p.rb | ruby q.rb
Traceback (most recent call last):
2: from p.rb:2:in `<main>'
1: from p.rb:2:in `print'
p.rb:2:in `write': Broken pipe @ io_write - <STDOUT> (Errno::EPIPE)
Давайте немного изменим код q.rb , чтобы он принимал входные данные:
#!/usr/bin/ruby -w
STDIN.gets
Запуск:
bash[~] $ ruby p.rb | ruby q.rb
Правильно, на самом деле ничего не отображается.Причина в том, что q.rb теперь ждет стандартных вводов. По-видимому, здесь важнее всего ожидание .Теперь p.rb не будет зависать даже с STDOUT.sync
или STDOUT.flush
при передаче по этому q.rb.
Другой пример:
STDOUT.sync = true
loop until print("\e[2K<<<#{Time.now.strftime('%H:%M:%S:%2N')}>>>\r")
[предупреждение: цикл без сна может увеличить загрузку вашего процессора]
sleep 3
Запуск:
bash[~] $ time ruby p.rb | q.rb
Traceback (most recent call last):
2: from p.rb:2:in `<main>'
1: from p.rb:2:in `print'
p.rb:2:in `write': Broken pipe @ io_write - <STDOUT> (Errno::EPIPE)
real 0m3.186s
user 0m0.282s
sys 0m0.083s
Вы видите, что программа потерпела крах через 3 секунды.Он потерпит крах через 5,1 секунды, если у q.rb было sleep 5
.Точно так же sleep 0
in q.rb завершится сбоем p.rb через 0,1 секунды.Я предполагаю, что дополнительные 0,1 секунды зависят от системы, потому что моей системе требуется 0,1 секунды для загрузки интерпретатора ruby.
Я написал программы Crystal для p.cr и q.cr для тестирования.Crystal скомпилирован и не загружается долго 0,1 секунды.
Программы Crystal:
STDOUT.sync = true
loop do print("\e[2KHi!\r") end rescue exit
sleep 3
Я скомпилировал их и запустил:
bash[~] $ time ./p | ./q
real 0m3.013s
user 0m0.007s
sys 0m0.019s
Двоичный файл . / P , очень близко к 3 секундам, обрабатывает Unhandled exception: Error writing file: Broken pipe (Errno)
и завершает работу.Опять же, для выполнения двух кристаллических программ может потребоваться 0,01 секунды, и, возможно, ядру также требуется немного времени для запуска процессов.
Также обратите внимание, что STDERR#print
, STDERR#puts
, STDERR#putc
, STDERR#printf
, STDERR#write
, STDERR#syswrite
не вызывает Errno :: EPIPE, даже если выход синхронизирован.
Заключение
Труба arcane .Установка STDOUT#sync
в значение true или использование STDOUT#flush
сбрасывает все буферизованные данные в базовую операционную систему.
При запуске hello.rb | whoami
без синхронизации я могу записать 8191 байт данных, а программа hello.rb не вылетает.Но с синхронизацией запись 1 байта через канал приведет к сбою hello.rb .
Так что, когда hello.rb синхронизирует стандартные выходные данные с переданной по каналу программой whoami
, иwhoami
не ждет hello.rb ; hello.rb повышает Errno::EPIPE
, потому что канал между этими двумя программами сломан (поправьте меня, если я здесь потерялся).