В каком порядке работают команды по каналам?

89

Я никогда не задумывался о том, как на самом деле оболочка выполняет конвейерные команды. Я всегда говорили , что «стандартный вывод одной программы получает по трубопроводу в потоке ввод других,» как способ мышления о трубах. Естественно, я подумал, что в случае, скажем, A | B, A запускается первым, затем B получает стандартный вывод A и использует стандартный вывод A в качестве входных данных.

Но я заметил, что когда люди ищут определенный процесс в ps, они включают grep -v "grep" в конце команды, чтобы убедиться, что grep не появляется в конечном выводе. Это означает, что в команде ps aux | grep "bash" | grep -v "grep", что означает, что ps знал, что grep запущен и поэтому находится в выводе ps. Но если ps завершает работу до того, как его выходные данные будут переданы в grep, как он узнал, что grep работает?

flamingtoast@FTOAST-UBUNTU: ~$ ps | grep ".*"
PID TTY          TIME CMD
3773 pts/0    00:00:00 bash
3784 pts/0    00:00:00 ps
3785 pts/0    00:00:00 grep
action_potato
источник
почему бы не принять ответ?
törzsmókus

Ответы:

64

Команды по конвейеру выполняются одновременно. Когда вы запускаете ps | grep …, это удача ничьей (или вопрос деталей работы оболочки в сочетании с тонкой настройкой планировщика глубоко в недрах ядра) относительно того, начинается ли сначала psили grepначинается, и в любом случае они продолжают выполнить одновременно.

Это очень часто используется для того, чтобы вторая программа могла обрабатывать данные, полученные из первой программы, до того, как первая программа завершила свою работу. Например

grep pattern very-large-file | tr a-z A-Z

начинает отображать совпадающие строки в верхнем регистре еще до того, grepкак закончил обход большого файла.

grep pattern very-large-file | head -n 1

отображает первую совпадающую строку и может прекратить обработку задолго до того, grepкак закончит чтение своего входного файла.

Если вы где-то читали, что программы по конвейеру запускаются последовательно, бегите от этого документа. Канальные программы запускаются одновременно и всегда работают.

жилль
источник
7
И что хорошо в этом примере, так это то, что когда голова получает ту строку, которая ей нужна, она завершается, а когда grep замечает это, она также завершается, не выполняя ничего лишнего.
Джо
Я предполагаю, что есть какой-то буфер ввода-вывода относительно канала ... как я узнаю его размер в байтах? Что я хочу прочитать, чтобы узнать больше об этом? :)
n611x007
3
@naxa На самом деле есть два буфера. Там в STDIO буфер внутри grepпрограммы, и есть буфер под управлением ядра в самой трубе. Для последнего, посмотрите, Насколько велика труба буфера?
Жиль
49

Порядок выполнения команд на самом деле не имеет значения и не гарантируется. Оставив в стороне тайных детали pipe(), fork(), dup()и execve(), оболочка сначала создает трубу, трубопровод для данных , которые будут течь между процессами, а затем создает процессы с концами трубы , соединенной с ними. Первый запущенный процесс может блокировать ожидание ввода от второго процесса или блокировать ожидание, пока второй процесс начнет читать данные из канала. Эти ожидания могут быть сколь угодно длинными и не имеют значения. Независимо от порядка запуска процессов, данные в конечном итоге передаются, и все работает.

Кайл Джонс
источник
5
Хороший ответ, но ОП, похоже, считает, что процессы идут последовательно. Вы могли бы прояснить, что процессы выполняются одновременно, и труба похожа на ... трубу между ведрами, где вода течет через все в (приблизительно) в одно и то же время.
Кит
Спасибо за разъяснение. Источники, которые я читал, создавали впечатление, что программы по каналам запускаются последовательно, а не одновременно.
action_potato
Чтобы увидеть, как процессы начинаются неопределенным образом, попробуйте выполнить это 1000 раз: echo -na> & 2 | эхо b> & 2
Оле Тэндж
28

Риск избиения мертвой лошади, кажется, ошибочное мнение, что

    A | В

эквивалентно

    A > временный_файл 
    B < временный_файл 
    rm временный_файл

Но, когда Unix был создан, и дети ездили с динозаврами в школу, диски были очень маленькими, и довольно обычная команда использовала все свободное пространство в файловой системе. Если бы Bчто-то было похоже , конечный результат конвейера мог бы быть намного меньше, чем этот промежуточный файл. Таким образом, канал был разработан не как сокращение для модели « сначала запустить A , а затем запустить B с вводом из выходных данных A », а как способ одновременного выполнения и устранения необходимости сохранения промежуточного файла. на диске.grep some_very_obscure_stringBA

Скотт
источник
2
Это отвечает почему и, следовательно, получает мой голос.
Маленький Древний Лес Ками
1

Обычно вы запускаете это под Bash. процесс работает и запускается одновременно, но выполняется оболочкой параллельно. Как это возможно?

  1. если это не последняя команда в трубе, создайте безымянный канал с парой сокетов
  2. вилка
  3. в дочернем случае переназначьте stdin / stdout на сокеты, если это необходимо (для первого процесса в pipe stdin не переназначается, то же самое для последнего процесса и его stdout)
  4. В дочернем EXEC указана команда с аргументами, которые сметают оригинальный код оболочки, но оставляют все открытые им сокеты. ID дочернего процесса не будет изменен, потому что это тот же самый дочерний процесс
  5. одновременно с дочерним, но параллельно под основной оболочкой перейдите к шагу 1.

Система не гарантирует, насколько быстро будет выполняться exec, и запускается указанная команда. это не зависит от оболочки, но система. Это потому что:

ps auxww| grep ps | cat

один раз показать grepи / или psкоманду, а теперь следующий. Это зависит от того, насколько быстро ядро ​​действительно запускает процессы, используя функцию exec системы.

Znik
источник
1
Параллельное выполнение означает, что два или более процесса выполняются в одном и том же временном интервале, обычно с некоторой зависимостью между ними. Параллельное выполнение означает, что два или более процессов выполняются одновременно (например, на отдельных ядрах ЦП одновременно). Параллелизм не имеет отношения к этому вопросу, также как «как быстро» exec()выполняется, но как чередуютсяexec() вызовы и выполнение программ в конвейере .
Томас Найман