Як працюють труби в Linux


25

Я читав про те, як труби реалізовані в ядрі Linux, і хотів підтвердити своє розуміння. Якщо я невірний, буде обрана відповідь з правильним поясненням.

  • У Linux є VFS під назвою pipefs, який встановлюється в ядрі (не в просторі користувача)
  • pipefs має єдиний супер блок і встановлюється під його власним root ( pipe:) поряд/
  • pipefs неможливо переглядати безпосередньо на відміну від більшості файлових систем
  • Вхід до pipefs здійснюється через pipe(2)системну виклик
  • pipe(2)Системний виклик , який використовується оболонками для трубопроводів з |оператором (вручну або з будь-якого іншого процесу) створює новий файл в pipefs , який веде себе дуже схоже на звичайний файл
  • Файл з лівого боку оператора труби stdoutперенаправлений на тимчасовий файл, створений у pipefs
  • Файл у правій частині оператора труби stdinвстановлений на файл на pipefs
  • pipefs зберігається в пам'яті і через деяку магію ядра, вона не повинна бути підписана на сторінку

Чи є це пояснення того, як труби (наприклад ls -la | less) функціонують досить правильно?

Я не розумію, як щось на зразок bash встановило б процес " stdinабо stdoutдескриптор файлів, повернутий pipe(2). Я про це ще нічого не міг знайти.


Зауважте, що ви говорите про два суттєво різних шари речей з однаковою назвою. pipe()Виклик ядра поряд з технікою , яка підтримує його ( pipefsі т.д.) значно нижчий рівень , ніж |оператор , що надається в оболонці. Останній зазвичай реалізується за допомогою першого, але цього не повинно бути.
Грег Хьюгілл

Так, я конкретно маю на увазі операції нижчого рівня з припущенням, що |оператор просто викликає pipe(2)як процес, як це робить bash.
Брендон Вамбольдт

Відповіді:


19

Ваш аналіз поки що, як правило, правильний. Те, як оболонка може встановити stdin процесу на дескриптор труби, може бути (псевдокод):

pipe(p) // create a new pipe with two handles p[0] and p[1]
fork() // spawn a child process
    close(p[0]) // close the write end of the pipe in the child
    dup2(p[1], 0) // duplicate the pipe descriptor on top of fd 0 (stdin)
    close(p[1]) // close the other pipe descriptor
    exec() // run a new process with the new descriptors in place

Спасибі! Цікаво, чому dup2потрібен дзвінок, і ви не можете просто призначити дескриптор труби stdin?
Брендон Вамбольдт

3
Абонент не може вибрати, яке числове значення дескриптора файлу створюється в ньому pipe(). dup2()Виклику дозволяє абоненту скопіювати дескриптор файлу певного числового значення (необхідно тому , що 0, 1, 2 STDIN, стандартний висновок, STDERR). Це еквівалент ядра "присвоєння безпосередньо stdin". Зауважимо, що глобальна змінна бібліотека виконання C stdinє a FILE *, яка не пов'язана з ядром (хоча вона ініціалізована для підключення до дескриптора 0).
Грег Хьюгілл

Чудова відповідь! Я трохи загубився в деталях. Просто цікаво, чому ви робите закриття (p [1]) перед запуском exec ()? Після повернення dup2 не буде p [1] вказувати на fd 0? Тоді закриття (p [1]) закриває дескриптор файлу 0. Тоді як ми можемо читати з stdin дочірнього процесу?
користувач1559897

@ user1559897: dup2Дзвінок не змінюється p[1]. Натомість він робить дві ручки p[1]і 0вказує на один і той же об’єкт ядра (трубу). Оскільки дочірньому процесу не потрібні дві ручки stdin (і не знаю, що таке нумерована ручка p[1]), p[1]це закрито раніше exec.
Грег Хьюгілл

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