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

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

Riwayat revisi (2 pembaruan, terakhir pada 1 Sep 2026)

Catatan perubahan yang dilakukan pada artikel ini. Jika versi sebelumnya telah diarsipkan, versi itu tetap dapat dibaca melalui tautan permanen dengan DOI.

Perbaikan tinjauan Codex: delimiter tabel berlebih pada baris FlushFileBuffers dihapus.
Diterjemahkan ulang sebagai terjemahan lengkap dari naskah Jepang. Versi bahasa Indonesia sebelumnya adalah ringkasan yang hanya memindahkan sebagian naskah, sehingga bagian, tabel, gambar Mermaid, keterangan gambar, dan FAQ tidak ada. Semuanya dipulihkan sesuai naskah Jepang, dan klaim teknisnya sama dengan versi Jepang.
Publikasi pertama
Mengutip artikel ini(DOI (arsip terdaftar): 10.5281/zenodo.22175318)

DOI di bawah mengarah ke versi yang telah diarsipkan sebelumnya dan mungkin berbeda dari teks saat ini. Gunakan URL halaman ini untuk merujuk teks saat ini.

Go Komura (2026). Kedalaman I/O Windows (bagian 4) — Cache Manager: kapan WriteFile Anda sampai ke disk. KomuraSoft LLC. https://comcomponent.com/id/blog/windows-cache-manager-writefile-disk/

DOI (arsip terdaftar)
10.5281/zenodo.22175318
DOI (versi terakhir yang didaftarkan)
10.5281/zenodo.22175319

WriteFile mengembalikan sukses. Jadi, di mana data itu sekarang?

Jawabannya, hampir pasti belum ada di disk. Data hanya disalin ke cache di memori. Itulah sebabnya «sudah disimpan, tetapi setelah pemutusan daya isinya hilang» terjadi; itulah sebabnya tolok ukur salin file bisa menembus kecepatan yang secara fisik mustahil; dan itulah sebabnya basis data dengan taat memanggil fsync.

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

1. Kesimpulan dulu

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

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. Wujud cache — file dipetakan ke memori

2.1. Slot 256KB dan salinan memori

Jika cache file Windows dibayangkan sebagai «wadah blok disk», banyak perilakunya tidak terjelaskan. Gambar yang benar adalah ini — Cache Manager memetakan rentang file per 256KB ke «slot» di ruang alamat sistem, dan baca-tulis dengan cache aktif dijalankan 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 256KB fileBuffer aplikasi(area yang diserahkan ke ReadFile/WriteFile)File di disk

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

Satu hal yang mudah disalahpahami. 256KB adalah granularitas tampilan (peta), bukan berarti I/O disk selalu dilakukan dalam satuan 256KB. 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 itu dibaca pertama kali, I/O disk terjadi untuk mengisinya (di sinilah 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 terlihat di bab 5 bagian 2 adalah wujud gerakan «permintaan yang bisa dijawab segera diselesaikan di tempat». Sebaliknya, jika cache aktif tetapi halaman tidak ada di memori, tidak ada mekanisme asinkron untuk penanganan page fault, sehingga pembacaan asinkron kadang diproses secara sinkron — jebakan yang juga sudah terlihat di bagian 2.7

2.2. Apa yang sebenarnya terjadi saat «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 di bab 6). Cache bergerak di bawah komando Cache Manager selama Windows berjalan.1 Jika file besar disalin atau banyak baca-tulis, memori fisik yang kosong terus dialihkan ke cache. Meski memori bebas di Task Manager tampak berkurang, sebagian besar adalah «memori siaga yang dipakai dengan cara bernilai, dan 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 bisa dicek di layar. Buka Task Manager > Kinerja > Memori, lalu lihat bilah «komposisi memori» di bagian bawah yang terbagi menjadi Digunakan / Diubah / Siaga / Kosong. Sebagian besar cache file masuk ke Siaga, dan di daftar sebelah kanan dijumlahkan sebagai «Di-cache». Untuk melihat lebih rinci, tab Memori di Resource Monitor menampilkan pembagian yang sama beserta kapasitasnya. Salin satu file berukuran beberapa GB, lalu lihat lagi: Siaga naik dan Kosong turun, tetapi «Digunakan» hampir tidak berubah — jadi yang terlihat bukan «memori habis dimakan», melainkan «memori yang kosong dipakai sebagai cache».

3. Read-ahead — spekulasi pada pembacaan

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

Riwayat permintaan baca aplikasidibaca berurutan dari awalCache Managermendeteksi polaRead-ahead: rentang lanjutandibaca sebelum diminta(jumlahnya berubah menurut pola dan ukuran permintaan)Petunjuk FILE_FLAG_SEQUENTIAL_SCAN= read-ahead lebih agresifPetunjuk FILE_FLAG_RANDOM_ACCESS= read-ahead akan sia-sia, jadi ditekan

Gambar 2: Read-ahead. Selain deteksi pola akses, petunjuk juga bisa diberikan lewat flag CreateFile

FileOptions.SequentialScan / RandomAccess yang tercantum di tabel korespondensi bagian 1 adalah petunjuk ke mesin read-ahead ini. Yang pertama untuk pemrosesan batch yang «menelusuri semuanya», yang kedua untuk akses seperti menelusuri indeks — jika dipikir sebagai flag untuk memberi tahu OS masa depan yang hanya diketahui aplikasi, tempat memakainya menjadi jelas.

4. Penulisan tertunda — arti «sukses» dari WriteFile

4.1. Lazy writer datang setiap detik

Sisi penulisan adalah cache write-back. WriteFile mengembalikan sukses begitu data disalin ke slot, dan penerapan ke disk ditunda. Kebijakan «tulis belakangan» inilah penulisan tertunda (lazy writing).1

Yang menjalankan penerapan itu adalah lazy writer yang dijalankan Cache Manager setiap detik. Ia menumpuk seperdelapan halaman yang belum di-flush baru-baru ini ke antrean, lalu menulisnya; jika data yang harus ditulis banyak, antrean ditambah lagi. File sementara yang dibuat dengan atribut FILE_ATTRIBUTE_TEMPORARY dikeluarkan dari sasaran flush lazy writer — menulis sesuatu yang segera akan dihapus hanya membuang kerja.1 Namun ini petunjuk lewat atribut, bukan jaminan: jika memori tertekan, penulisan kembali tetap bisa terjadi, dan file yang «namanya saja terkesan sementara» tidak kena aturan ini.

Disklazy writer (jalan setiap detik)Cache sistemAplikasiDisklazy writer (jalan setiap detik)Cache sistemAplikasiDisalin ke slot,halaman ditandai dirty (belum tertulis)Dari sini sampai penulisan kembali adalah «jendela berbahaya»Pemutusan daya atau crash OS menghilangkan data iniBaru di sini data dibuat tahan lamaWriteFile(data)TRUE segera kembaliMemilih 1/8 halaman dirtyMenulis kembali secara berkelompok

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

4.2. Apa yang hilang, tergantung apa yang terjadi

Arti «jendela berbahaya» perlu dipastikan. Nasib data terbelah menurut jenis kegagalan.

Data segera setelah WriteFile sukses(halaman dirty di cache)Apa yang terjadiProses aplikasicrash / dihentikan paksaSeluruh OS berhenti(pemutusan daya, layar biru)Data tetap adaCache milik OS,jadi lazy writer menulisnya kembali sesuai jadwalHalaman dirty hilangHanya porsi yang sudah sampai ke disk yang tersisa

Gambar 4: Jenis kegagalan dan batas bertahan. Cache bukan «milik proses», melainkan «milik OS»

  • Jika aplikasi mati, data tidak hilang. Begitu salinan ke cache selesai, pemilik data adalah OS. «Aplikasi jatuh segera setelah menyimpan, tetapi file tetap utuh» terjadi berkat ini.
  • Jika seluruh OS mati, porsi dirty hilang. Frekuensi flush diatur sebagai trade-off kinerja dan keandalan, dan dokumentasi juga menyatakan dengan jelas bahwa «jika terjadi kehilangan daya mendadak, data yang di-cache akan hilang».1

Jadi pertanyaan dalam desain aplikasi bisnis adalah: «apakah data ini boleh hilang pada saat pemutusan daya?». Beberapa detik log mungkin masih bisa diterima. Rekaman pasti data pesanan, kemungkinan besar tidak. Alat di bab berikutnya hanya dipakai pada yang tidak boleh hilang.

5. Peralatan untuk membuat «benar-benar tertulis»

5.1. FlushFileBuffers — tulis habis sekarang

FlushFileBuffers menulis habis data yang sudah di-buffer untuk file yang ditentukan sampai ke perangkat. Metadata sistem file selalu di-cache, jadi untuk mengantarkan metadata pun diperlukan flush (atau WRITE_THROUGH) — itu juga poin yang perlu dipegang.12 Di .NET, padanannya adalah FileStream.Flush(true) (Flush() saja hanya menyerahkan buffer internal .NET ke OS; cache OS tetap utuh).8

Namun dokumentasi resmi memperingatkan dengan tegas — memanggilnya pada setiap penulisan tidak efisien. Jika banyak penulisan dan setiap kali harus dibuat tahan lama, yang seharusnya dipakai adalah NO_BUFFERING+WRITE_THROUGH yang dibahas kemudian.2

5.2. FILE_FLAG_WRITE_THROUGH — hanya menghilangkan penundaan

Jika file dibuka dengan FILE_FLAG_WRITE_THROUGH, penulisan tetap ditulis ke cache, sekaligus segera ditulis ke disk tanpa menunggu lazy writer.1 Poin pentingnya: pembacaan tetap mendapat manfaat cache. Ini jawaban yang lurus untuk «biarkan pembacaan tetap cepat, hilangkan hanya penundaan penulisan».

5.3. FILE_FLAG_NO_BUFFERING — tidak melewati cache

FILE_FLAG_NO_BUFFERING mengeluarkan cache sistem itu sendiri dari baca-tulis. Semua baca-tulis menjadi I/O ke perangkat disk setiap kali, tanpa melewati cache.1 Namun yang bisa dilewati hanya sampai cache sistem Windows; seperti Gambar 5, cache tulis di dalam perangkat adalah tangga terpisah. Jika ketahanan terhadap pemutusan daya juga dibutuhkan, kombinasi WRITE_THROUGH atau FlushFileBuffers tetap diperlukan. Ini alat untuk transfer massal data dalam jumlah besar, atau untuk mesin basis data yang mengelola buffer sendiri, tetapi ada syarat yang ketat.3

  • Ukuran dan offset file baca-tulis harus kelipatan ukuran sektor volume (sektor 512 byte berarti 512, 1024, 1536, …).
  • Alamat buffer juga harus diselaraskan ke ukuran sektor fisik (perlu juga mempertimbangkan disk «Advanced Format» dengan sektor fisik 4096 byte).
  • Meski begitu, metadata tetap di-cache, jadi persistensi penuh tetap membutuhkan kombinasi WRITE_THROUGH atau FlushFileBuffers.12

«Syarat» inilah yang pertama membuat orang tersandung jika hanya menambahkan flag. Jika baca-tulis dilakukan tanpa menaati penyelarasan, operasi gagal dengan ERROR_INVALID_PARAMETER (87). Tiga hal yang harus dipenuhi dirangkum di bawah.3

Yang diselaraskan Syarat Cara memenuhinya
Ukuran baca-tulis Kelipatan ukuran sektor volume Ambil lpBytesPerSector dari GetDiskFreeSpace, lalu bulatkan ke kelipatannya
Offset file Sama (termasuk jika ditetapkan lewat Offset pada OVERLAPPED) Maju per kelipatan ukuran sektor
Alamat buffer Diselaraskan ke ukuran sektor fisik Alokasikan dengan VirtualAlloc (mengembalikan area yang diselaraskan ke batas halaman = biasanya 4096 byte)

Yang ketiga paling sering terlewat. Alamat yang dikembalikan malloc, new, atau array C# tidak menjamin penyelarasan ke batas sektor. Jika memakai VirtualAlloc yang bisa mengalokasikan di batas halaman, persyaratan disk «Advanced Format» dengan sektor fisik 4096 byte ikut terpenuhi. Bentuk minimalnya 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();
}

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

// Buffer diambil di area yang diselaraskan ke batas halaman (tidak dijamin oleh malloc/new)
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 kegagalan CreateFileW (akses ditolak, path tidak ada, dll.)
    // tertimpa hasil pembersihan, dan yang kembali hanya kode yang tidak menjelaskan penyebabnya
    const DWORD err = GetLastError();
    VirtualFree(buffer, 0, MEM_RELEASE);
    return err;
}

// Karena maju per chunk byte setiap kali, ukuran dan offset tetap terselaraskan
DWORD read = 0;
while (ReadFile(h, buffer, chunk, &read, nullptr) && read > 0)
{
    // Proses read byte di awal buffer
    // (Di akhir file, read < chunk. Ini normal)
}

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

Perlu dicatat, FileOptions di .NET tidak punya nilai yang sesuai dengan FILE_FLAG_NO_BUFFERING. Jika benar-benar diperlukan, CreateFile harus dipanggil langsung, dan persyaratan penyelarasan di atas tetap harus dipenuhi sendiri. Pertimbangkan dengan urutan bukan «ingin cepat, jadi NO_BUFFERING», melainkan «mengelola buffer sendiri, jadi NO_BUFFERING».

5.4. Cara memilah alat

WriteFile default: sukses kembali sampai di sinilazy writer (setiap detik) / WRITE_THROUGH (segera)Waktu perangkat /FlushFileBuffers meminta 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 sampai mana masing-masing alat mendorongnya. Perhatikan juga tangga terakhir «cache di dalam perangkat disk»

Cara Apa yang terjadi Cocok untuk
Default (cache aktif) Selesai dengan salinan ke cache. Penerapan oleh lazy writer Hampir semua I/O file
FlushFileBuffers / Flush(true) Menulis habis data+metadata pada saat itu Penetapan pada titik tertentu (commit transaksi, dll.)
FILE_FLAG_WRITE_THROUGH Setiap penulisan segera ke disk (pembacaan tetap lewat cache) Log atau jurnal yang penulisannya tidak boleh hilang
FILE_FLAG_NO_BUFFERING (+WRITE_THROUGH) Tidak melewati cache. Ada persyaratan penyelarasan Pengelolaan buffer sendiri, I/O massal dalam jumlah besar

Urutan memilih adalah dua tahap: tentukan dulu «berapa banyak yang boleh hilang saat pemutusan daya», lalu pastikan «seberapa lambat yang masih bisa diterima untuk itu». Jangan hanya memandang tabel dari atas; telusuri percabangan ini.

Boleh(log beberapa detik terakhir, dll.)Tidak bolehTitik tertentu(penetapan transaksi, dll.)Setiap entriTidak (aplikasi biasa)Ya (mesin DB, dll.)Akan menulis data iniPada saat pemutusan daya atau layar biru,apakah boleh hilangBiarkan default (cache aktif)Paling cepat. Hampir semua I/O ada di siniYang tidak boleh hilang:«titik tertentu» atau «setiap entri»FlushFileBuffers pada titik tertentuDi .NET: Flush(true)Biaya: hanya menunggu pada titik tertentuApakah buffer dikelola sendiridan persyaratan penyelarasan 5.3 terpenuhiFILE_FLAG_WRITE_THROUGHSetiap penulisan segera ke diskPembacaan tetap cepat lewat cacheFILE_FLAG_NO_BUFFERING+ FILE_FLAG_WRITE_THROUGHBentuk «persistensi yang sering» yang disebut dokumentasi resmi

Gambar 6: Cara memilih alat. Percabangan pertama adalah tuntutan keandalan, yang kedua adalah biaya yang masih bisa dibayar. Tidak ada jalur «FlushFileBuffers pada setiap entri» karena, seperti terlihat di 5.1, dokumentasi resmi menilai itu tidak efisien

Tiga pola praktis juga cukup disebutkan.

  1. «Tulis ke file sementara → flush → ganti nama» adalah langkah baku agar file setengah rusak tidak tertinggal. Isi ditulis habis, baru dikunci dengan nama — serah terima atomik ini dibahas lebih rinci di «Dasar pengendalian eksklusif pada integrasi file».
  2. Menyerahkannya ke basis data juga desain yang sah. Pembahasan SQLite yang merakit daya tahan lewat WAL dan flush ada di «Memakai SQLite di aplikasi bisnis dengan C#». Pilihan «tidak menulis strategi flush sendiri» selalu tersedia.
  3. Curigai cache pada tolok ukur. Pengukuran «pembacaan terlalu cepat» hampir selalu mengukur cache hit pada putaran kedua dan seterusnya. Tata cara pengukuran dirangkum di «Cara membandingkan kecepatan program per versi dengan benar di Windows».

Jangan lupa tangga terakhir Gambar 5 — cache di dalam perangkat disk. FlushFileBuffers meminta penulisan habis sampai ke situ, tetapi pada USB flash dan disk eksternal kebijakan cache tulis di sisi perangkat («pelepasan cepat» dan «kinerja tinggi») ikut berperan. Penanganan perangkat lepasan juga dibahas di «Cara menangani perangkat USB di aplikasi Windows».

6. Koherensi dengan memory-mapped file

Di bagian 1, setelah mendengar «wujud cache adalah file mapping», pasti ada yang bertanya — kalau begitu, apakah tampilan yang dibuat sendiri dengan MapViewOfFile dan cache ReadFile/WriteFile saling bertabrakan?

Tidak. Karena keduanya duduk di 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 MapViewOfFileKumpulan halaman fisik yang sama(memori yang ditopang file)File di diskI/O FILE_FLAG_NO_BUFFERINGberada di luar kerangka berbagi ini (langsung ke disk)

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

Ada dua hal yang perlu diingat.

  • I/O FILE_FLAG_NO_BUFFERING berada di luar koherensi ini. Baca-tulis yang tidak melewati cache tidak dicocokkan dengan isi lewat tampilan yang dipetakan atau lewat cache. Jika dicampur, keselarasan harus dijaga sendiri.
  • Persistensi tampilan yang dipetakan dilakukan dua tahap. FlushViewOfFile memulai penulisan halaman dirty dalam rentang itu, tetapi tidak menulis metadata, dan tidak menunggu penulisan fisik dari cache perangkat disk. Untuk mengantarkan dengan andal, panggil FlushFileBuffers setelah FlushViewOfFile.5

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

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

Dua baris terlebih dahulu. Fast I/O adalah jalan pintas yang disediakan untuk baca-tulis sinkron ke file yang sudah ada di cache; ia bertukar data langsung dengan cache tanpa merakit IRP (I/O request packet; wadah permintaan yang diserahkan kernel ke driver). Kolom Operation di Procmon yang menampilkan IRP_MJ_READ dan FASTIO_READ bercampur adalah perbedaan apakah «pembacaan» yang sama lewat jalur biasa atau lewat jalan pintas.

Di pasal 5.2 bagian 1 tertulis «tidak semua I/O menjadi IRP». Ini pencocokan jawabannya.

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

BisaTidak bisaBaca-tulis sinkron ke handle dengan cache aktifBisakah diproses dengan Fast I/O(misalnya sudah ada di cache)Fast I/OMenyalin langsung dengan cache tanpa membuat IRPDi Procmon tampil sebagai FASTIO_Jalur biasaMerakit IRP lalu ke device stack(dunia Gambar 6 bagian 1)

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

Alasan baris FASTIO_ ikut tercampur dalam pengamatan Procmon di bab 7 bagian 1 kini terjelaskan. Pembacaan yang cache hit bahkan membuat IRP terasa barang mewah. Keberadaan jalur ini juga memengaruhi driver filter yang dibahas di bagian 6 (minifilter dirancang agar bisa menyela Fast I/O juga).

8. Ringkasan

  • Cache file Windows adalah write-back, dan wujudnya adalah pemetaan rentang file per 256KB. Baca-tulis dengan cache aktif menjadi salinan memori dengan slot.1
  • Read-ahead berspekulasi pada pembacaan, dan SequentialScan/RandomAccess adalah petunjuknya.1
  • Penulisan diikuti lazy writer setiap detik. Jika aplikasi mati, data tetap ada; jika seluruh OS mati, hanya porsi dirty yang hilang. Pertanyaan desainnya: «apakah data ini boleh hilang pada saat pemutusan daya?».1
  • Alat untuk menulis dengan andal: FlushFileBuffers (penetapan pada titik tertentu) / WRITE_THROUGH (setiap penulisan) / NO_BUFFERING (tidak melewati cache + persyaratan penyelarasan). Flush setiap kali tidak efisien; 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 yang cache hit bahkan melewatkan IRP lewat Fast I/O. Itulah wujud FASTIO_ yang terlihat di Procmon bagian 1.6

Lanjutannya adalah bagian 5, «Interior NTFS — memahami sistem file lewat MFT». Sampai di sini file diperlakukan sebagai «offset dan deretan byte», tetapi di baliknya NTFS menata data bagaimana — 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 pada 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 yang juga masuk 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 256KB yang dibaca ke slot 256KB 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 mendadak seperti kehilangan daya terjadi, data cache yang belum tertulis 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 segera ditulis ke disk tanpa penundaan lazy writer; dan bahwa metadata sistem file selalu di-cache, jadi persistensi metadata memerlukan 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 sudah di-buffer untuk file yang ditentukan sampai ke perangkat; bahwa memanggilnya pada setiap penulisan dari banyak penulisan tidak efisien, dan aplikasi yang memerlukan persistensi data penting pada penulisan yang sering seharusnya memakai I/O tanpa buffer dengan FILE_FLAG_NO_BUFFERING dan FILE_FLAG_WRITE_THROUGH; dan bahwa memanggilnya 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 jika ditetapkan dengan OVERLAPPED) harus kelipatan ukuran sektor volume; alamat buffer baca-tulis sebaiknya 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 jika 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 selesainya 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 yang 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 dengan Flush(true), di atas itu semua buffer file perantara (buffer OS) juga di-flush. ↩

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 tertulis di disk?
Secara default, belum. Cache file Windows memakai cara write-back: WriteFile mengembalikan sukses begitu data disalin ke cache file sistem. Penulisan ke disk dilakukan kemudian oleh lazy writer Cache Manager, yang dijalankan setiap detik. Yang penting adalah perbedaan jenis kegagalan. Jika proses aplikasi crash, data yang sudah masuk cache tidak hilang — selama OS masih hidup, data itu akan ditulis kemudian. Sebaliknya, jika seluruh OS jatuh (pemutusan daya, layar biru), halaman dirty yang belum tertulis hilang. Cara membaca yang akurat bukan «WriteFile sukses = sudah dibuat tahan lama», melainkan «sukses = sudah diserahkan ke OS».
Bagaimana memastikan data benar-benar tertulis ke disk?
Ada tiga alat. Pertama, FlushFileBuffers, yang menulis habis data dan metadata yang sudah di-buffer untuk file itu sampai ke perangkat (di .NET, padanannya adalah FileStream.Flush(true)). Kedua, FILE_FLAG_WRITE_THROUGH, yang pada setiap penulisan tetap menulis ke cache sekaligus segera menulis ke disk. Ketiga, FILE_FLAG_NO_BUFFERING, yang tidak melewati cache sama sekali. Dokumentasi Microsoft menyatakan bahwa memanggil FlushFileBuffers pada setiap penulisan tidak efisien; jika penulisan sering dan persistensi harus andal, yang seharusnya dipakai adalah kombinasi FILE_FLAG_NO_BUFFERING dan FILE_FLAG_WRITE_THROUGH. Ketiganya melepaskan sebagian manfaat cache sehingga menjadi lebih lambat, jadi kunci praktisnya bukan menempelkannya ke semua penulisan, melainkan memakainya hanya pada data yang 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 operasi selesai». Pembacaan tetap mendapat manfaat cache; yang dihilangkan hanya penundaan lazy writer. NO_BUFFERING berarti «baca-tulis tidak melewati cache sistem», sehingga setiap baca dan tulis menjadi I/O ke perangkat disk (yang dilewati hanya cache Windows; cache tulis di dalam perangkat itu sendiri tidak dilewati). Sebagai gantinya ada batasan ketat. Ukuran dan offset file setiap baca atau tulis harus kelipatan ukuran sektor volume, dan alamat buffer harus diselaraskan ke batas sektor fisik. Selain itu, bahkan dengan NO_BUFFERING, metadata sistem file terus di-cache, jadi untuk menulis metadata dengan andal tetap diperlukan FlushFileBuffers atau kombinasi WRITE_THROUGH. Yang tipikal memakainya adalah perangkat lunak yang mengelola buffer sendiri, seperti mesin basis data. Untuk aplikasi biasa, yang wajar ditimbang lebih dulu adalah WRITE_THROUGH atau FlushFileBuffers.
Task Manager menunjukkan memori bebas sedikit. Apakah itu karena cache file?
Dalam banyak kasus, ya, dan itu perilaku yang normal. Windows secara aktif memakai memori fisik yang kosong sebagai cache file, jadi menyalin file besar atau banyak baca-tulis akan membengkak cache dan membuat pemakaian memori tampak naik. Namun sebagian besar halaman yang dipakai cache adalah jenis yang relatif cepat dialihkan jika aplikasi meminta memori — itu perlu dibedakan dari keadaan «memori habis dimakan sampai tidak cukup». Jika mencurigai kekurangan memori, yang praktis dilihat bukan hanya kapasitas kosong yang tampak, melainkan juga memori yang dikomit dan frekuensi hard fault.
Jika file yang sama disentuh lewat memory-mapped file dan lewat ReadFile/WriteFile, apakah isinya bisa tidak selaras?
Tidak, terhadap I/O biasa dengan cache aktif. Cache Windows sendiri diimplementasikan sebagai file mapping, sehingga tampilan yang dipetakan dan cache untuk file lokal yang sama berbagi data yang sama — perubahan di satu sisi terlihat dari sisi lain. Beberapa proses yang membuat tampilan dari objek file mapping yang sama juga melihat data yang koheren (konsisten). Namun baca-tulis pada handle yang dibuka dengan FILE_FLAG_NO_BUFFERING tidak melewati cache, jadi berada di luar kerangka koherensi ini. Selain itu, untuk menulis perubahan pada tampilan yang dipetakan sampai ke disk dengan andal, FlushViewOfFile saja tidak cukup: fungsi itu tidak menulis metadata dan tidak menunggu cache perangkat keras, jadi FlushFileBuffers perlu dipanggil 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