Bernama pipa, deskriptor file, dan EOF

10

Dua jendela, pengguna yang sama, dengan bash prompt. Di jendela-1 ketik:

$ mkfifo f; exec <f

Jadi bash sekarang mencoba membaca dari file descriptor 0, yang dipetakan ke pipa bernama f. Di jendela-2 ketik:

$ echo ls > f

Sekarang window-1 mencetak ls dan kemudian shellnya mati. Mengapa?

Eksperimen selanjutnya: buka window-1 lagi dengan exec <f. Di jendela-2 ketik:

$ exec 3>f
$ echo ls >&3

Setelah baris pertama di atas, jendela-1 bangun dan mencetak prompt. Mengapa? Setelah baris kedua di atas, window-1 mencetak lsoutput dan shell tetap hidup. Mengapa? Bahkan, sekarang di window-2, echo ls > ftidak menutup shell window-1.

Jawabannya pasti ada hubungannya dengan keberadaan file descriptor 3 dari window-2 yang mereferensikan pipa bernama ?!

Fixee
sumber
1
Setelah exec <f, bashtidak mencoba membaca dari f, itu pertama-tama mencoba untuk membukanya . Tidak open()akan kembali sampai ada beberapa proses melakukan buka lagi dalam mode tulis ke pipa (pada titik mana pipa akan dipakai, dan shell akan membaca input dari itu).
Stéphane Chazelas
Poin luar biasa, @ StéphaneChazelas. Ini pasti mengapa, setelah exec 3>fdijalankan, shell pertama kemudian memberikan prompt. (Poin minor, maksud Anda "dalam mode tulis " dalam komentar Anda?)
Fixee
1
ya maaf. Diedit sekarang tepat sebelum tenggat 5 menit
Stéphane Chazelas

Jawaban:

12

Ini ada hubungannya dengan penutupan deskriptor file.

Dalam contoh pertama Anda, echotulis ke aliran output standar yang dibuka shell untuk menghubungkannya f, dan ketika berakhir, deskriptornya ditutup (oleh shell). Di sisi penerima, shell, yang membaca input dari aliran input standar (terhubung ke f) membaca ls, berjalan lsdan kemudian berakhir karena kondisi akhir file pada input standarnya.

Kondisi akhir file terjadi karena semua penulis ke pipa bernama (hanya satu dalam contoh ini) telah menutup ujung pipa mereka.

Dalam contoh kedua, exec 3>fmembuka file descriptor 3 untuk menulis ke f, kemudian echomenulis lsuntuk itu. Itu shell yang sekarang memiliki deskriptor file dibuka, bukan echoperintah. Deskriptor tetap terbuka sampai Anda melakukannya exec 3>&-. Di sisi penerima, shell, yang membaca input dari aliran input standarnya (terhubung ke f) membaca ls, berjalan lsdan kemudian menunggu lebih banyak input (karena aliran masih terbuka).

Aliran tetap terbuka karena semua penulis untuk itu (shell, via exec 3>f, dan echo) belum menutup ujung pipa mereka ( exec 3>fmasih berlaku).


Saya telah menulis tentang di echoatas seolah-olah itu adalah perintah eksternal. Kemungkinan besar dibangun ke dalam shell. Efeknya tetap sama.

Kusalananda
sumber
6

Tidak ada banyak untuk itu: ketika tidak ada penulis ke pipa, itu terlihat tertutup bagi pembaca, yaitu mengembalikan EOF ketika membaca dan memblokir ketika dibuka.

Dari halaman manual Linux ( pipe(7), tetapi lihat juga fifo(7)):

Jika semua deskriptor file yang mengacu pada akhir penulisan pipa telah ditutup, maka upaya read(2)dari pipa akan melihat akhir file ( read(2)akan mengembalikan 0).

Menutup akhir penulisan adalah apa yang secara implisit terjadi pada akhir echo ls >f, dan seperti yang Anda katakan, dalam kasus lain, deskriptor file tetap terbuka.

ilkkachu
sumber
Sepertinya analog dengan jumlah referensi di Jawa (dan bahasa OO lainnya)! Masuk akal juga.
Fixee
2

Setelah membaca dua jawaban dari @ Kusalananda dan @ikkachu, saya pikir saya mengerti. Di jendela-1, shell sedang menunggu sesuatu untuk membuka kedua ujung pipa dan kemudian menutupnya. Setelah akhir penulisan dibuka, shell di window-1 mencetak prompt. Setelah akhir penulisan ditutup, shell mendapat EOF dan mati.

Di sisi jendela-2 kita memiliki dua situasi yang dijelaskan dalam pertanyaan saya: dalam situasi pertama dengan echo ls > f, tidak ada file deskriptor 3, jadi kami telah echomemijah, dan itu stdindan stdoutterlihat seperti ini:

0 --> tty
1 --> f

Kemudian echoberakhir dan shell menutup kedua deskriptor. Karena file descriptor 1 ditutup dan referensi f, akhir penulisan fditutup dan yang menyebabkan EOF ke jendela-1.

Dalam situasi kedua, kami menjalankan exec 3>fshell kami, menyebabkan shell mengambil lingkungan ini:

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

Sekarang kita jalankan echo ls >& 3dan shell mengalokasikan file deskriptor untuk echosebagai berikut:

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

Kemudian shell menutup tiga deskriptor di atas, termasuk f, tetapi fmasih memiliki referensi untuk itu dari shell itu sendiri. Inilah perbedaan penting. Menutup deskriptor 3 dengan exec 3>&-akan menutup referensi terbuka terakhir dan menyebabkan EOF ke jendela-1, seperti yang dicatat oleh @Kusalananda.

Fixee
sumber
Ini adalah contoh yang bagus mengapa Anda harus membiarkan tiga deskriptor file pertama saja kecuali ada alasan desain yang baik untuk memodifikasinya. Ketika Anda menggunakan deskriptor (1) yang akhirnya menjadi deskriptor input (0) ke shell lain, Anda tidak hanya menutup pipa (dan apa yang Anda lakukan dengan aliran data tertentu), tetapi juga menutup input ke yang kedua shell yang menyebabkannya berakhir. Ini baik-baik saja, tetapi hanya jika Anda sengaja melakukannya. Menggunakan deskriptor file bernomor lebih tinggi menghindari efek samping seperti ini karena tidak ada yang mengharapkan mereka dalam keadaan tertentu atau bahkan didefinisikan.
Joe
Sejujurnya, saya tidak yakin apa yang ingin saya katakan dalam komentar itu, saya hanya akan menghapusnya.
Stéphane Chazelas