Сначала рассмотрим более простой пример, такие как:
echo test | cat
То, что мы хотим, чтобы выполнить echo
в отдельном процессе, организуя свой стандартный вывод, чтобы быть отведен в стандартный ввод выполняющегося процесса cat
. В идеале эта утечка, после установки, не потребует дальнейшего вмешательства оболочки - оболочка просто спокойно ждет выхода обоих процессов.
Механизм достижения этого называется «трубой». Это устройство межпроцессного взаимодействия, реализованное в ядре и экспортированное в пользовательское пространство. После создания программой Unix в трубе появляется пара дескрипторов файлов со своеобразным свойством, которое, если вы записываете в один из них, вы можете читать одни и те же данные из другого. Это не очень полезно в рамках одного и того же процесса, но имейте в виду, что файловые дескрипторы, включая, но не ограничиваясь, каналы, наследуются по fork()
и даже по exec()
. Это делает трубу простой в установке и разумно эффективный механизм IPC.
Оболочка создает трубу и теперь имеет набор дескрипторов файлов, принадлежащих трубу, один для чтения и один для записи. Эти файловые дескрипторы наследуются обе разветвленными подпроцессами. Теперь, только если echo
записывали в дескриптор дескриптора трубы вместо его фактического стандартного вывода, и если cat
читали из дескриптора конца канала, а не из его стандартного ввода, все могло бы работать. Но они этого не делают, и именно здесь вступает в игру dup2
.
dup2
дублирует файловый дескриптор в качестве другого дескриптора файла, автоматически закрывая новый дескриптор заранее. Например, dup2(1, 15)
закроет дескриптор файла 1 (по соглашению, используемому для стандартного вывода), и снова откроет его как копию дескриптора файла 15 - это означает, что запись на стандартный вывод фактически будет эквивалентна записи в дескриптор файла 15. это также относится к чтению: dup2(0, 8)
сделает чтение из дескриптора файла 0 (стандартный ввод) эквивалентным чтению из дескриптора файла 8. Если мы приступим к закрытию исходного дескриптора файла, открытый файл (или труба) будет эффективно перемещен из оригинальный дескриптор для нового, очень похожий на телескопы sci-fi, которые работают, сначала дублируя кусок материи в отдаленном месте, а затем распадая оригинал.
Если вы все еще следуя теории, порядок операций, выполняемых в оболочке должны теперь быть ясно:
Оболочка создает трубу, а затем fork
два процесса, оба из которых унаследует файловые дескрипторы файлов, r
и w
.
В подпроцессе о выполнить echo
, оболочка вызывает dup2(1, w); close(w)
перед exec
для того, чтобы перенаправить стандартный вывод до конца записи трубы.
В подпроцессе, который должен выполнить cat
, оболочка вызывает dup2(0, r); close(r)
, чтобы перенаправить стандартный ввод на считываемый конец трубы.
После формования основной процесс оболочки должен сам закрыть оба конца трубы. Одна из причин заключается в том, чтобы освободить ресурсы, связанные с трубой, после выхода подпроцессов. Другой способ разрешить фактическое прекращение работы cat
- считыватель трубок получит EOF только после того, как все копии конца записи трубы закрыты. В приведенных выше шагах мы закрыли избыточную копию ребенка конца записи, дескриптор файла 15, сразу после его дублирования до 1. Но дескриптор файла 15 также должен существовать в родительском, поскольку он был унаследован под этим номером и может только закрывается родителем. В противном случае это приведет к тому, что стандартный входной сигнал cat
никогда не сообщает об EOF, а также его процесс cat
.
Этот механизм легко обобщает его на три или более процесса, соединенных трубами. В случае трех процессов трубам необходимо упорядочить вывод echo
на вход cat
, а выход cat
записывается на вход grep
. Для этого требуется два вызова pipe()
, три вызовов на fork()
, четыре вызовов в dup2()
и close
(один для echo
и grep
и два для cat
), три звонков в exec()
, а также четыре дополнительных вызовов к close()
(два для каждой трубы).
Обратите внимание, что вам также необходимо вызвать 'close()', поэтому на ваш список системных вызовов на самом деле недостаточно для реализации конвейера. – user4815162342