Bagaimana pipa membatasi penggunaan memori?

36

Brian Kernighan menjelaskan dalam video ini daya tarik Bell Labs awal untuk bahasa kecil / program yang didasarkan pada keterbatasan memori

Mesin besar akan menjadi 64 k-byte - K, bukan M atau G - dan itu berarti setiap program tidak bisa sangat besar, dan ada kecenderungan alami untuk menulis program kecil, dan kemudian mekanisme pipa, pada dasarnya input redirection input, memungkinkan untuk menghubungkan satu program dengan yang lain.

Tapi saya tidak mengerti bagaimana ini dapat membatasi penggunaan memori mengingat fakta bahwa data harus disimpan dalam RAM untuk mentransmisikan antar program.

Dari Wikipedia :

Di sebagian besar sistem mirip Unix, semua proses pipa dimulai pada saat yang sama [penekanan tambang], dengan aliran mereka terhubung dengan tepat, dan dikelola oleh penjadwal bersama dengan semua proses lain yang berjalan pada mesin. Aspek penting dari ini, mengatur pipa Unix terpisah dari implementasi pipa lainnya, adalah konsep buffering: misalnya program pengiriman dapat menghasilkan 5.000 byte per detik, dan program penerima hanya dapat menerima 100 byte per detik, tetapi tidak ada data hilang. Sebagai gantinya, output dari program pengiriman disimpan dalam buffer. Ketika program penerima siap untuk membaca data, maka program berikutnya dalam pipa membaca dari buffer. Di Linux, ukuran buffer adalah 65536 byte (64KB). Filter pihak ketiga open source yang disebut bfr tersedia untuk menyediakan buffer yang lebih besar jika diperlukan.

Ini lebih membingungkan saya, karena ini benar-benar mengalahkan tujuan program kecil (meskipun mereka akan modular hingga skala tertentu).

Satu-satunya hal yang dapat saya pikirkan sebagai solusi untuk pertanyaan pertama saya (keterbatasan memori menjadi masalah tergantung pada data ukuran) adalah bahwa set data besar tidak dihitung saat itu dan pipa masalah sebenarnya dimaksudkan untuk menyelesaikan adalah jumlah memori yang dibutuhkan oleh program itu sendiri. Tetapi mengingat teks tebal dalam kutipan Wikipedia, bahkan ini membingungkan saya: karena satu program tidak diterapkan pada suatu waktu.

Semua ini akan sangat masuk akal jika file temp digunakan, tapi ini pemahaman saya bahwa pipa tidak menulis ke disk (kecuali swap digunakan).

Contoh:

sed 'simplesubstitution' file | sort | uniq > file2

Jelas bagi saya bahwa sedmembaca dalam file dan meludahkannya secara garis demi garis. Tapi sort, seperti yang dinyatakan BK dalam video yang ditautkan, adalah perhentian penuh, jadi semua data harus dibaca ke dalam memori (atau bukan?), Lalu diteruskan ke uniq, yang (menurut saya) akan menjadi satu. Program-line-pada-waktu. Tetapi antara pipa pertama dan kedua, semua data harus ada dalam memori, bukan?

malan
sumber
1
unless swap is usedswap selalu digunakan ketika tidak ada cukup RAM
edc65

Jawaban:

44

Data tidak perlu disimpan dalam RAM. Pipes memblokir penulis mereka jika pembaca tidak ada atau tidak bisa mengikuti; di Linux (dan sebagian besar implementasi lainnya, saya bayangkan) ada beberapa buffering tapi itu tidak diperlukan. Seperti yang disebutkan oleh mtraceur dan JdeBP (lihat jawaban yang terakhir), versi awal pipa buffer Unix ke disk, dan ini adalah bagaimana mereka membantu membatasi penggunaan memori: pipa pemrosesan dapat dibagi menjadi beberapa program kecil, yang masing-masing akan memproses beberapa data, dalam batas buffer disk. Program-program kecil memakan lebih sedikit memori, dan penggunaan pipa berarti bahwa pemrosesan dapat diserialisasi: program pertama akan berjalan, mengisi buffer outputnya, ditangguhkan, maka program kedua akan dijadwalkan, memproses buffer, dll. Sistem modern dipesan besarnya lebih besar dari sistem Unix awal, dan dapat menjalankan banyak pipa secara paralel; tetapi untuk sejumlah besar data Anda masih akan melihat efek yang sama (dan varian dari teknik semacam ini digunakan untuk pemrosesan "big data").

Dalam contoh Anda,

sed 'simplesubstitution' file | sort | uniq > file2

sedmembaca data dari yang filediperlukan, kemudian menulisnya selama sortsiap untuk membacanya; jika sorttidak siap, blok tulis. Data memang memang hidup di memori pada akhirnya, tapi itu khusus untuk sort, dan sortsiap untuk menangani masalah apa pun (itu akan menggunakan file sementara itu jumlah data untuk disortir terlalu besar).

Anda dapat melihat perilaku pemblokiran dengan menjalankan

strace seq 1000000 -1 1 | (sleep 120; sort -n)

Ini menghasilkan cukup banyak data dan mengirimkannya ke proses yang tidak siap untuk membaca apa pun selama dua menit pertama. Anda akan melihat sejumlah writeoperasi berjalan, tetapi sangat cepat seqakan berhenti dan menunggu dua menit berlalu, diblokir oleh kernel ( writepanggilan sistem menunggu).

Stephen Kitt
sumber
13
Jawaban ini dapat mengambil manfaat dari penjelasan tambahan mengapa memecah program menjadi banyak yang menghemat penggunaan memori: Suatu program harus dapat masuk dalam memori untuk dijalankan, tetapi hanya program yang sedang berjalan . Setiap program lainnya ditukar ke disk pada awal Unix, dengan hanya satu program yang bahkan ditukar dengan RAM aktual pada suatu waktu. Jadi CPU akan menjalankan satu program, yang akan menulis ke pipa (yang saat itu berada di disk ), menukar program itu dan menukar dengan program yang membaca dari pipa. Cara yang elegan untuk mengubah jalur perakitan paralel secara paralel menjadi eksekusi serial yang bertahap.
mtraceur
6
@alan: Beberapa proses dapat dimulai dan bisa dalam kondisi runnable pada saat yang sama. Tetapi paling banyak satu proses dapat dijalankan pada setiap CPU fisik pada waktu tertentu, dan merupakan tugas penjadwal proses kernel untuk mengalokasikan "irisan" waktu CPU untuk setiap proses yang dapat dijalankan. Dalam sistem modern, proses yang dapat dijalankan tetapi saat ini tidak dijadwalkan jadwal waktu CPU biasanya tetap ada di memori saat menunggu irisan berikutnya, tetapi kernel diizinkan untuk mem-page memori dari setiap proses keluar ke disk dan kembali ke memori lagi sebagai menemukan nyaman. (Handwaving detail di sini.)
Daniel Pryden
5
Proses di kedua sisi pipa dapat berperilaku efektif seperti co-rutin: satu sisi menulis sampai mengisi buffer dan blok tulis, di mana titik proses tidak dapat melakukan apa pun dengan sisa kutu waktu dan masuk ke dalam IO mode tunggu. Kemudian OS memberikan sisa kutu waktu (atau kutu waktu lain yang akan datang) ke sisi pembacaan, yang berbunyi sampai tidak ada yang tersisa di buffer dan blok pembacaan berikutnya, pada titik mana proses pembaca tidak dapat melakukan apa pun dengan sisa kutu waktu dan hasil kembali ke OS. Data melewati nilai pipa satu buffer pada satu waktu.
Daniel Pryden
6
@malan Program-program dimulai "pada saat yang sama" secara konseptual pada semua sistem Unix, hanya pada sistem multiprosesor modern dengan RAM yang cukup untuk menahannya, yang berarti mereka semua benar-benar disimpan dalam RAM pada saat yang sama, sementara pada sistem yang dapat bisa menahan mereka semua dalam RAM pada saat yang sama, beberapa bisa ditukar ke disk. Perhatikan juga bahwa "memori" dalam banyak konteks berarti memori virtual yang merupakan jumlah ruang RAM dan ruang swap pada disk. Wikipedia berfokus pada konsep daripada detail implementasi, terutama karena seberapa lama Unix melakukan hal-hal yang kurang relevan sekarang.
mtraceur
2
@ Malalan Juga, kontradiksi yang Anda lihat berasal dari dua arti "memori" yang berbeda (RAM vs RAM + swap). Saya hanya berbicara tentang RAM perangkat keras saja, dan dalam konteks itu hanya kode yang saat ini dieksekusi oleh CPU yang harus sesuai dengan RAM (yang merupakan apa yang mempengaruhi keputusan yang dibicarakan Kernighan), sementara dalam konteks semua program dieksekusi secara logis oleh OS pada waktu tertentu (pada tingkat abstrak yang disediakan di atas slicing waktu) program hanya perlu masuk ke dalam seluruh memori virtual yang tersedia untuk OS, yang mencakup ruang swap pada disk.
mtraceur
34

Tapi saya tidak mengerti bagaimana ini dapat membatasi penggunaan memori mengingat fakta bahwa data harus disimpan dalam RAM untuk mentransmisikan antar program.

Ini adalah kesalahan mendasar Anda. Versi awal Unix tidak menyimpan data pipa dalam RAM. Mereka menyimpannya di disk. Pipa memiliki i-node; pada perangkat disk yang dilambangkan dengan perangkat pipa . Administrator sistem menjalankan program yang diberi nama /etc/configuntuk menentukan (di antara hal-hal lain) volume mana pada disk mana yang merupakan perangkat pipa, yang volume adalah perangkat root , dan yang merupakan perangkat dump .

Jumlah data yang tertunda dibatasi oleh fakta bahwa hanya blok langsung dari simpul-i pada disk yang digunakan untuk penyimpanan. Mekanisme ini membuat kode lebih sederhana, karena banyak algoritma yang sama digunakan untuk membaca dari pipa seperti yang digunakan untuk membaca file biasa, dengan beberapa perubahan yang disebabkan oleh fakta bahwa pipa tidak dapat dicari dan buffer berbentuk lingkaran.

Mekanisme ini digantikan oleh yang lain pada pertengahan hingga akhir 1980-an. SCO XENIX memperoleh "Sistem Pipa Kinerja Tinggi", yang menggantikan node-i dengan buffer inti. 4BSD membuat pipa tanpa nama ke dalam soketpairs. AT&T menerapkan kembali pipa menggunakan mekanisme STREAMS.

Dan tentu saja sortprogram ini melakukan jenis input 32KiB internal yang terbatas (atau berapa pun jumlah memori yang lebih kecil yang dapat dialokasikan jika 32KiB tidak tersedia), menulis hasil yang diurutkan ke stmX??file perantara di /usr/tmp/mana kemudian digabungkan secara eksternal untuk menyediakan final keluaran.

Bacaan lebih lanjut

  • Steve D. Pate (1996). "Komunikasi antar-proses". UNIX Internal: Suatu Pendekatan Praktis . Addison-Wesley. ISBN 9780201877212.
  • Maurice J. Bach (1987). "Panggilan Sistem untuk Sistem File". Desain Sistem Operasi Unix . Prentice-Hall. ISBN 0132017571.
  • Steven V. Earhart (1986). " config(1M)". Manual Programmer Unix: 3. Fasilitas Administrasi Sistem . Holt, Rinehart, dan Winston. ISBN 0030093139. hlm. 23–28.
JdeBP
sumber
1

Anda sebagian benar, tetapi hanya karena kecelakaan .

Dalam contoh Anda, semua data memang harus telah dibaca "di antara" pipa-pipa, tetapi itu tidak perlu berada dalam memori (termasuk memori virtual). Implementasi yang biasa dari sortdapat mengurutkan dataset yang tidak sesuai dengan RAM dengan melakukan penyortiran parsial untuk tempfile, dan penggabungan. Namun, itu adalah fakta yang diberikan bahwa Anda tidak mungkin menampilkan urutan yang diurutkan sebelum membaca setiap elemen. Itu cukup jelas. Jadi ya, sorthanya bisa mulai mengeluarkan ke pipa kedua setelah membaca (dan melakukan apa pun, mungkin sebagian menyortir tempfile) semuanya dari yang pertama. Tetapi tidak harus selalu menyimpan semuanya dalam RAM.

Namun, ini tidak ada hubungannya dengan cara kerja pipa. Pipa dapat dinamai (secara tradisional mereka semua bernama) yang berarti tidak lebih dan tidak kurang dari mereka memiliki lokasi dalam sistem file, seperti file. Dan itu hanya apa yang dilakukan pipa pada suatu waktu, file (dengan tulis digabung sebanyak ketersediaan memori fisik akan memungkinkan, sebagai optimasi).

Saat ini, pipa adalah penyangga kernel berukuran kecil hingga data disalin, setidaknya itulah yang secara konseptual terjadi. Jika kernel dapat membantu, salinan dielakkan dengan memainkan trik-trik VM (misalnya, pemipaan dari file biasanya hanya membuat halaman yang sama tersedia untuk proses lain untuk dibaca, sehingga akhirnya hanya operasi baca, bukan dua salinan, dan tidak ada memori tambahan dari yang sudah digunakan oleh cache buffer tetap diperlukan Dalam beberapa situasi Anda mungkin mendapatkan 100% salinan nol juga, atau sesuatu yang sangat dekat.

Jika pipa berukuran kecil dan terbatas, maka bagaimana cara kerjanya untuk jumlah data yang tidak diketahui (mungkin besar)? Itu sederhana: Ketika tidak ada lagi yang cocok, blok tulis sampai ada ruang lagi.

Filosofi dari banyak program sederhana sangat berguna sekali pada masa ketika memori sangat langka. Karena, yah, Anda bisa melakukan pekerjaan dalam langkah-langkah kecil, satu per satu. Saat ini, kelebihannya, terlepas dari fleksibilitas ekstra, saya berani katakan, tidak terlalu bagus lagi.
Namun, pipa diterapkan dengan sangat efisien (memang harus demikian!), Jadi tidak ada kerugiannya juga, dan itu adalah hal yang mapan yang berfungsi dengan baik dan digunakan orang-orang, sehingga tidak perlu mengubah paradigma.

Damon
sumber
Ketika Anda mengatakan 'pipa dinamai' (JdeBP tampaknya mengatakan ada satu 'perangkat pipa'), apakah itu berarti ada batasan jumlah pipa yang dapat digunakan pada waktu tertentu (yaitu, ada batas untuk berapa kali Anda bisa menggunakan |perintah)?
malan
2
Saya belum pernah melihat batas seperti itu, dan saya tidak berpikir bahwa secara teori ada satu pun. Dalam praktiknya, apa pun yang memiliki nama file memerlukan inode, dan jumlah inode tentu saja terbatas. Seperti halnya jumlah halaman fisik pada suatu sistem, jika tidak ada yang lain. Sistem modern menjamin penulisan atom 4k, sehingga setiap pipa setidaknya harus memiliki satu halaman 4k yang lengkap, yang membatasi jumlah pipa yang Anda miliki. Tapi pertimbangkan memiliki beberapa gigabytes RAM ... praktis, itu adalah batas yang tidak akan pernah Anda temui. Coba dan ketik beberapa juta pipa pada terminal ... :)
Damon