Розуміння трубних команд в Unix / Linux


16

У мене є дві прості програми: Aі B. Aзапускається спочатку, потім Bотримує "stdout" Aі використовує його як "stdin". Припустимо, я використовую операційну систему GNU / Linux, і найпростіший можливий спосіб це зробити:

./A | ./B

Якби мені довелося описати цю команду, я б сказав, що це команда, яка приймає дані (тобто читає) від виробника ( A) і записує до споживача ( B). Це правильний опис? Я щось пропускаю?



Це не команда, це об'єкт kenerl, створений процесом bash, який використовується як stdout процесу A і stdin як B. Два процеси запускаються майже одночасно.
炸鱼 薯条 德里克

1
@ 炸鱼 Ти прав, бо трубопровід ядра є об’єктом у файловій системі pipefs, але що стосується самої оболонки - технічно це командова конвеєра
Сергій Колодяжний,

Відповіді:


26

Єдине, що у вашому питанні не стоїть як неправильне - це ви сказали

Спочатку біжить A, тоді B отримує відтінок A

Насправді обидві програми будуть запускатися майже в один і той же час. Якщо для Bспроби читання немає вводу , він блокується, поки не буде введення для читання. Так само, якщо нікого не читає вихід з нього A, його запис блокується, поки його вихід не буде прочитаний (частина його буде забудована трубою).

Єдине, що синхронізує процеси, які беруть участь у трубопроводі, - це введення-виведення, тобто читання та запис по трубі. Якщо жодного письма чи читання не відбувається, то два процеси працюватимуть абсолютно незалежно один від одного. Якщо хтось ігнорує читання або запис іншого, ігнорований процес блокується і в кінцевому підсумку буде вбитий SIGPIPEсигналом (якщо він пише) або отримає стан закінчення файлу у своєму стандартному потоці введення (якщо читання), коли інший процес припиняється .

Ідіоматичний спосіб описати A | B- це трубопровід, що містить дві програми. Вихід, отриманий на стандартному виході з першої програми, доступний для зчитування на стандартному вході другою ("[висновок] Aвводиться у [вхід]] B). Раковина робить необхідну сантехніку, щоб це могло статися.

Якщо ви хочете вживати слова "споживач" та "виробник", я вважаю, що це теж нормально.

Те, що це програми, написані на С, не має значення. Те, що це Linux, macOS, OpenBSD або AIX, не має значення.


2
Запис у тимчасовий файл використовувався в DOS, оскільки він не підтримував декілька процесів.
CSM

2
@AlexVong Зауважте, що ваш приклад із тимчасовим файлом не зовсім еквівалентний. Програма може шукати хоч вміст файлу, але дані, що виходять з каналу, не є доступними. Кращим іспитом було б скористатися mkfifoдля створення названої труби, потім запустити B у фоновому режимі читання з труби, а потім A на ньому. Це, однак, не забирає ніт, оскільки ефект був би однаковий.
Kusalananda

2
@AlexVong У цій статті спрощення відокремлюють його від реальних трубопроводів; паралельне виконання справді смислове, а не оптимізаційне. Це розумне пояснення неправдивим дітям монадичної оцінки або складу для того, хто бачив оболонки трубопроводів, але це не вірно в іншому напрямку. Версія FOS від Kusalananda ближче, але частини моделі поширення помилок є справді важливими і не можна повторити. (про все, що я говорю як хтось, хто дуже перебуває на "оболонках трубопроводів - це лише функціональна композиція")
Майкл Гомер

6
@AlexVong Ні, це зовсім поза увагою . Це не в змозі пояснити навіть щось просте на кшталт yes | sed 10q
дядько Біллі

1
@UncleBilly Я згоден з вашим прикладом. Це свідчить про те, що паралельне виконання дійсно потрібно також відзначити Майклом. В іншому випадку ми отримаємо не припинення.
Алекс Вонг

2

Термін, який зазвичай використовується в документації, - це "конвеєр", який складається з однієї або декількох команд. Див. Визначення POSIX. Отже, технічно кажучи, це дві команди, дві підпроцеси для оболонки (або fork()+exec()"зовнішні команди, або підпакети")

Що стосується частини виробника-споживача , трубопровід можна описати за цією схемою, оскільки:

  • Виробник та споживач діляться буфером фіксованого розміру, принаймні для Linux та MacOS X є фіксований розмір буфера конвеєра
  • Виробник і Споживач є зв'язаними між собою, команди в конвеєрі не знають про існування один одного (якщо тільки вони активно не перевіряють /proc/<pid>/fdкаталог).
  • Виробники пишуть, stdoutа споживачі читають stdinтак, ніби виконується одна команда, але вони можуть існувати один без одного .

Відмінність, яку я бачу тут, полягає в тому, що на відміну від виробника-споживача в інших мовах команди оболонок використовують буферизацію і вони записують stdout, коли буфер заповнений, але немає жодної згадки, що виробник-споживач повинен дотримуватися цього правила - лише чекайте, коли черга заповнена або відкинеться дані (це щось інше, що конвеєр не робить).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.