2013-11-22 4 views
1

Итак, я получил это одна строка сценария:Нарушение сценариев оболочки; Что происходит под капотом?

echo test | cat | grep test 

Не могли бы вы объяснить мне, как именно это будет работать с учетом следующих системных вызовов: труба(), вилка(), Exec() и dup2 ()?

Я ищу общий обзор здесь и в основном последовательность операций. Что я знаю до сих пор, так это то, что оболочка будет использовать fork с помощью fork(), а код скрипта заменит оболочку с помощью exec(). Но как насчет трубы и dup2? Как они падают на место?

Заранее благодарен.

+0

Обратите внимание, что вам также необходимо вызвать 'close()', поэтому на ваш список системных вызовов на самом деле недостаточно для реализации конвейера. – user4815162342

ответ

6

Сначала рассмотрим более простой пример, такие как:

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, которые работают, сначала дублируя кусок материи в отдаленном месте, а затем распадая оригинал.

Если вы все еще следуя теории, порядок операций, выполняемых в оболочке должны теперь быть ясно:

  1. Оболочка создает трубу, а затем fork два процесса, оба из которых унаследует файловые дескрипторы файлов, r и w.

  2. В подпроцессе о выполнить echo, оболочка вызывает dup2(1, w); close(w) перед exec для того, чтобы перенаправить стандартный вывод до конца записи трубы.

  3. В подпроцессе, который должен выполнить cat, оболочка вызывает dup2(0, r); close(r), чтобы перенаправить стандартный ввод на считываемый конец трубы.

  4. После формования основной процесс оболочки должен сам закрыть оба конца трубы. Одна из причин заключается в том, чтобы освободить ресурсы, связанные с трубой, после выхода подпроцессов. Другой способ разрешить фактическое прекращение работы cat - считыватель трубок получит EOF только после того, как все копии конца записи трубы закрыты. В приведенных выше шагах мы закрыли избыточную копию ребенка конца записи, дескриптор файла 15, сразу после его дублирования до 1. Но дескриптор файла 15 также должен существовать в родительском, поскольку он был унаследован под этим номером и может только закрывается родителем. В противном случае это приведет к тому, что стандартный входной сигнал cat никогда не сообщает об EOF, а также его процесс cat.

Этот механизм легко обобщает его на три или более процесса, соединенных трубами. В случае трех процессов трубам необходимо упорядочить вывод echo на вход cat, а выход cat записывается на вход grep. Для этого требуется два вызова pipe(), три вызовов на fork(), четыре вызовов в dup2() и close (один для echo и grep и два для cat), три звонков в exec(), а также четыре дополнительных вызовов к close() (два для каждой трубы).

+0

. Должно ли быть 4 вызова dup2() в случае трех процессов? Например, вызов 1: перенаправление эхо-выхода, вызов 2: переадресация ввода кота, вызов 3: переадресация вывода кота, вызов 4: перенаправление grep-входа. – kstratis

+0

@ Konos5 Вы правы; Я уже исправил ответ. – user4815162342

Смежные вопросы