Названі труби, дескриптори файлів та EOF


10

Два вікна, той самий користувач, з bash-підказками. У вікні-1 тип:

$ mkfifo f; exec <f

Отже, bash зараз намагається прочитати з дескриптора файлів 0, який відображається на ім'я pipe f. У вікні-2 введіть:

$ echo ls > f

Тепер вікно-1 друкує ls, а потім оболонка відмирає. Чому?

Наступний експеримент: знову відкрийте вікно-1 за допомогою exec <f. У вікні-2 введіть:

$ exec 3>f
$ echo ls >&3

Після першого рядка вище вікно-1 прокидається і друкує підказку. Чому? Після другого рядка вище, вікно-1 друкує lsвихід, і оболонка залишається живою. Чому? Насправді зараз у вікні-2 echo ls > fне закривається оболонка вікна-1.

Відповідь повинна мати відношення до існування дескриптора файлу 3 з вікна-2 із посиланням на названу трубку ?!


1
Після того, як exec <f, bashне намагається прочитати з fйого спочатку намагається відкрити його. open()Не повернусь до тих пір, поки який - то процес робить інші відкрита в режимі запису в трубу (в цей момент труба буде екземпляр, і оболонка буде зчитувати вхідні дані з нього).
Стефан Шазелас

Чудовий момент, @ StéphaneChazelas. Це повинно бути, чому після exec 3>fзапуску перша оболонка надає підказку. (Дрібниця, ви мали в виду «в запису режимі» у Вашому коментарі?)
Fixee

1
так вибачте Відредаговано зараз перед 5-хвилинним терміном
Стефан Шазелас

Відповіді:


12

Це пов'язано із закриттям дескриптора файлів.

У першому прикладі echoзаписує у свій стандартний вихідний потік, з яким відкривається оболонка, щоб зв'язати його f, а коли він закінчується, його дескриптор закривається (оболонкою). На приймальному кінці оболонка, яка зчитує вхід зі свого стандартного вхідного потоку (підключений до f), зчитує ls, запускається lsі потім припиняється через стан закінчення файлу на його стандартному вході.

Умова кінця файлу виникає тому, що всі автори з названою трубою (лише один у цьому прикладі) закрили свій кінець труби.

У вашому другому прикладі exec 3>fвідкривається дескриптор файлу 3 для запису f, а потім echoзаписується lsв нього. Це оболонка, де відкрито дескриптор файлів, а не echoкоманда. Дескриптор залишається відкритим, поки ви цього не зробите exec 3>&-. На приймальному кінці оболонка, яка зчитує вхід зі свого стандартного вхідного потоку (підключений до f), читає ls, запускається lsі потім чекає додаткового введення (оскільки потік все ще відкритий).

Потік залишається відкритим, тому що всі автори до нього (оболонка, через exec 3>fі та echo) не закрили свій кінець труби ( exec 3>fдіє досі).


Я вже писав про echoвище, ніби це була зовнішня команда. Це, швидше за все, вбудований в оболонку. Ефект, тим не менше.


6

Це не так багато: коли немає авторів на трубу, він виглядає закритим для читачів, тобто повертає EOF при читанні і блокує при відкритті.

Зі сторінки man в Linux ( pipe(7)але дивіться також fifo(7)):

Якщо всі дескриптори файлів, що відносяться до кінця запису труби, були закриті, то спроба read(2)з труби побачить кінець файлу ( read(2)поверне 0).

Закриття кінця запису - це те, що неявно відбувається в кінці echo ls >f, а в іншому випадку дескриптор файлу залишається відкритим.


Здається, подібний до числа посилань на Java (та інших мовах ОО)! Має сенс, хоча.
Fixee

2

Прочитавши дві відповіді від @Kusalananda та @ikkachu, я думаю, що я розумію. У вікні-1 оболонка чекає, щоби відкрити кінець запису труби, а потім закрити її. Після відкриття кінця запису оболонка у вікні-1 надрукує підказку. Після закінчення запису закінчується оболонка EOF і гине.

На вікні-2 у нас є дві ситуації, описані в моєму запитанні: у першій ситуації з echo ls > f, дескриптора файлу 3 немає, тому ми echoнерестуємо і його, stdinі stdoutвиглядаємо так:

0 --> tty
1 --> f

Потім echoзакінчується і оболонка закриває обидва дескриптори. Оскільки дескриптор 1 файлу закритий і посилається f, кінець запису fзакритий і це викликає EOF до вікна-1.

У другій ситуації ми запускаємося exec 3>fв свою оболонку, внаслідок чого оболонка приймає це середовище:

bash:
0 --> tty
1 --> tty
2 --> tty
3 --> f

Тепер ми запускаємо echo ls >& 3і оболонка виділяє дескриптори файлів echoнаступним чином:

echo:
0 --> tty
1 --> f     # because 3 points to f
2 --> tty

Потім оболонка закриває три дескриптори вище, включаючи f, але fвсе ще має посилання на неї від самої оболонки. У цьому важлива відмінність. Закриття дескриптора 3 з exec 3>&-закриває останню відкриту посилання і спричинятиме EOF до вікна-1, як зазначав @Kusalananda.


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

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