Apakah buffer akan secara otomatis dialihkan ke disk saat proses keluar?

21

Ketika saya mengarahkan output perintah ke file (misalnya, echo Hello > file) akankah file itu dijamin memiliki data seperti itu setelah perintah keluar? Atau masih ada jendela yang sangat kecil antara perintah keluar dan data yang ditulis ke file? Saya ingin membaca file tepat setelah perintah keluar, tetapi saya tidak ingin membaca file kosong.

Eric
sumber
1
Ini mungkin menjalankan perintah segera, tetapi jumlah waktu yang dibutuhkan untuk benar-benar membuka file, menulis, dan menutup akan tergantung pada kecepatan dan jenis hard drive Anda, semua program yang sedang berjalan, dll.
freginold
Dalam hal contoh yang diberikan, apa itu 'proses'? Apakah echodan >bukan proses yang terpisah (berumur pendek)? Dan di mana output echotetap sebelum >dieksekusi?
o
1
@ oɔɯǝɹ >adalah pengalihan shell. Itu sama seperti jika program telah membuka file bernama untuk menulis dan mengganti stdout dengan itu yang persis seperti yang dilakukan shell.
Dan D.
7
Saya pikir itu adalah tanggung jawab OS untuk memberikan filemengandung Helloterlepas dari apakah itu memerah atau tidak.
Salman A
1
Jika program berjalan pada mesin A, dan Anda membaca file pada mesin B, dengan sistem file mesin A dipasang di jaringan, maka Anda mungkin akan membaca file kosong, tergantung pada tipe sistem file jaringan dan pengaturan pemasangan. Jadi, Anda mungkin ingin menonaktifkan caching untuk pemasangan itu.
Poin

Jawaban:

21

Ada beberapa lapisan buffer / cache yang terlibat.

  1. Cache CPU.

    Data disatukan byte demi byte, dan disimpan dalam cache CPU. Jika cache CPU penuh dan data belum diakses untuk sementara waktu, blok yang berisi data kami dapat ditulis ke memori utama. Ini, sebagian besar, disembunyikan dari pemrogram aplikasi.

  2. Buffer dalam proses.

    Ada beberapa memori yang disisihkan dalam proses pengumpulan data sehingga kita perlu sesedikit mungkin meminta OS, karena itu relatif mahal. Proses menyalin data ke buffer ini, yang sekali lagi dapat didukung oleh cache CPU, sehingga tidak ada jaminan bahwa data akan disalin ke memori utama. Aplikasi perlu menyiram buffer ini secara eksplisit, misalnya menggunakan fclose (3) atau fsync (3). Fungsi exit (3) juga melakukan ini sebelum proses diakhiri, sedangkan fungsi _exit (2) tidak , yang mengapa ada peringatan besar di halaman manual untuk fungsi memanggilnya hanya jika Anda tahu apa yang Anda lakukan. perbuatan.

  3. Kernel buffer

    OS kemudian menyimpan cache sendiri, untuk meminimalkan jumlah permintaan yang perlu dikirim ke disk. Tembolok ini bukan milik proses tertentu, jadi data di sana mungkin milik proses yang telah selesai, dan karena semua akses masuk ke sini, program selanjutnya akan melihat data jika telah mencapai di sini. Kernel akan menulis data ini ke disk ketika ada waktu untuk melakukannya atau ketika ditanya secara eksplisit.

  4. Cache drive

    Disk drive itu sendiri juga menyimpan cache untuk mempercepat akses. Ini ditulis dengan cukup cepat, dan ada perintah untuk menulis data yang tersisa di cache dan melaporkan ketika itu selesai, yang menggunakan OS pada shutdown untuk memastikan tidak ada data yang tersisa yang tidak tertulis sebelum mematikan.

Untuk aplikasi Anda, itu sudah cukup bagi data untuk didaftarkan di buffer kernel (data aktual mungkin masih hidup dalam cache CPU pada saat ini, dan mungkin belum ditulis ke memori utama): proses "echo" berakhir, yang berarti bahwa setiap buffer dalam proses pasti telah memerah dan data diserahkan ke OS, dan ketika Anda memulai proses baru, dijamin bahwa OS akan memberikan data yang sama saat diminta.

Simon Richter
sumber
7
Mengingat caching CPU tampaknya tidak relevan bagi saya. Ini adalah level detail yang tidak perlu di sini. Seperti yang akan melalui semua detail sampai beberapa kuantitas fisik yang mewakili sedikit pada piring hard disk atau memori SSD diubah untuk membaliknya.
mvw
3
Memang, cache CPU cukup orthogonal.
Simon Richter
2
Dan yang lebih penting, cache CPU adalah koheren di antara core, itulah sebabnya itu benar-benar keluar dari gambar. Pada x86, bahkan koheren dengan DMA (dan x86 memiliki mode pemesanan memori urutan-toko-total), sehingga apa pun yang dapat membaca memori akan melihat data yang paling baru disimpan ke alamat itu dalam urutan global operasi memori. (Inti CPU akan melihat toko sendiri bahkan sebelum mereka menjadi terlihat secara global, karena penerusan toko dari antrian toko). Pada platform non-x86 tanpa DMA-cache yang koheren, kernel Linux memastikan cache disiram sebelum DMA ke alamat-alamat tersebut.
Peter Cordes
1
"Sebagian besar, ini tersembunyi dari pemrogram aplikasi." Mengapa "sebagian besar"? Saya seorang pengembang tertanam dan kecuali selama boot loader (jadi bukan "aplikasi") saya benar-benar mengabaikan cache CPU. Saya tidak berpikir pengembang aplikasi dapat dipengaruhi oleh efek cache CPU.
Sam
1
@Sam cache misses / hits bersama dengan eksekusi spekulatif dapat dieksploitasi di beberapa CPU untuk memotong pembatasan akses baca. Mungkin inilah jawaban yang telah dirujuk?
John Dvorak
22

Jika aplikasi tidak memiliki cache internal, maka perubahan akan segera ditulis ke file. Sama untuk contoh Anda. File tersebut adalah entitas logis dalam memori yang akan segera diperbarui. Setiap operasi selanjutnya pada file akan melihat perubahan yang dilakukan oleh program.

Namun , ini tidak berarti perubahan ditulis ke disk fisik. Perubahan mungkin berlama-lama di dalam cache sistem file OS atau cache perangkat keras. Untuk membersihkan buffer sistem file, gunakan syncperintah.

Saya ingin membaca file tepat setelah perintah keluar, tetapi saya tidak ingin membaca file kosong.

Anda seharusnya tidak mengalami masalah praktis di sini.

mtak
sumber
1
"Jika aplikasi tidak memiliki cache internal" - itu adalah "jika" yang sangat besar: sebagian besar implementasi perpustakaan I / O menggunakan buffer stdout secara default. Yang mengatakan, standar C misalnya mengamanatkan bahwa buffer stdout disiram pada saat keluar (tetapi berpotensi tidak jika exittidak setidaknya disebut secara implisit). Perpustakaan / bahasa lain (mis. Java!) Memberikan lebih sedikit jaminan.
Konrad Rudolph
Bagaimana jika hanya membatasi ke redirect primitive (yaitu, perintah dalam pertanyaan saya)? Itu tidak memiliki cache internal, kan?
Eric
@Eric Tidak, Anda harus baik-baik saja.
mtak
10
Saya tidak yakin apakah saya mendapatkan jawaban ini. Pertanyaannya adalah tentang "kapan proses keluar". Setiap aplikasi dengan cache tulis internal akan membuangnya ke disk saat proses keluar, jika itu tidak terjadi sebelumnya. TKI, cache itu tidak penting di sini.
MSalters
2
Selain itu, buffer internal akan disiram saat keluar atau hanya memudar dari keberadaan, kan? Jadi, bahkan jika buffer internal tidak menyiram, konten tidak akan dapat diamati, tidak peduli berapa lama orang akan menunggu.
WorldSEnder
21

Apakah buffer akan secara otomatis dialihkan ke disk saat proses keluar?

Secara umum jawabannya tidak .

Itu tergantung pada perintah. Seperti jawaban lain menyebutkan, jika perintah tidak secara internal buffer data, semua data akan tersedia ketika perintah berakhir.

Tetapi sebagian besar, jika tidak semua, perpustakaan I / O standar melakukan buffer stdout secara default (sampai batas tertentu), dan memberikan jaminan yang berbeda tentang penyiraman otomatis buffer ketika aplikasi ditutup.

C menjamin bahwa jalan keluar yang normal akan mengguyur buffer . "Keluar normal" berarti yang exitdisebut - baik secara eksplisit, atau dengan kembali dari main. Namun, jalan keluar yang abnormal dapat menghindari panggilan ini (dan karenanya meninggalkan buffer yang tidak terhalang).

Berikut ini contoh sederhana:

#include <signal.h>
#include <stdio.h>

int main() {
    printf("test");
    raise(SIGABRT);
}

Jika Anda mengkompilasi ini dan melaksanakannya, testakan tidak selalu ditulis ke stdout.

Bahasa pemrograman lain bahkan memberikan jaminan lebih sedikit: Java, misalnya, tidak otomatis dihapus pada saat penghentian program . Jika buffer output berisi garis yang tidak ditentukan, maka mungkin akan hilang, kecuali System.out.flush()disebut secara eksplisit.

Yang mengatakan, badan pertanyaan Anda menanyakan sesuatu yang sedikit berbeda: jika data masuk dalam file sama sekali , itu harus segera dilakukan setelah perintah berakhir (tunduk pada peringatan yang dijelaskan dalam jawaban lain).

Konrad Rudolph
sumber
7
Saya juga melihat keluar abnormal ketika alat baris perintah menulis ke file dan stdout atau stderr, seperti log debug, dan pengguna telah melakukan pipa untuk menuju atau kurang dari mengetik 'q' untuk berhenti lebih sedikit. File disk tidak selalu sepenuhnya memerah jika alat baris perintah tidak menangani SIGPIPE.
Zan Lynx
+1, tetapi "itu harus dilakukan segera setelah perintah berakhir" tidak cukup benar: panggilan sistem write()atau apa pun pwrite()akan terjadi sebelum proses keluar, dan saat itulah perubahan file menjadi terlihat. Jadi perubahan file terakhir pasti sebelum proses penghentian, segera-sebelum paling lambat. Saya pikir bahkan dengan sebuah mmap(MAP_SHARED)file, tidak ada cara untuk mengamati proses pemutusan terjadi sebelum semua perubahan file yang akan terjadi.
Peter Cordes
9

Saya pikir tidak ada pertanyaan yang cukup untuk mengatasi masalah ini:

Saya ingin membaca file tepat setelah perintah keluar, tetapi saya tidak ingin membaca file kosong.

Seperti jawaban lain menjelaskan, program berperilaku baik flush buffer file internal sebelum proses berakhir secara normal . Setelah itu, data mungkin masih ada di buffer kernel atau perangkat keras sebelum ditulis ke penyimpanan persisten. Namun , semantik sistem file Linux menjamin bahwa semua proses melihat konten file dengan cara yang sama seperti kernel termasuk buffer internal 1 .

Ini biasanya diterapkan dengan memiliki paling banyak satu buffer in-kernel per objek file dan meminta semua akses file untuk melewati buffer ini.

  • Jika suatu proses membaca file, kernel akan menyajikan konten buffer ke proses, jika bagian file yang diminta saat ini dalam buffer; jika tidak, kernel akan mengambil data dari media penyimpanan yang mendasarinya dan menempatkannya di dalam buffer, kemudian kembali ke langkah sebelumnya.

  • Jika suatu proses menulis ke suatu file, data pertama-tama ditempatkan di dalam buffer in-kernel untuk file itu. Akhirnya konten buffer akan dibuang ke penyimpanan. Sementara itu, akses baca dipenuhi dari buffer yang sama (lihat di atas).


1 Setidaknya untuk file, direktori, dan tautan simbolik biasa. FIFO dan soket adalah masalah yang berbeda karena kontennya tidak pernah disimpan secara tetap. Ada beberapa kasus khusus file biasa yang isinya bergantung pada siapa yang bertanya; contohnya adalah file dalam procfs dan sysfs (pikirkan /proc/selfyang merupakan tautan simbolis ke ID proses dari proses membaca tautan simbolis).

David Foerster
sumber
2
Sebenarnya, bukan semantik sistem berkas Linux yang menjamin ini, semantik POSIX yang melakukannya. Secara khusus, BSD berperilaku persis sama, seperti halnya macOS, dan bahkan Windows (meskipun ini adalah salah satu dari sedikit kasus di mana Windows mengikuti semantik POSIX). Ini juga mengasumsikan tidak ada orang yang melakukan hal-hal aneh dengan mmap()dan O_DIRECT, yang dapat menyebabkan hal-hal yang tidak sinkron antara disk dan cache halaman (tetapi itu akan menyelesaikan saat proses yang keluar itu dilakukan).
Austin Hemmelgarn
2
@AustinHemmelgarn: Sebenarnya kami berdua benar karena Linux dirancang dengan dukungan untuk aplikasi Unix (Sistem V) dan kemudian dibuat untuk mendukung POSIX yang juga mendasarkan banyak konsep pada Sistem V.
David Foerster
5

Dengan anggapan perintah Anda dijalankan oleh beberapa program menggunakan pustaka runtime C, pada titik tertentu harus dijalankan fcloseuntuk menutup file yang terbuka.

Halaman manual untuk fclosefungsi C mengatakan:

CATATAN Perhatikan bahwa fclose () hanya mengguyur buffer ruang pengguna yang disediakan oleh perpustakaan C. Untuk memastikan bahwa data disimpan secara fisik di disk, buffer kernel juga harus dibilas, misalnya, dengan sinkronisasi (2) atau fsync (2).

dan halaman manual untuk fflushmemiliki catatan yang sama. Halaman manual untuk closemengatakan:

Penutupan yang berhasil tidak menjamin bahwa data telah berhasil disimpan ke disk, seperti yang dituliskan oleh kernel. Tidak umum bagi sistem file untuk menyiram buffer ketika aliran ditutup. Jika Anda perlu memastikan bahwa data disimpan secara fisik, gunakan fsync (2). (Ini akan tergantung pada perangkat keras disk pada saat ini.)

Perhatikan bahwa data tersedia untuk proses lain meskipun tidak disinkronkan ke drive. Mungkin itu sudah cukup baik untukmu.

Jika Anda ragu, tulis tes.

mvw
sumber
2
C atau tidak, semuanya akan / harus menggunakan close()syscall untuk menutup deskriptor file.
Attie
@Attie: Anda tidak perlu untuk closefile sebelum keluar (dalam program Hacky yang tidak memeriksa kesalahan); kernel akan membersihkannya, memanggil closeAnda secara efektif setelah proses Anda mati. Namun, Anda perlu melakukan fclosestdio stream yang disangga, atau membiarkan libc melakukan itu untuk Anda exit(3), sebagai lawan dari panggilan sistem keluar secara langsung.
Peter Cordes
Jika Anda ragu, tulis tes. Ini adalah saran yang buruk untuk mendeteksi kondisi ras. Pengujian pada satu kernel yang berjalan pada satu perangkat keras mungkin memberi tahu Anda bahwa perlombaan tidak dapat terjadi dalam kondisi perangkat lunak yang dihasilkan oleh pengujian Anda pada sistem itu, atau jika itu terjadi, itu terlalu jarang untuk dideteksi. Tetapi tidak bisa memberi tahu Anda apakah perilaku itu seharusnya aman di semua sistem file, kernel, dan semua perangkat keras (misalnya PowerPC). yaitu Anda tidak dapat mengatakan apakah jaminan yang Anda andalkan adalah detail implementasi atau jaminan bukti masa depan yang disengaja! (Dalam hal ini.)
Peter Cordes
Itu tergantung situasi. Beberapa orang yang mencoba menjalankan skrip shell-nya mungkin dibantu oleh saran ini. Itu tidak dimaksudkan sebagai solusi umum untuk lingkungan yang lebih maju tetapi kurang mungkin, misalnya seorang insinyur perangkat lunak yang bekerja pada kernel OS, beberapa orang yang bekerja pada pembaruan mikrokode Intel, atau beberapa gadis yang melakukan pekerjaan pada beberapa sistem untuk ISS.
mvw
3

Ketika saya mengarahkan output perintah ke file (misalnya, echo Hello > file) akankah file itu dijamin memiliki data seperti itu setelah perintah keluar?

Iya nih. Shell membuka file output, dan echooutput langsung ke sana. Setelah perintah keluar, selesai.

Atau masih ada jendela yang sangat kecil antara perintah keluar dan data yang ditulis ke file?

Apakah data sudah ada di media adalah masalah lain, yang hanya penting jika ada kegagalan perangkat keras, atau Anda memeriksa partisi langsung dengan beberapa perangkat lunak forensik, melewati sistem file yang dipasang.

Saya ingin membaca file tepat setelah perintah keluar, tetapi saya tidak ingin membaca file kosong.

Jangan khawatir, kernel hanya menyimpan satu tampilan file, terlepas dari seberapa sering dibuka.

Deduplicator
sumber
"kernel hanya menyimpan satu tampilan file": tidak sepenuhnya benar untuk mmap(MAP_SHARED): menyimpan ke dalam wilayah mmaped tidak koheren dengan pembacaan file (oleh utas itu atau proses lainnya). Inilah sebabnya mengapa msync(2)ada. Setidaknya itulah yang diperingatkan halaman manual; tergantung pada implementasinya, Linux sebenarnya dapat memetakan halaman fisik dari pagecache, dalam hal ini saya kira itu pada dasarnya adalah koheren (modulo memory-order). Bagaimanapun, itu semua masih terjadi sebelumnya _exit(2).
Peter Cordes
2

Sebagai aturan umum, setiap data yang dimiliki oleh kernel dipelihara & dibersihkan oleh kernel, titik. Data tersebut termasuk data yang ditransfer ke memori kernel dengan panggilan sistem seperti write(2).

Namun, jika aplikasi Anda (mis. C library) melakukan buffering di atas ini, maka kernel jelas tidak tahu dan karenanya tidak menjamin pembersihannya.

Selain itu, saya tidak percaya ada jaminan waktu untuk pembersihan — itu, pada umumnya, dilakukan atas dasar "upaya terbaik" (baca: "ketika saya punya waktu").

Mehrdad
sumber
Ada jaminan bahwa pembersihan / penyiraman buffer akan terjadi sebelum proses induk waitpid()kembali, jika pembersihan sama sekali terjadi. yaitu proses lain tidak dapat secara langsung mengamati penghentian proses yang terjadi sebelum modifikasi file dilakukan oleh proses itu. (Saya mengatakan "langsung" untuk mengesampingkan pengamatan tidak langsung melalui stempel waktu file NFS, karena caching NFS tidak sepenuhnya koheren di antara host.)
Peter Cordes
@PeterCordes: Saya kira itu tergantung apa yang Anda maksud dengan "pembersihan" sebagai kebalikan dari "memelihara". Bagi saya "mempertahankan" adalah "memberikan pandangan yang koheren" (yang memang memiliki jaminan yang Anda sebutkan) dan "bersihkan" adalah "flush to disk" yang saya tidak yakin memiliki jaminan waktu.
Mehrdad
Oh saya mengerti, Anda sedang menjawab bagian "flushed to disk" dari pertanyaan yang tidak relevan dengan apa yang nantinya akan dilihat proses ketika membaca file. "bersihkan" dalam arti "bersihkan cache i / o cache / buffer memory". Benar, tidak ada jaminan waktu kecuali jika Anda menggunakan fsync/ fdatasync, meskipun buffer-write-back di Linux akan mulai setelah /proc/sys/vm/dirty_writeback_centisecsseperseratus detik (jika tidak ditunda oleh lalu lintas I / O lainnya), dan berbagai merdu lain dalam direktori procfs juga mempengaruhi hal-hal (misalnya bagaimana besar untuk membiarkan buffer tumbuh sebelum melakukan balasan apa pun).
Peter Cordes
2

Atau masih ada jendela yang sangat kecil antara perintah keluar dan data yang ditulis ke file?

Tidak, tidak ada.

Saya ingin membaca file tepat setelah perintah keluar, tetapi saya tidak ingin membaca file kosong.

Anda dapat membaca konten final file tepat setelah perintah keluar, Anda tidak akan pernah membaca file yang kosong. (Di C dan C ++, gunakan sistem panggilan wait , waitpid , wait3 atau wait4 untuk menunggu program keluar, dan baru kemudian membaca file. Jika Anda menggunakan shell, bahasa pemrograman lain atau pustaka (mis. Perpustakaan C sistem panggilan atau kelas Proses Java ), mungkin sudah menggunakan salah satu dari panggilan sistem ini.)

Seperti yang telah ditunjukkan oleh jawaban dan komentar lainnya, Anda mungkin pada akhirnya membaca file kosong setelah keluar dari program jika program telah keluar tanpa menyiram buffer output internal (mis. Karena _exit , batalkan , atau menerima sinyal fatal, atau karena itu program Java keluar secara normal). Namun tidak ada yang dapat Anda lakukan mengenai hal ini pada saat ini: data yang tidak terhapus hilang selamanya, menunggu tambahan tidak akan memulihkannya.

Poin
sumber
0

iya nih

Maaf karena mungkin menambahkan jawaban yang berlebihan, tetapi sebagian besar tampaknya berfokus pada herring merah pada judul pertanyaan. Tapi sejauh yang saya tahu, pertanyaannya bukan tentang buffering sama sekali, tetapi ini:

Ketika saya mengarahkan output perintah ke file (misalnya, gema Hello> file) akankah file itu dijamin memiliki data seperti itu tepat setelah perintah keluar?

Ya tanpa syarat. Penggunaan ">" yang Anda gambarkan, bersama dengan "|" dan "<", adalah model pemrosesan berbasis pipa yang menjadi dasar dunia Unix dan Linux. Anda akan menemukan ratusan, jika tidak ribuan skrip benar-benar tergantung pada perilaku ini di setiap instalasi Linux.

Ini berfungsi seperti yang Anda inginkan per desain, dan jika ada peluang sekecil apa pun dari kondisi balapan, mungkin sudah diperbaiki beberapa dekade yang lalu.

AnoE
sumber
Ini berlebihan, sayangnya. Hanya beberapa jawaban yang sebagian besar fokus pada red-herring dari data yang dilakukan ke penyimpanan non-volatil. Lihat jawaban @ pts dan beberapa lainnya untuk deskripsi yang jelas: modifikasi file terjadi sebelum keluar, atau tidak sama sekali.
Peter Cordes