Bagaimana pipa bekerja di Linux

25

Saya telah membaca tentang bagaimana pipa diimplementasikan di kernel Linux dan ingin memvalidasi pemahaman saya. Jika saya salah, jawaban dengan penjelasan yang benar akan dipilih.

  • Linux memiliki VFS yang disebut pipef yang dipasang di kernel (bukan di ruang pengguna)
  • pipefs memiliki super blok tunggal dan dipasang di root sendiri ( pipe:), di samping/
  • pipef tidak dapat dilihat secara langsung tidak seperti kebanyakan sistem file
  • Entri ke pipef adalah melalui pipe(2)syscall
  • The pipe(2)syscall digunakan oleh kerang untuk pipa dengan |operator (atau manual dari proses lainnya) membuat file baru di pipefs yang berperilaku cukup banyak seperti file normal
  • File di sisi kiri operator pipa stdoutdialihkan ke file sementara yang dibuat di pipefs
  • File di sisi kanan operator pipa memiliki stdinset ke file di pipefs
  • pipef disimpan dalam memori dan melalui beberapa kernel sulap, tidak boleh halaman

Apakah penjelasan tentang cara kerja pipa (mis. ls -la | less) Ini benar?

Satu hal yang saya tidak mengerti adalah bagaimana sesuatu seperti bash akan mengatur proses ' stdinatau stdoutke file descriptor yang dikembalikan oleh pipe(2). Saya belum dapat menemukan apa pun tentang itu.

Brandon Wamboldt
sumber
Perhatikan bahwa Anda berbicara tentang dua lapisan yang sangat berbeda dengan nama yang sama. The pipe()panggilan kernel bersama dengan mesin yang mendukung itu ( pipefs, dll) adalah tingkat yang lebih rendah dari |operator yang ditawarkan dalam shell Anda. Yang terakhir biasanya diimplementasikan menggunakan yang pertama, tetapi tidak harus.
Greg Hewgill
Ya, saya secara khusus merujuk pada operasi tingkat bawah, dengan asumsi bahwa |operator hanya memanggil pipe(2)sebagai proses seperti halnya bash.
Brandon Wamboldt
Lihat juga Apa perbedaan antara "Redirection" dan "Pipe"?
Sergiy Kolodyazhnyy

Jawaban:

19

Analisis Anda sejauh ini umumnya benar. Cara sebuah shell dapat mengatur stdin dari suatu proses menjadi deskriptor pipa bisa menjadi (pseudocode):

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
Greg Hewgill
sumber
Terima kasih! Hanya ingin tahu mengapa dup2panggilan itu diperlukan, dan Anda tidak bisa langsung menetapkan deskriptor pipa ke stdin?
Brandon Wamboldt
3
Penelepon tidak bisa memilih apa nilai numerik dari deskriptor file saat dibuat pipe(). The dup2()panggilan memungkinkan penelpon untuk menyalin file descriptor untuk nilai numerik tertentu (diperlukan karena 0, 1, 2 yang stdin, stdout, stderr). Itu setara dengan kernel dari "menetapkan langsung ke stdin". Perhatikan bahwa variabel global C runtime library stdinadalah a FILE *, yang tidak terkait dengan kernel (meskipun diinisialisasi untuk dihubungkan ke deskriptor 0).
Greg Hewgill
Jawaban bagus! Saya sedikit bingung dengan detailnya. Hanya ingin tahu mengapa Anda menutup (p [1]) sebelum menjalankan exec ()? Setelah dup2 kembali, bukankah p [1] menunjuk ke fd 0? Kemudian tutup (p [1]) menutup file descriptor 0. Lalu bagaimana kita bisa membaca dari stdin dari proses anak?
user1559897
@ user1559897: dup2Panggilan tidak berubah p[1]. Sebagai gantinya, ia membuat kedua pegangan p[1]dan 0menunjuk ke objek kernel yang sama (pipa). Karena proses anak tidak memerlukan dua pegangan stdin (dan tidak akan tahu apa pegangan nomor itu p[1],) p[1]ditutup sebelumnya exec.
Greg Hewgill
@GregHewgill Gotchu. Terima kasih!
user1559897