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, danFILE_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
flowchart TB
subgraph U["Aplikasi (mode pengguna)"]
BUF["Buffer aplikasi<br/>(area yang diserahkan ke ReadFile/WriteFile)"]
end
subgraph S["Ruang alamat sistem"]
SLOT["Cache file sistem<br/>slot yang memetakan rentang 256KB file"]
end
DISK[("File di disk")]
BUF <-->|"ReadFile/WriteFile =<br/>salinan memori dengan slot"| SLOT
SLOT <-->|"Pembacaan saat akses pertama dan<br/>penulisan kembali kemudian, per halaman"| 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.
flowchart LR
A["Riwayat permintaan baca aplikasi<br/>dibaca berurutan dari awal"]
D{"Cache Manager<br/>mendeteksi pola"}
R["Read-ahead: rentang lanjutan<br/>dibaca sebelum diminta<br/>(jumlahnya berubah menurut pola dan ukuran permintaan)"]
H1["Petunjuk FILE_FLAG_SEQUENTIAL_SCAN<br/>= read-ahead lebih agresif"]
H2["Petunjuk FILE_FLAG_RANDOM_ACCESS<br/>= read-ahead akan sia-sia, jadi ditekan"]
A --> D
D --> R
H1 -.-> D
H2 -.-> D
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.
sequenceDiagram
participant App as Aplikasi
participant C as Cache sistem
participant LW as lazy writer (jalan setiap detik)
participant D as Disk
App->>C: WriteFile(data)
Note over C: Disalin ke slot,<br/>halaman ditandai dirty (belum tertulis)
C-->>App: TRUE segera kembali
Note over App,C: Dari sini sampai penulisan kembali adalah «jendela berbahaya»<br/>Pemutusan daya atau crash OS menghilangkan data ini
LW->>C: Memilih 1/8 halaman dirty
LW->>D: Menulis kembali secara berkelompok
Note over D: Baru di sini data dibuat tahan lama
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.
flowchart TB
W["Data segera setelah WriteFile sukses<br/>(halaman dirty di cache)"]
Q{"Apa yang terjadi"}
A1["Proses aplikasi<br/>crash / dihentikan paksa"]
A2["Seluruh OS berhenti<br/>(pemutusan daya, layar biru)"]
S["Data tetap ada<br/>Cache milik OS,<br/>jadi lazy writer menulisnya kembali sesuai jadwal"]
L["Halaman dirty hilang<br/>Hanya porsi yang sudah sampai ke disk yang tersisa"]
W --> Q
Q --> A1
Q --> A2
A1 --> S
A2 --> L
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:\\", §orsPerCluster, &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
flowchart TB
A["Buffer aplikasi"]
B["Cache file sistem<br/>(halaman dirty)"]
C["Cache di dalam perangkat disk"]
D[("Media rekam non-volatile")]
A -->|"WriteFile default: sukses kembali sampai di sini"| B
B -->|"lazy writer (setiap detik) / WRITE_THROUGH (segera)"| C
C -->|"Waktu perangkat /<br/>FlushFileBuffers meminta penulisan habis"| D
A -.->|"NO_BUFFERING melewati cache dan pergi langsung"| C
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.
flowchart TB
S["Akan menulis data ini"]
Q1{"Pada saat pemutusan daya atau layar biru,<br/>apakah boleh hilang"}
A0["Biarkan default (cache aktif)<br/>Paling cepat. Hampir semua I/O ada di sini"]
Q2{"Yang tidak boleh hilang:<br/>«titik tertentu» atau «setiap entri»"}
A1["FlushFileBuffers pada titik tertentu<br/>Di .NET: Flush(true)<br/>Biaya: hanya menunggu pada titik tertentu"]
Q3{"Apakah buffer dikelola sendiri<br/>dan persyaratan penyelarasan 5.3 terpenuhi"}
A2["FILE_FLAG_WRITE_THROUGH<br/>Setiap penulisan segera ke disk<br/>Pembacaan tetap cepat lewat cache"]
A3["FILE_FLAG_NO_BUFFERING<br/>+ FILE_FLAG_WRITE_THROUGH<br/>Bentuk «persistensi yang sering» yang disebut dokumentasi resmi"]
S --> Q1
Q1 -->|"Boleh<br/>(log beberapa detik terakhir, dll.)"| A0
Q1 -->|"Tidak boleh"| Q2
Q2 -->|"Titik tertentu<br/>(penetapan transaksi, dll.)"| A1
Q2 -->|"Setiap entri"| Q3
Q3 -->|"Tidak (aplikasi biasa)"| A2
Q3 -->|"Ya (mesin DB, dll.)"| A3
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.
- «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».
- 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.
- 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
flowchart TB
subgraph P1["Ruang alamat proses A"]
V1["Tampilan MapViewOfFile"]
end
subgraph SYS["Ruang alamat sistem"]
SC["Tampilan Cache Manager<br/>(slot yang dipakai ReadFile/WriteFile)"]
end
PAGES["Kumpulan halaman fisik yang sama<br/>(memori yang ditopang file)"]
DISK[("File di disk")]
V1 --> PAGES
SC --> PAGES
PAGES --> DISK
NB["I/O FILE_FLAG_NO_BUFFERING<br/>berada di luar kerangka berbagi ini (langsung ke disk)"]
NB -.-> 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_BUFFERINGberada 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.
FlushViewOfFilememulai penulisan halaman dirty dalam rentang itu, tetapi tidak menulis metadata, dan tidak menunggu penulisan fisik dari cache perangkat disk. Untuk mengantarkan dengan andal, panggilFlushFileBufferssetelahFlushViewOfFile.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.
flowchart TB
REQ["Baca-tulis sinkron ke handle dengan cache aktif"]
Q{"Bisakah diproses dengan Fast I/O<br/>(misalnya sudah ada di cache)"}
FAST["Fast I/O<br/>Menyalin langsung dengan cache tanpa membuat IRP<br/>Di Procmon tampil sebagai FASTIO_"]
IRP["Jalur biasa<br/>Merakit IRP lalu ke device stack<br/>(dunia Gambar 6 bagian 1)"]
REQ --> Q
Q -->|Bisa| FAST
Q -->|Tidak bisa| IRP
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/RandomAccessadalah 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
- Kedalaman I/O Windows (bagian 1) — semua baca-tulis menjadi IRP: gambaran keseluruhan sistem I/O
- Kedalaman I/O Windows (bagian 2) — I/O sinkron dan I/O asinkron: arti sebenarnya OVERLAPPED
- Kedalaman I/O Windows (bagian 3) — I/O completion port (IOCP) dan thread pool .NET: lapisan di bawah async/await
- Dasar pengendalian eksklusif pada integrasi file — praktik terbaik file lock dan claim atomik
- Jebakan shared memory dan praktik terbaik di lapangan
- Memakai SQLite di aplikasi bisnis dengan C# — mode WAL, pengendalian eksklusif, penanggulangan kerusakan, dan pemilahan dengan EF Core
- Cara membandingkan kecepatan program per versi dengan benar di Windows
- Cara menangani perangkat USB di aplikasi Windows — memilih COM virtual, HID, WinUSB, dan SDK khusus
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».
- Pengembangan aplikasi Windows
- Investigasi bug dan analisis penyebab
- Pemanfaatan aset yang ada dan dukungan migrasi
- Hubungi kami
Tautan referensi
-
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
-
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
-
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
-
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
-
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
-
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
-
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. ↩
-
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 terkait
Artikel terbaru dengan tag yang sama untuk mendalami topik-topik terdekat.
Kedalaman I/O Windows (bagian 5) — struktur internal NTFS: memahami sistem file lewat MFT
Bagian 5 seri yang menjelaskan struktur internal NTFS dengan diagram. Dari sudut pandang pengembang, artikel ini menata MFT dan rekaman f...
Kedalaman I/O Windows (bagian 6, terakhir) — driver filter dan minifilter: mengapa Procmon dan pemindaian virus dapat menyela I/O
Bagian terakhir seri yang menjelaskan driver filter dan minifilter Windows dengan diagram. Filter Manager dan altitude, callback pre/post...
Praktik terbaik multithreading di lapangan: edisi .NET — yang harus diputuskan sebelum menambah thread
Merangkum pola desain .NET/C# agar aplikasi multithread tidak sesekali crash atau macet: jangan membuat thread sendiri, bertumpu pada Tas...
Memakai WMI/CIM dari C# dan PowerShell — panduan praktis pengambilan informasi perangkat keras, pemantauan proses, dan kueri jarak jauh
Cara klasik mengambil nomor seri PC, memantau ruang disk, dan mendeteksi proses yang mulai adalah WMI/CIM. Artikel ini menjelaskan pemaka...
Named pipe dalam praktik — IPC andalan Windows, dari desain sampai keamanan
Penjelasan praktis named pipe, IPC andalan di Windows. Dari sumber primer: memilih mode byte versus mode pesan, desain server yang menang...
Topik terkait
Halaman-halaman ini menempatkan topik dalam konteks layanan dan keputusan yang lebih luas.
Topik teknis Windows
Portal tentang pengembangan Windows, investigasi bug, dan pemanfaatan aset yang ada.
Layanan yang terkait dengan topik ini
Artikel ini berkaitan langsung dengan layanan berikut.
Pengembangan aplikasi Windows
Aplikasi bisnis, integrasi perangkat, dan alat komunikasi, dari kebutuhan hingga pengembangan.
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.