Kedalaman I/O Windows (bagian 4) — Cache Manager: kapan WriteFile Anda sampai ke disk

· · Windows, Win32, I/O, Cache, Kernel, Sistem file, .NET, C#

WriteFile mengembalikan sukses. Jadi, di mana data sekarang?

Jawabannya, hampir pasti belum ada di disk. Hanya disalin ke cache di memori. Karena itu «sudah disimpan, tetapi setelah pemutusan daya kembali, hilang» terjadi, karena itu tolok ukur salin file mengetuk kecepatan yang secara fisik mustahil, dan karena itu basis data dengan taat memanggil fsync.

Bagian 4 seri «Kedalaman I/O Windows» adalah Cache Manager yang berdiri di antara ini. Di bagian 2 kita menulis «jika ada di cache, I/O asinkron pun selesai secara sinkron», dan di bagian 1 kita menjadikan «jalan pintas yang tidak membuat IRP (Fast I/O)» sebagai pekerjaan rumah. Kali ini semua benang itu ditarik.

1. Intinya dulu

  • Cache file Windows adalah cara write-back. Pembacaan dulu dari cache file sistem, penulisan juga dulu ke cache. Pencerminan ke disk dilakukan OS kemudian.1
  • Identitas cache adalah file mapping. Cache Manager memetakan rentang 256 KB file ke ruang alamat sistem, dan baca tulis menjadi «salinan memori antara tampilan itu dan buffer aplikasi» (bab 2).1
  • Penulisan dicerminkan kemudian oleh penulisan tertunda (lazy writer) setiap detik. Crash aplikasi tidak kehilangan data, tetapi pemutusan daya atau crash OS kehilangan cache dirty (bab 4).1
  • Ada tiga alat «menulis dengan andal». FlushFileBuffers (= Flush(true) .NET), FILE_FLAG_WRITE_THROUGH, FILE_FLAG_NO_BUFFERING. Flush setiap kali pada penulisan yang sering tidak efisien, dan dokumentasi resmi mencantumkan kombinasi NO_BUFFERING+WRITE_THROUGH (bab 5).21
  • NO_BUFFERING punya persyaratan penyelarasan. Ukuran dan offset adalah kelipatan ukuran sektor, alamat buffer juga diselaraskan ke batas sektor fisik. Dan bahkan dengan NO_BUFFERING, metadata terus di-cache (bab 5.3).31
  • Tampilan yang dipetakan dan cache berbagi data yang sama. Memory-mapped file dan I/O cache biasa koheren, dan persistensi peta adalah dua tahap FlushViewOfFile+FlushFileBuffers (bab 6).45
  • Baca tulis sinkron yang ada di cache kadang bahkan tidak membuat IRP. Jalan pintas bernama Fast I/O pergi langsung ke Cache Manager — jawaban pekerjaan rumah bagian 1 (bab 7).6

Peta pengetahuan artikel ini

Sukses WriteFile tidak berarti persistensi ke disk; cache file Windows pertama-tama menyalin ke tampilan cache sistem, dan pencerminan ke disk diikuti kemudian setiap detik oleh lazy writer. Saat pemutusan daya atau crash OS, halaman dirty yang belum ditulis hilang, jadi untuk data yang ingin ditulis dengan andal perlu memilah FlushFileBuffers, FILE_FLAG_WRITE_THROUGH, dan FILE_FLAG_NO_BUFFERING.

Peta pengetahuan Cache Manager dan write-behindDiagram yang menunjukkan hubungan Cache Manager, cache write-back, tampilan cache sistem 256 KB, read-ahead, penulisan tertunda oleh lazy writer, kehilangan halaman dirty saat pemutusan daya, penulisan andal dengan FlushFileBuffers, FILE_FLAG_WRITE_THROUGH, dan FILE_FLAG_NO_BUFFERING, persyaratan penyelarasan, koherensi dengan memory-mapped file, serta Fast I/Omengimplementasikanmenggunakanmenggunakanmenggunakanmenggunakanmenggunakanmengotomatiskanmengurangitidak kompatibeldapat menyebabkandisimpan didapat menyebabkanmencegahmenggunakandikonfigurasi dengandikonfigurasi denganmencegahmencegahmengurangimensyaratkanmencegahdapat menyebabkantidak disarankandisarankan untukdisarankan untuktidak kompatibelmensyaratkansebaiknya didahuluidiverifikasi denganmensyaratkantidak kompatibelmensyaratkanCache Managercache write-back (metode penulisan tertunda)file mapping (memory-mapped file)tampilan cache sistem (slot 256 KB)objek filelazy writer (thread penulisan tertunda)kehilangan data yang belum ditulis ke diskFILE_ATTRIBUTE_TEMPORARYhalaman dirtypemutusan daya atau crash OSread-ahead (pembacaan spekulatif)FILE_FLAG_SEQUENTIAL_SCANFILE_FLAG_RANDOM_ACCESSFlushFileBuffersFILE_FLAG_WRITE_THROUGHFILE_FLAG_NO_BUFFERINGpersyaratan penyelarasan sektorERROR_INVALID_PARAMETER(87)persistensi andal pada penulisan yang seringFlushViewOfFileFast I/OProcess Monitor (procmon.exe)IRP (paket permintaan I/O)I/O sinkron

Pada diagram, garis utuh menunjukkan relasi yang selalu berlaku dan garis putus-putus menunjukkan relasi bersyarat (syaratnya ada pada penjelasan masing-masing relasi di halaman rincian). Daftar lengkap relasi (total 32, beserta bukti dan tingkat kepastian) serta definisi konsep utama dikumpulkan di halaman rincian peta pengetahuan (dalam bahasa Jepang). Data: JSON-LD / Turtle

2. Identitas cache — file dipetakan ke memori

2.1. Slot 256 KB dan salinan memori

Jika cache file Windows dipikirkan sebagai «wadah blok disk», berbagai perilaku tidak dapat dijelaskan. Gambar yang benar adalah ini — Cache Manager memetakan rentang 256 KB file ke «slot» di ruang alamat sistem, dan baca tulis dengan cache aktif dieksekusi sebagai salinan memori antara slot itu dan buffer aplikasi.1

Ruang alamat sistemAplikasi (mode pengguna)ReadFile/WriteFile =salinan memori dengan slotPembacaan saat akses pertama danpenulisan kembali kemudian per halamanCache file sistemslot yang memetakan rentang 256 KB fileBuffer aplikasi(area yang diserahkan ke ReadFile/WriteFile)File di disk

Gambar 1: Wajah nyata I/O dengan cache aktif. «Baca tulis file» dari sudut aplikasi, dalam banyak kasus, hanyalah salinan memori.

Satu poin yang mudah disalahpahami. 256 KB adalah granularitas tampilan (peta), bukan berarti I/O disk selalu dilakukan dalam satuan 256 KB. Halaman di dalam slot dibaca sesuai kebutuhan, dan jumlah I/O yang benar-benar terbang ke disk berubah menurut ukuran permintaan dan pola akses. Jika rentang yang dibaca pertama kali, I/O disk terjadi untuk mengisi (di sini IRP bagian 1 terbang ke storage stack di bawah). Jika sudah ada di cache, pembacaan selesai hanya dengan salinan. «Cache hit membuat penerbitan asinkron pun selesai secara sinkron» yang kita lihat di bab 5 bagian 2 adalah perwujudan gerakan «permintaan yang dapat dijawab segera diselesaikan di tempat». Sebaliknya, jika cache aktif tetapi halaman tidak ada di memori, tidak ada mekanisme asinkron untuk penanganan page fault, jadi pembacaan asinkron kadang diproses secara sinkron — jebakan yang juga kita lihat di bagian 2.7

2.2. Identitas «memori bebas berkurang»

Apakah cache dipakai dan keadaan read-ahead dikelola per cara membuka (per objek file),1 tetapi isi data yang di-cache dibagi per file (stream). Membuka file yang sama berkali-kali tidak membuat cache terpisah; isi cache yang sama terlihat dari handle mana pun (fondasi koherensi bab 6). Cache bergerak di bawah komando Cache Manager selama Windows berjalan.1 Jika file besar disalin atau banyak baca tulis, memori fisik kosong terus dialihkan ke cache. Meski memori bebas Task Manager tampak berkurang, sebagian besar adalah «memori siaga yang dipakai dengan cara bernilai, yang segera diserahkan jika aplikasi meminta». Agar diagnosis kekurangan memori tidak salah membedakan ini, praktik pengamatan juga dibahas di «Membedakan menunggu GC dan kebocoran memori di .NET».

Gerakan ini dapat dikonfirmasi di layar. Buka Task Manager > Kinerja > Memori, dan bilah «komposisi memori» di bawah terbagi menjadi sedang dipakai / diubah / siaga / kosong. Sebagian besar cache file masuk ke siaga ini, dan di daftar kanan dijumlahkan sebagai «sudah di-cache». Untuk melihat lebih rinci, tab Memori Resource Monitor menyusun pembagian yang sama dengan kapasitas. Setelah menyalin satu file beberapa GB lalu melihat lagi, siaga bertambah dan kosong berkurang, tetapi «sedang dipakai» hampir tidak berubah — yaitu, bukan «memori dimakan habis» melainkan «memori yang kosong dipakai sebagai cache» dapat dikonfirmasi dengan mata.

3. Read-ahead — spekulasi pembacaan

Cache Manager, dari pola akses masa lalu, membaca lebih dulu rentang yang kemungkinan dibaca berikutnya (read-ahead). Jika file dibaca berurutan, data lanjutan sudah ada di cache sebelum aplikasi memintanya — inilah rahasia kecepatan pembacaan sekuensial. Jumlah read-ahead tidak tetap; ia berubah menurut pola yang terdeteksi dan ukuran permintaan.

Riwayat permintaan baca aplikasimembaca berurutan dari awalCache Managermendeteksi polaRead-ahead: rentang lanjutandibaca sebelum diminta(jumlah berubah menurut pola dan ukuran permintaan)Petunjuk FILE_FLAG_SEQUENTIAL_SCAN= read-ahead secara aktifPetunjuk FILE_FLAG_RANDOM_ACCESS= read-ahead menjadi sia-sia jadi ditekan

Gambar 2: Read-ahead. Selain deteksi pola akses, petunjuk dapat diberi lewat bendera CreateFile.

FileOptions.SequentialScan / RandomAccess yang diletakkan di tabel korespondensi bagian 1 adalah petunjuk ke mesin read-ahead ini. Untuk pemrosesan batch «menjilat semuanya» yang pertama, untuk akses seperti menelusuri indeks yang kedua — jika dipikirkan sebagai bendera untuk mengajarkan masa depan yang hanya diketahui aplikasi kepada OS, tempat pemakaiannya menjadi jelas.

4. Penulisan tertunda — arti «sukses» WriteFile

4.1. Lazy writer datang setiap detik

Sisi penulisan adalah cache write-back. WriteFile mengembalikan sukses pada saat data disalin ke slot, dan pencerminan ke disk ditunda. Kebijakan «tunda lalu tulis» ini adalah penulisan tertunda (lazy writing).1

Yang menanggung pencerminan adalah lazy writer yang dijalankan Cache Manager setiap detik. Ia menumpuk seperdelapan halaman yang belum di-flush baru-baru ini ke antrean dan menuliskannya, dan jika data yang harus ditulis banyak, ia menumpuk lebih banyak. Selain itu, file sementara yang dibuat dengan atribut FILE_ATTRIBUTE_TEMPORARY dikeluarkan dari sasaran flush lazy writer — karena menulis yang akan segera dihapus hanya sia-sia.1 Namun ini petunjuk lewat atribut, dan jika memori terdesak ia dapat ditulis kembali; file yang «namanya saja tampak sementara» tidak mendapatkannya.

Disklazy writer (berjalan setiap detik)Cache sistemAplikasiDisklazy writer (berjalan setiap detik)Cache sistemAplikasiMenyalin ke slot danmenandai halaman dirty (belum ditulis)Dari sini sampai penulisan kembali adalah «jendela berbahaya»jika pemutusan daya / crash OS, data ini hilangBaru di sini dibuat tahan lamaWriteFile(data)TRUE segera dikembalikanMemilih 1/8 halaman dirtyMenulis kembali secara massal

Gambar 3: Penulisan tertunda. Sukses WriteFile adalah «diserahkan ke OS», bukan «dibuat tahan lama».

4.2. Jika apa yang terjadi, sampai di mana yang hilang

Mari buat arti «jendela berbahaya» tepat. Nasib terpecah menurut jenis kegagalan.

Data segera setelah WriteFile berhasil(halaman dirty di cache)Apa yang terjadiProses aplikasicrash / dihentikan paksaSeluruh OS berhenti(pemutusan daya / layar biru)Data tetap adacache milik OS jadilazy writer menulis kembali sesuai rencanaHalaman dirty hilanghanya yang sudah sampai ke disk yang tersisa

Gambar 4: Jenis kegagalan dan garis pemisah kelangsungan. Cache adalah «milik OS», bukan «milik proses».

  • Meski aplikasi mati, data tidak hilang. Pada saat salinan ke cache selesai, pemilik data adalah OS. «Aplikasi jatuh segera setelah menyimpan tetapi file selamat» berkat ini.
  • Jika seluruh OS mati, bagian dirty hilang. Frekuensi flush disesuaikan sebagai trade-off kinerja dan keandalan, dan dokumentasi juga menyatakan dengan tegas bahwa «jika kegagalan sistem tiba-tiba seperti kehilangan daya terjadi, data yang di-cache hilang».1

Jadi pertanyaan dalam desain aplikasi bisnis adalah, «apakah data ini boleh hilang pada saat pemutusan daya». Beberapa detik log mungkin boleh. Rekaman penegasan data pesanan mungkin tidak. Hanya yang tidak boleh, memakai alat bab berikutnya.

5. Kotak alat membuat «sudah ditulis dengan andal»

5.1. FlushFileBuffers — tulis habis sekarang

FlushFileBuffers menulis habis data yang di-buffer untuk file yang ditetapkan ke perangkat. Metadata sistem file selalu di-cache, jadi untuk memastikan metadata juga sampai, flush (atau WRITE_THROUGH) diperlukan; itu juga poin yang harus dipegang.12 Di .NET, FileStream.Flush(true) adalah padanannya (Flush() saja hanya menyerahkan buffer internal .NET ke OS, dan cache OS tetap apa adanya).8

Namun dokumentasi resmi dengan jelas menancapkan paku — memanggil setiap kali pada setiap penulisan tidak efisien. Jika persistensi setiap kali diperlukan pada banyak penulisan, pakailah NO_BUFFERING+WRITE_THROUGH yang dibahas kemudian, katanya.2

5.2. FILE_FLAG_WRITE_THROUGH — hanya menghilangkan penundaan

Jika dibuka dengan FILE_FLAG_WRITE_THROUGH, penulisan ditulis juga ke cache, sekaligus ditulis segera ke disk tanpa menunggu lazy writer.1 Poinnya adalah pembacaan tetap mendapat manfaat cache, jawaban lurus terhadap «biarkan pembacaan cepat, hanya hilangkan penundaan penulisan».

5.3. FILE_FLAG_NO_BUFFERING — tidak lewat cache

FILE_FLAG_NO_BUFFERING mengeluarkan cache sistem itu sendiri dari baca tulis. Setiap baca tulis menjadi I/O ke perangkat disk setiap kali, tanpa lewat cache.1 Namun yang dapat dilewati hanya sampai cache sistem Windows, dan seperti Gambar 5 cache tulis di dalam perangkat adalah tahap terpisah. Jika ketahanan sampai pemutusan daya dituntut, kombinasi WRITE_THROUGH atau FlushFileBuffers tetap diperlukan. Ini alat untuk transfer massal data besar atau mesin basis data yang mengelola buffer sendiri, tetapi perjanjian ketat menyertainya.3

  • Ukuran dan offset file baca tulis adalah kelipatan ukuran sektor volume (jika sektor 512 byte: 512, 1024, 1536…).
  • Alamat buffer juga diselaraskan ke ukuran sektor fisik (pertimbangan disk «Advanced Format» dengan sektor fisik 4096 byte juga diperlukan).
  • Meski begitu metadata terus di-cache, jadi untuk persistensi penuh kombinasi WRITE_THROUGH atau FlushFileBuffers diperlukan.12

«Perjanjian» ini adalah tempat orang yang mencoba selesai hanya dengan menambah bendera jatuh pertama kali. Jika baca tulis tanpa menaati penyelarasan, gagal dengan ERROR_INVALID_PARAMETER (87). Tiga poin yang harus ditaati ditata.3

Yang diselaraskan Syarat Cara memenuhinya
Ukuran baca tulis Kelipatan ukuran sektor volume Ambil lpBytesPerSector GetDiskFreeSpace dan bulatkan ke kelipatannya
Offset file Sama (juga ketika ditetapkan dengan Offset OVERLAPPED) Maju kelipatan ukuran sektor
Alamat buffer Diselaraskan ke ukuran sektor fisik Alokasikan dengan VirtualAlloc (area yang diselaraskan ke batas halaman = biasanya 4096 byte dikembalikan)

Yang ketiga khususnya terlewat. Alamat yang dikembalikan malloc atau new, atau array C#, tidak punya jaminan penyelarasan ke batas sektor. Memakai VirtualAlloc yang dapat mengalokasikan di batas halaman memenuhi juga persyaratan disk «Advanced Format» dengan sektor fisik 4096 byte. Bentuk minimumnya seperti ini.

// C++ / Win32. Penanganan error dibuat seminimal mungkin
DWORD sectorsPerCluster = 0, bytesPerSector = 0, freeClusters = 0, totalClusters = 0;
if (!GetDiskFreeSpaceW(L"C:\\", &sectorsPerCluster, &bytesPerSector,
                       &freeClusters, &totalClusters))
{
    return GetLastError();
}

// Jadikan satuan baca tulis kelipatan ukuran sektor (di sini setara 1MiB)
const DWORD chunk = (1024 * 1024 / bytesPerSector) * bytesPerSector;

// Buffer mengambil area yang diselaraskan ke batas halaman (malloc/new tidak menjamin ini)
BYTE* buffer = static_cast<BYTE*>(
    VirtualAlloc(nullptr, chunk, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
if (buffer == nullptr) { return GetLastError(); }

HANDLE h = CreateFileW(L"C:\\temp\\big.bin", GENERIC_READ, FILE_SHARE_READ, nullptr,
                       OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, nullptr);
if (h == INVALID_HANDLE_VALUE)
{
    // GetLastError mengembalikan hasil «panggilan Win32 terakhir». Jika
    // VirtualFree dipanggil lebih dulu, alasan gagalnya CreateFileW (akses ditolak,
    // path tidak ada, dll.) ditimpa hasil pembersihan, dan yang kembali hanyalah
    // kode yang tidak diketahui penyebabnya
    const DWORD err = GetLastError();
    VirtualFree(buffer, 0, MEM_RELEASE);
    return err;
}

// Karena setiap kali maju dalam satuan chunk byte, ukuran maupun offset tetap selaras
DWORD read = 0;
while (ReadFile(h, buffer, chunk, &read, nullptr) && read > 0)
{
    // Proses read byte pertama dari buffer
    // (di akhir file, read < chunk. Ini normal)
}

CloseHandle(h);
VirtualFree(buffer, 0, MEM_RELEASE);

Selain itu, FileOptions .NET tidak punya nilai yang sesuai FILE_FLAG_NO_BUFFERING. Jika benar-benar diperlukan, Anda akan memanggil CreateFile secara langsung, tetapi dalam kasus itu pun persyaratan penyelarasan di atas harus ditaati sendiri. Pertimbangkan dalam urutan «NO_BUFFERING karena mengelola buffer sendiri», bukan «NO_BUFFERING karena ingin cepat».

5.4. Penataan pemilahan

WriteFile default: sukses dikembalikan sampai di sinilazy writer (setiap detik) / WRITE_THROUGH (segera)Waktu perangkat /FlushFileBuffers menuntut penulisan habisNO_BUFFERING melewati cache dan pergi langsungBuffer aplikasiCache file sistem(halaman dirty)Cache di dalam perangkat diskMedia rekam non-volatile

Gambar 5: Hierarki data, dan seberapa jauh setiap alat mendorongnya. Perhatikan juga tahap terakhir «cache di dalam perangkat disk».

Metode Apa yang terjadi Adegan yang cocok
Default (cache aktif) Selesai dengan salinan cache. Pencerminan oleh lazy writer Hampir semua I/O file
FlushFileBuffers / Flush(true) Menulis habis data + metadata pada saat itu Penegasan di titik (commit transaksi dll.)
FILE_FLAG_WRITE_THROUGH Ke disk segera per penulisan (pembacaan tetap cache) Log / jurnal dengan penulisan yang tidak boleh hilang yang berlanjut
FILE_FLAG_NO_BUFFERING (+WRITE_THROUGH) Tidak lewat cache. Ada persyaratan penyelarasan Pengelolaan buffer sendiri / I/O massal besar

Urutan memilih adalah dua tahap: pertama putuskan «berapa banyak yang boleh hilang saat pemutusan daya», lalu konfirmasikan «seberapa lambat yang boleh sebagai gantinya». Jangan memandang tabel dari atas; telusuri percabangan ini.

Boleh(log beberapa detik terakhir dll.)Tidak bolehTitik(penegasan transaksi dll.)Setiap entriTidak (aplikasi biasa)Ya (mesin DB dll.)Akan menulis data iniBoleh hilang pada saatpemutusan daya / layar biru?Tetap default (cache aktif)paling cepat. Hampir semua I/O di siniYang tidak boleh hilang«titik» atau «setiap entri»?FlushFileBuffers di titik.NET: Flush(true)biaya: hanya menunggu di titikDapat mengelola buffer sendiri danmemenuhi persyaratan penyelarasan 5.3?FILE_FLAG_WRITE_THROUGHke disk segera per penulisanpembacaan tetap cepat dengan cacheFILE_FLAG_NO_BUFFERING+ FILE_FLAG_WRITE_THROUGHbentuk «persistensi sering» yang dicantumkan resmi

Gambar 6: Cara memilih alat. Percabangan tahap 1 adalah tuntutan keandalan, tahap 2 adalah biaya yang dapat dibayar. Tidak ada jalan «FlushFileBuffers setiap entri» karena, seperti yang kita lihat di 5.1, dokumentasi resmi menganggapnya tidak efisien.

Tiga pola praktis saja dicantumkan.

  1. «Tulis ke file sementara → flush → ganti nama» adalah praktik standar agar tidak meninggalkan file setengah rusak. Menulis habis isi lalu menegaskan dengan nama — serah terima atomik ini dibahas secara rinci di «Pengetahuan dasar pengendalian eksklusif integrasi file».
  2. Menyerahkan ke basis data juga desain yang layak. Cerita SQLite membangun ketahanan dengan WAL dan flush lihat «Memakai SQLite di aplikasi bisnis dengan C#». Pilihan «tidak menulis strategi flush sendiri» selalu ada.
  3. Curigai cache pada tolok ukur. Pengukuran «pembacaan terlalu cepat» biasanya mengukur cache hit dari kali kedua dan seterusnya. Tata cara pengukuran dirangkum di «Cara membandingkan kecepatan per versi program dengan benar di Windows».

Selain itu jangan lupa tahap terakhir Gambar 5 — cache di dalam perangkat disk. FlushFileBuffers menuntut penulisan habis termasuk itu, tetapi pada USB dan disk eksternal kebijakan cache tulis sisi perangkat («pelepasan cepat» dan «kinerja tinggi») terlibat. Penanganan perangkat yang dapat dilepas juga lihat «Cara menangani perangkat USB di aplikasi Windows».

6. Koherensi dengan memory-mapped file

Di bagian 1 kita mendengar «identitas cache adalah file mapping», dan pasti ada yang berpikir — lalu, apakah tampilan yang Anda MapViewOfFile sendiri dan cache ReadFile/WriteFile bertengkar?

Tidak. Karena mereka duduk di atas mekanisme yang sama. Objek file mapping ditopang file, dan pengusiran halaman dilakukan sebagai penulisan kembali ke file. Bahkan jika beberapa proses membuat tampilan terhadap file lokal yang sama, isi yang terlihat koheren (konsisten).4

Ruang alamat sistemRuang alamat proses ATampilan Cache Manager(slot yang dipakai ReadFile/WriteFile)Tampilan MapViewOfFileKelompok halaman fisik yang sama(memori yang ditopang file)File di diskI/O FILE_FLAG_NO_BUFFERINGdi luar kerangka berbagi ini (langsung ke disk)

Gambar 7: Tampilan yang dipetakan maupun cache melihat «halaman yang ditopang file» yang sama. Yang di luar kerangka hanyalah NO_BUFFERING.

Dua perhatian.

  • I/O FILE_FLAG_NO_BUFFERING di luar kerangka koherensi ini. Baca tulis yang tidak lewat cache dan isi lewat tampilan yang dipetakan/cache tidak dicocokkan. Jika dicampur, Anda sendiri yang harus mengambil keselarasan.
  • Persistensi tampilan yang dipetakan adalah dua tahap. FlushViewOfFile memulai penulisan halaman dirty dalam rentang, tetapi tidak menulis metadata, dan tidak menunggu penulisan fisik dari cache perangkat disk. Untuk memastikan sampai, panggil FlushFileBuffers setelah FlushViewOfFile.5

Praktik file mapping sebagai memori bersama (berbagi bernama, sinkronisasi, pola kecelakaan) dibahas di «Jebakan memori bersama dan praktik terbaik».

7. Fast I/O — menarik pekerjaan rumah bagian 1

Dua baris dulu. Fast I/O adalah jalan pintas yang disediakan untuk baca tulis sinkron ke file yang ada di cache, yang bertukar data langsung dengan cache tanpa merakit IRP (I/O Request Packet; wadah permintaan yang kernel serahkan ke driver). IRP_MJ_READ dan FASTIO_READ yang tercampur di kolom Operation Procmon adalah perbedaan apakah «pembacaan» yang sama lewat jalur biasa atau jalan pintas.

Di bab 5.2 bagian 1 kita menulis «tidak semua I/O menjadi IRP». Ini jawaban ujiannya.

Sudah diketahui bahwa baca tulis file yang ada di cache dapat selesai dengan salinan memori dengan cache, tanpa merakit IRP dan mengalirkannya di device stack. Karena itu Windows menyediakan jalan pintas bernama Fast I/O untuk I/O sinkron ke file yang di-cache — jalur yang tidak membuat IRP, memanggil «titik masuk Fast I/O» sistem file secara langsung, dan menyalin langsung dari Cache Manager.6 Jika tidak dapat diproses dengan Fast I/O (tidak ada di cache, kunci terlibat, filter menyela, dll.), ia berbalik ke jalur IRP biasa. Selain itu, ini jalur cepat untuk permintaan sinkron, bukan «cache hit = selalu Fast I/O». Operasi handle asinkron (FILE_FLAG_OVERLAPPED) kadang diproses di jalur IRP bahkan ketika selesai di tempat dari cache (bab 5 bagian 2).

DapatTidak dapatBaca tulis sinkron ke handle dengan cache aktifDapat diproses dengan Fast I/O(ada di cache dll.)?Fast I/Omenyalin langsung dengan cache tanpa membuat IRPdi Procmon ditampilkan sebagai FASTIO_Jalur biasamerakit IRP ke device stack(dunia Gambar 6 bagian 1)

Gambar 8: Percabangan Fast I/O. Inilah sebabnya FASTIO_READ dan IRP_MJ_READ tampak tercampur di Procmon.

Alasan baris FASTIO_ tercampur di pengamatan Procmon bab 7 bagian 1 kini dapat dijelaskan. Pembacaan cache hit, bahkan IRP pun barang mewah. Keberadaan jalur ini juga memengaruhi driver filter yang dibahas di bagian 6 (minifilter dibuat agar dapat menyela Fast I/O juga).

8. Ringkasan

  • Cache file Windows adalah write-back, dan identitasnya adalah pemetaan rentang 256 KB file. Baca tulis dengan cache aktif menjadi salinan memori dengan slot.1
  • Pembacaan read-ahead berspekulasi, dan SequentialScan/RandomAccess adalah petunjuknya.1
  • Penulisan diikuti kemudian oleh lazy writer setiap detik. Meski aplikasi mati data tetap ada, jika seluruh OS mati hanya bagian dirty yang hilang. Pertanyaan desain adalah «apakah data ini boleh hilang pada saat pemutusan daya».1
  • Alat menulis dengan andal adalah FlushFileBuffers (penegasan di titik) / WRITE_THROUGH (per penulisan) / NO_BUFFERING (tidak lewat cache + persyaratan penyelarasan). Flush setiap kali tidak efisien, dan untuk persistensi yang sering kombinasi NO_BUFFERING+WRITE_THROUGH adalah rekomendasi resmi. Perhatikan juga bahwa metadata selalu di-cache.231
  • Tampilan yang dipetakan dan cache berbagi halaman yang sama dan koheren. Yang di luar kerangka hanya NO_BUFFERING. Persistensi peta adalah dua tahap FlushViewOfFile+FlushFileBuffers.45
  • Baca tulis sinkron cache hit bahkan IRP dihilangkan dengan Fast I/O. Identitas FASTIO_ yang dilihat di Procmon bagian 1.6

Lanjutannya adalah bagian 5 «Interior NTFS — memahami sistem file lewat MFT». Sampai kali ini file diperlakukan sebagai «offset dan rangkaian byte», tetapi di belakangnya bagaimana NTFS menata data — MFT, beberapa data stream, jurnal, hard link — kita turun ke struktur statis di atas disk.

Artikel terkait

Area konsultasi terkait

KomuraSoft LLC menangani desain dan investigasi bug I/O file aplikasi bisnis Windows, seperti «data yang seharusnya sudah disimpan hilang» atau «penulisan file lambat / terlalu cepat sehingga mencurigakan».

Tautan referensi

  1. Microsoft Learn, File Caching. Tentang Windows yang secara default meng-cache data file, pembacaan yang dilakukan dari cache file sistem, dan penulisan juga ke cache sebagai cache write-back; cache yang dikelola per objek file dan bergerak di bawah komando Cache Manager; kebijakan menunda penulisan ke disk dan menahannya di cache yang disebut penulisan tertunda (lazy writing); saat pembacaan file, rentang 256 KB yang dibaca ke slot 256 KB di ruang alamat sistem, dan proses pengguna yang menyalin data dengan slot itu; Cache Manager yang menjalankan lazy writer setiap detik, menumpuk seperdelapan halaman yang belum di-flush baru-baru ini ke antrean penulisan disk, dan menumpuk lebih banyak jika perlu; file sementara yang tidak di-flush; bahwa jika kegagalan sistem tiba-tiba seperti kehilangan daya terjadi, data cache yang belum ditulis hilang; bahwa menonaktifkan cache dengan FILE_FLAG_NO_BUFFERING pun metadata file masih dapat di-cache; bahwa dengan FILE_FLAG_WRITE_THROUGH data ditulis juga ke cache sekaligus ditulis segera ke disk tanpa penundaan lazy writer; dan bahwa metadata sistem file selalu di-cache jadi persistensi metadata mensyaratkan flush atau FILE_FLAG_WRITE_THROUGH.  2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

  2. Microsoft Learn, FlushFileBuffers function. Tentang WriteFile yang biasanya menulis ke buffer internal, dan OS yang secara berkala menulis ke disk; FlushFileBuffers yang menulis semua informasi yang di-buffer untuk file yang ditetapkan ke perangkat; bahwa memanggil setiap kali pada banyak penulisan tidak efisien, dan aplikasi yang memerlukan persistensi data penting pada penulisan yang sering harus memakai I/O tanpa buffer dengan FILE_FLAG_NO_BUFFERING dan FILE_FLAG_WRITE_THROUGH; dan bahwa memanggil terhadap handle volume (dengan hak administrator) dapat mem-flush semua file terbuka di volume.  2 3 4 5

  3. Microsoft Learn, File Buffering. Tentang persyaratan akses ke file yang dibuka dengan FILE_FLAG_NO_BUFFERING: ukuran baca tulis dan offset file (termasuk ketika ditetapkan dengan OVERLAPPED) harus kelipatan ukuran sektor volume; alamat buffer baca tulis harus diselaraskan ke ukuran sektor fisik; dan pertimbangan perangkat Advanced Format dengan sektor fisik 4.096 byte yang diperlukan.  2 3 4

  4. Microsoft Learn, File Mapping. Tentang objek file mapping yang ditopang file di disk, dan swap-out halaman yang dilakukan sebagai penulisan perubahan ke file; dan bahwa ketika beberapa proses membuat tampilan file lokal dari objek file mapping yang sama, data koheren (isi yang sama dengan file di disk).  2 3

  5. Microsoft Learn, FlushViewOfFile function. Tentang FlushViewOfFile yang memulai penulisan halaman dirty dalam rentang tampilan yang dipetakan ke disk; fungsi ini yang tidak mem-flush metadata file, dan tidak menunggu penyelesaian penulisan fisik dari cache disk perangkat keras; dan bahwa untuk menulis habis semua halaman dirty dan metadata secara fisik, FlushFileBuffers harus dipanggil setelah FlushViewOfFile.  2 3

  6. Microsoft Learn, IRPs Are Different From Fast I/O. Tentang Fast I/O sebagai jalur cepat I/O sinkron untuk file yang di-cache, yang memanggil titik masuk sistem file atau Cache Manager secara langsung tanpa menghasilkan IRP; data yang ditransfer langsung dari cache ke buffer pengguna (atau sebaliknya); dan bahwa jika tidak dapat diproses dengan Fast I/O, jalur biasa berbasis IRP dipakai.  2 3

  7. Microsoft Learn, Asynchronous disk I/O appears as synchronous on Windows. Tentang permintaan yang selesai di tempat dan mengembalikan TRUE ketika data ada di cache; cache Windows yang diimplementasikan sebagai file mapping, dan karena tidak ada mekanisme page fault asinkron ketika halaman tidak ada, pembacaan asinkron dengan cache aktif kadang diproses secara sinkron. 

  8. Microsoft Learn, FileStream.Flush method (.NET). Tentang Flush() yang menulis buffer internal stream ke OS; dan bahwa menetapkan Flush(true) di atas itu juga mem-flush semua buffer file perantara (buffer OS). 

Artikel terbaru dengan tag yang sama untuk mendalami topik-topik terdekat.

Halaman-halaman ini menempatkan topik dalam konteks layanan dan keputusan yang lebih luas.

Artikel ini berkaitan langsung dengan layanan berikut.

Pertanyaan yang sering diajukan

Pertanyaan yang sering muncul dalam konsultasi tentang topik artikel ini.

Pada saat WriteFile mengembalikan sukses, apakah data sudah ditulis ke disk?
Secara default, belum. Cache file Windows adalah cache write-back, dan WriteFile mengembalikan sukses begitu data disalin ke cache file sistem. Penulisan ke disk dilakukan kemudian oleh lazy writer Cache Manager, yang berjalan sekali per detik. Yang penting adalah perbedaan jenis kegagalan. Jika proses aplikasi crash, data yang sudah masuk cache tidak hilang, karena OS kemudian menulisnya kembali selama OS sendiri masih hidup. Jika seluruh OS jatuh — pemutusan daya, layar biru — data cache dirty yang belum ditulis kembali hilang. Cara membaca «WriteFile berhasil» yang akurat bukan «sudah dibuat tahan lama» melainkan «sudah diserahkan ke OS».
Bagaimana memastikan data benar-benar sampai ke disk?
Ada tiga alat. Pertama, FlushFileBuffers, yang menulis semua data dan metadata yang di-buffer untuk file itu sampai ke perangkat (di .NET, FileStream.Flush(true) adalah padanannya). Kedua, FILE_FLAG_WRITE_THROUGH, yang menulis ke cache pada setiap penulisan sekaligus menulis ke disk segera. Ketiga, FILE_FLAG_NO_BUFFERING, yang melewati cache sama sekali. Dokumentasi Microsoft sendiri mencatat bahwa memanggil FlushFileBuffers pada setiap penulisan tidak efisien, dan bahwa aplikasi yang memerlukan ketahanan andal dengan penulisan yang sering harus menggabungkan FILE_FLAG_NO_BUFFERING dengan FILE_FLAG_WRITE_THROUGH. Setiap satunya menyerahkan sebagian manfaat cache sehingga menjadi lebih lambat, jadi aturan praktisnya bukan menempelkannya ke semua melainkan menyimpannya untuk penulisan yang benar-benar tidak boleh hilang.
Apa perbedaan FILE_FLAG_WRITE_THROUGH dan FILE_FLAG_NO_BUFFERING?
WRITE_THROUGH berarti «tetap menulis ke cache, tetapi juga menulis ke disk sebelum selesai». Pembacaan tetap mendapat manfaat cache; hanya penundaan lazy writer pada penulisan yang dihilangkan. NO_BUFFERING berarti «baca tulis melewati cache sistem sepenuhnya», jadi keduanya menjadi I/O perangkat pada setiap panggilan (meskipun yang dilewati hanya cache Windows sendiri — ia tidak melewati cache tulis yang duduk di dalam perangkat penyimpanan itu sendiri). Sebagai gantinya, ia datang dengan batasan ketat: ukuran dan offset file setiap baca atau tulis harus kelipatan tepat ukuran sektor volume, dan alamat buffer itu sendiri harus diselaraskan ke batas sektor fisik. Selain itu, bahkan dengan NO_BUFFERING, metadata sistem file terus di-cache, jadi membuat metadata tahan lama tetap mensyaratkan FlushFileBuffers atau WRITE_THROUGH di atasnya. Biasanya dipakai perangkat lunak yang mengelola buffering sendiri, seperti mesin basis data; aplikasi biasa pada umumnya harus meraih WRITE_THROUGH atau FlushFileBuffers dulu.
Task Manager menunjukkan sedikit memori bebas — apakah itu salah cache file?
Sering, ya, dan itu perilaku normal. Windows secara aktif memakai memori fisik kosong sebagai cache file, jadi menyalin file besar atau banyak membaca dan menulis akan membengkak cache dan membuat pemakaian memori tampak lebih tinggi. Meski begitu, sebagian besar halaman yang dipakai cache adalah jenis yang dapat diambil kembali cukup cepat begitu aplikasi meminta memori, yang perlu dibedakan dari benar-benar kehabisan memori. Ketika Anda mencurigai kekurangan nyata, lebih berguna melihat memori yang dikomit dan frekuensi hard fault daripada jumlah memori bebas yang tampak saja.
Jika saya menyentuh file yang sama lewat tampilan memory-mapped dan lewat ReadFile/WriteFile, bisakah isinya tidak selaras?
Tidak terhadap I/O biasa dengan cache aktif. Cache Windows sendiri diimplementasikan sebagai file mapping, jadi tampilan yang dipetakan dan cache untuk file lokal yang sama berbagi data yang sama — perubahan lewat yang satu terlihat lewat yang lain. Beberapa proses yang membuat tampilan dari objek file mapping yang sama juga melihat data yang koheren. Namun, baca tulis pada handle yang dibuka dengan FILE_FLAG_NO_BUFFERING melewati cache dan jatuh di luar koherensi ini. Selain itu, untuk secara andal menahan perubahan yang dibuat lewat tampilan yang dipetakan, FlushViewOfFile saja tidak cukup — ia tidak menulis metadata dan tidak menunggu cache perangkat keras — jadi Anda perlu memanggil FlushFileBuffers setelah FlushViewOfFile.

Profil penulis

Halaman perkenalan penulis artikel.

Go Komura

Direktur KomuraSoft LLC

Berspesialisasi dalam pengembangan perangkat lunak Windows, konsultasi teknis, dan investigasi bug, terutama pada proyek dengan sistem yang sudah ada dan bug yang sulit direproduksi.

Kembali ke blog