Kedalaman memori Windows (bagian 3) — Objek section dan copy-on-write: apa sebenarnya DLL dan pemetaan berkas
· Go Komura · Windows, Manajemen memori, Memori bersama, Pemetaan berkas, Copy-on-Write, DLL, Cache Manager
Di artikel sebelumnya, “Kedalaman memori Windows (bagian 2) — Hidup sebuah halaman fisik”, kita mengikuti bagaimana halaman fisik yang meninggalkan Working Set bergerak lewat Modified, Standby, Free, dan Zeroed. Standby itu juga menampung halaman dari DLL, EXE, berkas terpetakan, dan cache berkas.
Muncul pertanyaan. Ketika 100 proses memakai kernel32.dll yang sama, apakah Windows menaruh 100 set halaman kode di RAM? Ketika dua proses memetakan berkas yang sama ke memori, dapatkah yang lain memakai halaman yang sudah dibaca salah satunya?
Jawabannya adalah memetakan beberapa alamat virtual ke halaman fisik yang sama. Pusat yang mewakili satuan berbagi itu adalah objek section, dan mekanisme yang memecah berbagi hanya saat menulis adalah copy-on-write (CoW).
Artikel ini mengikuti bagaimana EXE dan DLL, berkas data, memori bersama yang ditopang page file, dan cache berkas menempel pada aliran berkas yang sama, serta di mana jalur pemrosesan berpisah. Cara membaca angkanya sendiri mengacu pada artikel pengantar “What Does Windows’ "Memory Usage" Actually Mean?”.
“Kedalaman memori Windows” — ketiga bagian
- Bagian 1: Alamat virtual dan page fault
Kita mengikuti saat halaman virtual yang sudah di-Commit memperoleh RAM fisik. - Bagian 2: Hidup sebuah halaman fisik
Kita mengikuti transisi keadaan halaman yang meninggalkan Working Set. - Bagian 3 (artikel ini): Objek section dan copy-on-write
Kita mengikuti mekanisme DLL, pemetaan berkas, dan memori bersama memakai halaman fisik bersama.
Pertanyaan yang dijawab bagian 3 hanya satu.
Mengapa beberapa proses dapat memakai DLL atau berkas yang sama sebagai satu set halaman fisik?
Pembaca sasaran adalah pengembang dan operator yang ingin memahami berbagi DLL, CreateFileMapping, MapViewOfFile, memori bersama, CoW, dan hubungan dengan cache berkas bukan hanya sebagai pemakaian API melainkan dari struktur internal. Prasyarat adalah Windows 10/11 atau Windows Server terkini, dan latar yang dibutuhkan adalah dasar alamat virtual, page fault, dan Working Set. Tingkat kesulitannya menengah; kita juga memakai istilah internal seperti Control Area dan Prototype PTE, tetapi pengamatan dapat diulang dengan VMMap, Process Explorer, dan QueryWorkingSetEx.
1. Intinya dulu
Untuk memulai, ini gambaran keseluruhannya.
- Objek section mewakili rentang memori yang dapat dibagi.
Setiap proses memetakan sebagian section itu ke ruang virtualnya sendiri sebagai “tampilan”.1 - Tampilan section yang sama tidak harus di alamat virtual yang sama.
0x000001...proses A dan0x000002...proses B dapat menunjuk offset section yang sama dan halaman fisik yang sama.2 - Ada section yang ditopang berkas dan yang ditopang page file.
Yang pertama memakai berkas nyata; yang kedua dipakai untuk memori bersama tanpa berkas eksplisit, dan sejenisnya.2 - Memuat EXE/DLL adalah section image; pemetaan berkas biasa adalah section data.
DenganSEC_IMAGE, atribut section di dalam PE menentukan perlindungan halaman.3 - Saat membaca, halaman fisik yang sama dapat dibagi.
Ketika salah satu sisi menulis ke halaman CoW, hanya halaman itu yang disalin dan PTE proses penulis diganti.4 - Meski pada aliran berkas yang sama, jalur cache, data, dan image berpisah.
I/O cache Cache Manager memakaiSharedCacheMap, pemetaan data memakaiDataSectionObject, dan EXE/DLL memakaiImageSectionObject. Ketiganya menempel pada aliran berkas yang sama lewatSECTION_OBJECT_POINTERS, tetapi fault image tidak melewati Cache Manager.5 - Private Bytes saja tidak dapat memastikan CoW terjadi.
FILE_MAP_COPYmembebankan Commit untuk seluruh tampilan di muka, terhadap kemungkinan setiap halaman nanti diprivatisasi. Untuk pemeriksaan per halaman, pakai bit Shared dariQueryWorkingSetEx.67
Dalam satu kalimat: yang dibagi bukan alamat virtual, melainkan isi di dalam section dan halaman fisik yang sesuai pada saat itu.
2. Objek section dan tampilan
Dalam definisi Microsoft, objek section mewakili wilayah memori yang dapat dibagi dan juga merupakan mekanisme yang memetakan berkas ke ruang alamat proses.1
Kuncinya adalah memisahkan section itu sendiri dari tampilan yang dilihat setiap proses.
| Konsep | Peran |
|---|---|
| Objek section | Mewakili isi yang dibagi, ukuran, backing store, dan batas atas perlindungan |
| Tampilan | Menunjukkan sebagian section sebagai rentang alamat virtual di suatu proses |
| PTE | Mengikat setiap halaman virtual dalam tampilan ke halaman fisik saat ini atau keadaan yang belum diwujudkan |
| PFN | Mewakili halaman fisik yang benar-benar ada di RAM |
Di Win32, CreateFileMapping mengembalikan handle ke objek pemetaan berkas, dan MapViewOfFile membuat tampilan di ruang virtual proses.3
HANDLE mapping = CreateFileMappingW(
file,
nullptr,
PAGE_READONLY,
0,
0,
nullptr);
void* view = MapViewOfFile(
mapping,
FILE_MAP_READ,
0,
0,
0);
Memanggil CreateFileMapping saja belum memberi alamat yang dapat dibaca proses. Lebih lanjut, membuat tampilan tidak langsung menaruh setiap halaman ke RAM. Dari halaman pertama yang disentuh, page fault membaca isi berkas dan mengikat halaman fisik ke PTE.8 Dengan kata lain, demand paging yang kita lihat di bagian 1 berlaku juga untuk tampilan section.
2.1. Section yang sama, alamat virtual berbeda
Bahkan ketika proses A dan proses B memetakan offset yang sama dari section yang sama, alamat awal tampilan dapat berbeda.
flowchart LR
accTitle: Memetakan alamat virtual berbeda ke halaman fisik yang sama
accDescr: Proses A dan proses B masing-masing punya tampilan di alamat virtual berbeda, tetapi keduanya mencapai halaman fisik PFN X yang sama lewat offset section yang sama
viewA["Proses A: 0x000001A00000 + 0x3000"] --> offset["Offset section 0x3000"]
viewB["Proses B: 0x000002700000 + 0x3000"] --> offset
offset --> pfnX["Halaman fisik yang sama PFN X"]
Gambar 1: Yang dibagi adalah isi di dalam section dan halaman fisik, bukan alamat virtual.
Itulah sebabnya Anda tidak boleh menyimpan pointer mentah di memori bersama. Nilai pointer proses A bisa menjadi alamat yang tidak berkaitan di proses B.
Dalam struktur bersama, pakai offset dari awal tampilan, integer lebar tetap, serta versi dan perataan yang eksplisit. Dokumentasi MapViewOfFileEx Microsoft juga menganjurkan menyimpan offset dari basis, bukan pointer, karena tidak ada jaminan alamat yang sama akan tersedia di kemudian hari.6
3. Ditopang berkas dan ditopang page file
Section terbagi menjadi dua jenis besar menurut dari mana isinya dapat dipulihkan.
flowchart TB
accTitle: Bagaimana file-backed dan pagefile-backed berpisah
accDescr: Memberi berkas nyata ke CreateFileMapping menghasilkan section yang ditopang berkas, dan halaman bersih dapat dibaca ulang dari berkas asli. Memberi INVALID_HANDLE_VALUE menghasilkan section yang ditopang page file; page file menopang isi, yang hilang saat objek dihancurkan
create["CreateFileMapping"] -->|Berikan handle berkas nyata| fileBacked["Section ditopang berkas"]
create -->|Berikan INVALID_HANDLE_VALUE| pfBacked["Section ditopang page file"]
fileBacked --> restore1["Halaman bersih dapat dibaca ulang dari berkas asli"]
pfBacked --> restore2["Page file menopang isi, yang hilang saat dihancurkan"]
Gambar 2: Perbedaan backing store menentukan dari mana isi dapat dipulihkan dan berapa lama ia hidup.
3.1. Section yang ditopang berkas
Memberi berkas nyata ke CreateFileMapping menghasilkan section yang ditopang berkas.
- Tampilan hanya-baca membaca halaman yang dibutuhkan dari berkas.
- Perubahan pada tampilan baca/tulis diperlakukan sebagai data berkas itu.
- Perubahan pada tampilan CoW tidak ditulis ke berkas asli; mereka menjadi halaman privat.
Jika halaman yang ditopang berkas bersih, halaman fisik dapat dibuang dan dibaca ulang dari berkas asli. Sifat itu yang menopang efisiensi Standby dan cache berkas yang kita lihat di bagian 2.
3.2. Section yang ditopang page file
Memberi INVALID_HANDLE_VALUE sebagai hFile CreateFileMapping dan menentukan ukuran menghasilkan section yang ditopang page file.
HANDLE mapping = CreateFileMappingW(
INVALID_HANDLE_VALUE,
nullptr,
PAGE_READWRITE,
0,
64 * 1024,
L"Local\\KomuraMemoryDemo");
Ini section tanpa berkas data eksplisit, ditopang page file. Isi awal adalah nol, dan beberapa proses dapat membuka objek yang sama lewat nama, pewarisan handle, DuplicateHandle, dan sejenisnya.93 Perubahan terlihat oleh proses yang memetakan halaman bersama yang sama. Di sisi lain, ketika objek section dihancurkan isinya tidak tersisa, jadi tidak cocok untuk meninggalkan berkas yang persisten.2
Catatan: memori bersama tidak otomatis membawa saling-kecualian. Mutex, semaphore, event, protokol lock-free, atau sejenisnya Anda rancang terpisah.9
4. Pemetaan image dan pemetaan data
Ketika kita berkata “EXE atau DLL juga pemetaan berkas”, perbedaan dari berkas data biasa tetap perlu diingat.
| Butir | Pemetaan image | Pemetaan data |
|---|---|---|
| Penggunaan utama | Memuat EXE atau DLL | Berkas biasa, data bersama |
| Atribut pembuatan | SEC_IMAGE |
PAGE_READONLY, PAGE_READWRITE, dan sejenisnya |
| Perlindungan halaman | Atribut di dalam image PE yang memutuskan | Spesifikasi pemetaan dan tampilan yang memutuskan |
| Penulisan | Dapat diprivatisasi lewat section yang dapat ditulis atau CoW | Penulisan bersama atau CoW dapat dipilih |
Type VirtualQuery |
MEM_IMAGE |
MEM_MAPPED |
Dengan SEC_IMAGE, atribut section milik image yang dieksekusi sendirilah yang lebih menentukan perlindungan halaman tampilan daripada nilai perlindungan biasa yang diberi ke CreateFileMapping.3
Lewat mekanisme ini, halaman yang belum diubah seperti kode dapat berbagi halaman fisik yang sama di banyak proses, dan hanya halaman yang membutuhkan perubahan milik proses yang bercabang lewat CoW. Meski begitu, tidak setiap halaman DLL pasti dibagi — karena relokasi ASLR, perbaikan loader, hotpatching, atribut section PE yang sebenarnya, dan sebagainya.
Rancangan yang penting adalah bagi dulu halaman yang dapat dibagi, dan salin secara malas hanya halaman yang perlu diubah.
5. Copy-on-write dari awal sampai akhir
Mari kita ikuti dari keadaan dua proses sedang membaca halaman CoW yang sama hingga proses A menulis satu byte.
5.1. Sebelum penulisan
Sebelum penulisan terjadi, PTE kedua proses secara konseptual mencapai halaman bersama yang sama, dan pembacaan berhasil apa adanya.
flowchart LR
accTitle: Keadaan bersama sebelum copy-on-write
accDescr: Sebelum penulisan terjadi, PTE proses A dan PTE proses B keduanya mencapai halaman bersama PFN X yang sama, dan pembacaan berhasil apa adanya
pteA["PTE proses A"] --> pfnX["PFN X bersama(baca / copy-on-write)"]
pteB["PTE proses B"] --> pfnX
Gambar 3: Sebelum penulisan, PTE kedua proses menunjuk ke halaman fisik yang sama.
5.2. Protection fault saat menulis
Halaman CoW sejak awal bukan halaman bersama yang dapat ditulis biasa. Ketika proses A mencoba menulis, CPU menaikkan protection fault. Memory manager yang menerima kendali menilai ini bukan penulisan ilegal melainkan penulisan ke atribut CoW.
5.3. Membuat halaman fisik baru
Atas penilaian itu, Windows melakukan hal berikut.
- Memperoleh satu halaman fisik untuk proses A.
- Menyalin isi PFN X ke halaman baru PFN Y.
- Mengganti PTE proses A ke PFN Y.
- Mengubah perlindungan proses A menjadi baca/tulis biasa.
- Mengeksekusi ulang instruksi tulis yang gagal.
flowchart LR
accTitle: Keadaan terpisah setelah copy-on-write
accDescr: Setelah penulisan proses A, hanya PTE proses A yang diganti ke halaman privat PFN Y yang menerima salinan isi, sementara PTE proses B terus menunjuk ke halaman bersama asli PFN X
pteA2["PTE proses A"] --> pfnY["PFN Y privat(R/W, setelah tulis)"]
pteB2["PTE proses B"] --> pfnX2["PFN X bersama(asli)"]
pfnX2 -.->|Disalin saat menulis| pfnY
Gambar 4: Hanya PTE proses yang menulis yang diganti ke halaman privat baru; sisi lain terus membaca isi asli.
Proses B terus membaca isi asli dan tidak melihat perubahan proses A. Itulah Copy-on-Write. Berbagi DLL dan FILE_MAP_COPY memakai prinsip yang sama: jangan salin sampai Anda menulis.46
5.4. Perbedaan dari FILE_MAP_WRITE
Halaman yang ditulis lewat penulisan bersama FILE_MAP_WRITE dirancang agar perubahan satu sisi juga terlihat dari tampilan lain yang memakai pemetaan berkas yang sama. Dengan FILE_MAP_COPY, sebaliknya, hanya halaman yang ditulis yang menjadi milik proses; perubahan tidak ditulis kembali ke berkas asli dan hilang saat tampilan dilepas.6
Apakah Anda ingin “menyampaikan pembaruan lewat memori bersama”, atau “setiap proses membuat perubahan privat dari data awal bersama”? Pilihan yang tepat berlawanan tergantung tujuannya.
6. Setelah CoW tetap MEM_MAPPED / MEM_IMAGE
Halaman setelah CoW secara fisik sudah Private. Anda mungkin lalu mengira Type VirtualQuery juga berubah menjadi MEM_PRIVATE, tetapi sebenarnya tampilan data tetap MEM_MAPPED dan image yang dapat dieksekusi tetap MEM_IMAGE. VirtualQuery melaporkan dari alokasi awal mana wilayah itu berasal.7
Untuk melihat per halaman apakah CoW sudah terjadi, pakai prosedur berikut.
- Akses halaman sasaran dan jadikan residens.
- Dapatkan informasi Working Set halaman dengan
QueryWorkingSetEx. - Lihat bit
Shared. - Jika
Shared == 0, halaman residens itu Private.
Saat mengonfirmasi di VMMap juga, lihat bukan hanya Type wilayah melainkan rincian Private/Shareable Working Set.
6.1. Private Bytes mungkin tidak naik
Dengan FILE_MAP_COPY, proses mungkin nanti menulis ke setiap halaman dalam tampilan. Jadi Windows mengambil beban Commit setara seluruh tampilan saat pemetaan.6 Akibatnya, menulis halaman pertama tidak selalu menaikkan Private Bytes sebesar 4KiB pada saat itu.
Metrik yang lebih baik saat mengamati CoW adalah berikut.
- Bit Shared dari
QueryWorkingSetEx - Private WS / Shareable WS di VMMap
- Informasi halaman fisik RAMMap
- Private Bytes sebagai informasi pelengkap
Jika Anda memperlakukan “apakah Private Bytes naik pada saat menulis” sebagai tes lulus/gagal, Anda akan melewatkan CoW yang bekerja dengan benar.
7. Titik temu dengan Cache Manager — memisahkan tiga jalur
Mengatakan “pemuatan EXE/DLL, cache berkas, dan memori bersama semuanya section” memberi gambaran keseluruhan, tetapi Anda tidak boleh meratakan implementasi menjadi satu objek.
Aliran berkas memiliki SECTION_OBJECT_POINTERS, yang dipakai memory manager dan Cache Manager.
typedef struct _SECTION_OBJECT_POINTERS {
PVOID DataSectionObject;
PVOID SharedCacheMap;
PVOID ImageSectionObject;
} SECTION_OBJECT_POINTERS;
DataSectionObject: keadaan section untuk berkas dataSharedCacheMap: tampilan cache yang dilacak Cache ManagerImageSectionObject: keadaan section untuk image yang dapat dieksekusi
Dokumentasi Microsoft menjelaskan bahwa struktur ini mengikat objek berkas ke section aliran berkas dan melacak isi di memori serta informasi cache.5
Di sini seri I/O dan seri memori bertemu. Namun pahami ketiga jalur secara terpisah.
ReadFile/WriteFileyang di-cache memakaiSharedCacheMapdan tampilan cache Cache Manager.- Fault pemetaan pada berkas data ditangani memory manager di sisi
DataSectionObject. Ia bekerja sama dengan I/O cache pada aliran berkas yang sama untuk menjaga isi tetap konsisten. - Fault image pada EXE/DLL ditangani memory manager dengan
ImageSectionObjectdan I/O paging. Itu bukan jalur yang melewatiSharedCacheMapCache Manager.
flowchart TB
accTitle: Tiga jalur yang menempel pada aliran berkas yang sama
accDescr: ReadFile/WriteFile yang di-cache memakai SharedCacheMap, fault pemetaan data memakai DataSectionObject, dan fault image EXE/DLL memakai ImageSectionObject; ketiganya menempel pada aliran berkas yang sama lewat SECTION_OBJECT_POINTERS
cached["ReadFile / WriteFile yang di-cache"] --> scm["SharedCacheMap"]
dataFault["Fault pemetaan data"] --> dso["DataSectionObject"]
imageFault["Fault image EXE/DLL"] --> iso["ImageSectionObject"]
scm --> stream["Aliran berkas yang sama(SECTION_OBJECT_POINTERS)"]
dso --> stream
iso --> stream
Gambar 5: Ketiga jalur diproses terpisah, tetapi menempel pada aliran berkas yang sama.
Kesamaan ketiganya bukan bahwa “semuanya masuk Cache Manager”, melainkan bahwa aliran berkas yang sama mengikat keadaan terpisah — cache, section data, dan section image — lewat SECTION_OBJECT_POINTERS.5 Pembacaan dan penulisan cache, Lazy Writer, dan hubungan Cc dengan Mm dibahas di “The Depths of Windows I/O (Part 4) — Cache Manager: When Does Your WriteFile Actually Reach the Disk?”.
Perhatikan bahwa ketika Anda mencampur tampilan yang dipetakan ke memori dengan ReadFile/WriteFile, Anda tidak dijamin selalu melihat isi pada saat yang sama. Rancangan perlu mencakup sinkronisasi, flush, dan mode berbagi berkas.36
8. Masa hidup objek dan tampilan
Menutup handle CreateFileMapping saja tidak menghancurkan tampilan yang sudah ada. Tampilan menahan referensi internal ke section, dan baru setelah setiap tampilan di-UnmapViewOfFile dan setiap handle di-CloseHandle objek dapat dihancurkan.3
UnmapViewOfFile(view);
CloseHandle(mapping);
CloseHandle(file);
Pemisahan masa hidup ini adalah penyebab fenomena “saya sudah menutup berkas, tetapi masih dipakai”. Bahkan setelah Anda menutup handle berkas, jika section image atau tampilan data masih merujuk aliran berkas, penutupan akhir berkas datang belakangan.
Hubungan dengan cleanup/close di sisi I/O dibahas di “The Depths of Windows I/O (Part 1)”, dan jebakan implementasi di “Shared Memory Pitfalls and Practical Best Practices”.
9. Lihat sendiri
9.1. Melihat DLL yang sama dari dua proses
Pertama, konfirmasikan berbagi DLL dengan proses yang sudah ada.
- Jalankan Process Explorer sebagai administrator.
- Jalankan dua proses
cmd.exe. - Pilih View > Lower Pane View > DLLs.
- Konfirmasikan jalur dan pemetaan DLL yang sama di kedua proses.
- Buka setiap
cmd.exedi VMMap dan bandingkan Images Working Set, Private, dan Shareable.
Melihat DLL yang sama di Process Explorer adalah bukti keduanya memetakan image yang sama. Namun itu saja tidak membuktikan PFN setiap halaman cocok. Gabungkan rincian Shareable VMMap, RAMMap, dan QueryWorkingSetEx untuk mengonfirmasi berbagi per halaman. Process Explorer dan VMMap disediakan Sysinternals.1011
9.2. Mengamati FILE_MAP_COPY dari dua proses
Program berikut memetakan berkas yang sama sebagai tampilan CoW dan menampilkan bit Shared dari QueryWorkingSetEx. Objek pemetaan berkas dibuat dengan PAGE_READONLY, tetapi perlindungan itu kompatibel dengan tampilan FILE_MAP_COPY, dan penulisan pertama di sisi tampilan menyebabkan CoW.3
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>
#include <cstdio>
#include <cwchar>
#pragma comment(lib, "Psapi.lib")
void PrintPage(const char* stage, void* address)
{
MEMORY_BASIC_INFORMATION mbi{};
if (!VirtualQuery(address, &mbi, sizeof(mbi))) {
std::printf("VirtualQuery failed: %lu\n", GetLastError());
return;
}
for (int attempt = 0; attempt < 3; ++attempt) {
// The page may have been trimmed while the user was waiting.
// Touch it immediately before querying the working-set attributes.
volatile unsigned char resident =
*static_cast<volatile unsigned char*>(address);
(void)resident;
PSAPI_WORKING_SET_EX_INFORMATION ws{};
ws.VirtualAddress = address;
if (!QueryWorkingSetEx(GetCurrentProcess(), &ws, sizeof(ws))) {
std::printf("QueryWorkingSetEx failed: %lu\n", GetLastError());
return;
}
if (!ws.VirtualAttributes.Valid) {
Sleep(0);
continue;
}
std::printf(
"%s: Type=0x%lx Valid=1 Shared=%llu ShareCount=%llu\n",
stage,
static_cast<unsigned long>(mbi.Type),
static_cast<unsigned long long>(ws.VirtualAttributes.Shared),
static_cast<unsigned long long>(ws.VirtualAttributes.ShareCount));
return;
}
std::printf(
"%s: page is not resident; Shared/ShareCount were not interpreted\n",
stage);
}
int wmain(int argc, wchar_t** argv)
{
if (argc != 3) {
std::fwprintf(stderr, L"usage: cow_demo <file> <read|write>\n");
return 2;
}
HANDLE file = CreateFileW(
argv[1], GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (file == INVALID_HANDLE_VALUE) return 3;
HANDLE mapping = CreateFileMappingW(
file, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (!mapping) {
CloseHandle(file);
return 4;
}
auto* view = static_cast<unsigned char*>(
MapViewOfFile(mapping, FILE_MAP_COPY, 0, 0, 0));
if (!view) {
CloseHandle(mapping);
CloseHandle(file);
return 5;
}
volatile unsigned char value = view[0];
(void)value;
std::puts("Start the other process. When both are waiting, press Enter...");
(void)std::getchar();
PrintPage("before", view);
if (std::wcscmp(argv[2], L"write") == 0) {
std::puts("Press Enter to trigger copy-on-write...");
(void)std::getchar();
view[0] ^= 0x5a;
PrintPage("after write", view);
} else {
std::puts("After the writer changes its page, press Enter...");
(void)std::getchar();
PrintPage("reader after peer write", view);
}
std::puts("Press Enter to exit...");
(void)std::getchar();
UnmapViewOfFile(view);
CloseHandle(mapping);
CloseHandle(file);
}
Bangun dan siapkan.
cl /std:c++20 /EHsc /W4 cow_demo.cpp
$path = "$env:TEMP\\cow-demo.bin"
[IO.File]::WriteAllBytes($path, [byte[]]::new(65536))
Lalu buka berkas yang sama dari dua konsol.
.\\cow_demo.exe "$env:TEMP\\cow-demo.bin" read
.\\cow_demo.exe "$env:TEMP\\cow-demo.bin" write
Setelah keduanya dimulai, tekan Enter dulu di sisi baca lalu di sisi tulis, dan pastikan Shared terpasang di before pada keduanya. Tekan Enter sekali lagi di sisi tulis dan halaman proses itu menjadi Shared 0. Lalu tekan Enter di sisi baca dan Anda dapat memastikan pembaca terus membaca halaman asli. Type VirtualQuery tetap MEM_MAPPED setelah penulisan juga.
Perhatikan bahwa PrintPage menyentuh ulang halaman sasaran tepat sebelum kueri, dan jika Valid == 0 ia tidak menafsirkan Shared dan ShareCount serta mencoba ulang hingga tiga kali. Jika halaman masih tidak residens, ia tidak menghasilkan hasil dan melaporkan itu. ShareCount dapat berubah dengan waktu dan tekanan memori, jadi lihat perubahan bit Shared yang dikonfirmasi pada Valid == 1, bukan nilai tetap.
10. Lima salah baca yang dihindari dalam praktik
10.1. “Memori bersama berakhir di alamat virtual yang sama”
Yang dibagi adalah section dan halaman fisik. Alamat virtual tampilan dapat berbeda per proses, jadi simpan offset, bukan pointer mentah.
10.2. “Jika DLL-nya sama, setiap halaman pasti dibagi”
Halaman kode yang bersih mudah dibagi, sementara relokasi, section yang dapat ditulis, CoW, dan residensi pada saat pengukuran juga menghasilkan halaman Private.
10.3. “Setelah CoW menjadi MEM_PRIVATE”
Type VirtualQuery tetap MEM_MAPPED atau MEM_IMAGE. Konfirmasikan keadaan berbagi sebenarnya dengan QueryWorkingSetEx.7
10.4. “Jika Private Bytes tidak naik, CoW tidak terjadi”
FILE_MAP_COPY membebankan Commit seluruh tampilan di muka. Utamakan Private WS dan bit Shared.6
10.5. “Jika halaman dibagi, sinkronisasi tidak perlu”
Melihat halaman fisik yang sama dan dapat memperbaruinya dengan aman dari beberapa inti CPU adalah masalah berbeda. Rancang atomisitas, urutan memori, saling-kecualian, keadaan antara saat crash, dan kompatibilitas versi.
Gagasan mengikuti referensi dan masa hidup secara terpisah juga berlaku pada masalah proses yang tertinggal setelah interop COM Excel. Lihat juga “Why EXCEL.EXE Processes Remain After C# Excel COM Automation — Reference Release Patterns and the Replacement Decision”.
11. Ringkasan
- Objek section mewakili rentang memori yang dapat dibagi, dan setiap proses memetakannya ke ruang virtualnya sendiri sebagai tampilan.1
- Offset yang sama dari section yang sama dipetakan dari alamat virtual berbeda ke halaman fisik yang sama.2
- Section yang ditopang berkas menopang berkas nyata; section yang ditopang page file menopang memori bersama bernama dan sejenisnya.9
- EXE/DLL diperlakukan sebagai section image dan berkas biasa sebagai section data; perlindungan dan tujuan penulisan kembali berbeda.3
- CoW membagi halaman fisik saat membaca dan, pada penulisan pertama, menyalin hanya halaman itu lalu mengganti PTE.4
- Setelah CoW,
VirtualQuerymasih mengembalikanMEM_MAPPED/MEM_IMAGE, jadi Anda mengonfirmasi dengan bit Shared dariQueryWorkingSetEx.7 - Dengan
FILE_MAP_COPY, Commit seluruh tampilan dibebankan lebih dulu, jadi Anda tidak dapat menilai CoW dari Private Bytes saja.6 - I/O cache Cache Manager, pemetaan data, dan pemetaan image menempel pada aliran berkas yang sama sebagai jalur terpisah yang memakai
SharedCacheMap,DataSectionObject, danImageSectionObject.5 - Memori bersama menjadi aman hanya jika Anda menyertakan masa hidup tampilan dan handle, sinkronisasi, ACL, dan rancangan offset.
Dengan ini ketiga bagian “Kedalaman memori Windows” selesai. Anda Reserve/Commit alamat virtual, memperoleh halaman fisik lewat page fault, memindahkan halaman dari Working Set ke daftar halaman, membaginya lewat section, dan memecah hanya halaman yang Anda tulis lewat CoW — manajemen memori Windows terhubung sebagai satu alur ini.
Artikel terkait
- Kedalaman memori Windows (bagian 1) — Saat alamat virtual menjadi RAM fisik: page fault dari awal sampai akhir
- Kedalaman memori Windows (bagian 2) — Hidup sebuah halaman fisik: lima daftar dan kebenaran tentang page file
- The Depths of Windows I/O (Part 4) — Cache Manager: When Does Your WriteFile Actually Reach the Disk?
- Shared Memory Pitfalls and Practical Best Practices
- Why EXCEL.EXE Processes Remain After C# Excel COM Automation — Reference Release Patterns and the Replacement Decision
- Process Explorer / Handle / VMMap in Practice — Chasing Hangs, Leaks, and “File in Use” from the State Right Now
Area konsultasi terkait
KomuraSoft LLC menangani investigasi cacat memori bersama, pemetaan berkas, pemuatan DLL, penguncian berkas, komunikasi antarproses, dan penggunaan memori aplikasi Windows.
Tautan referensi
-
Microsoft Learn, Section Objects and Views. Tentang objek section yang mewakili rentang memori yang dapat dibagi, dan setiap proses yang memetakan sebagian section sebagai tampilan. ↩ ↩2 ↩3
-
Microsoft Learn, File-Backed and Page-File-Backed Sections. Tentang section yang ditopang berkas dan page file, CoW, dan kemampuan berbagi memori fisik yang sama dari alamat virtual proses berbeda. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, CreateFileMappingW function. Tentang objek pemetaan berkas, section yang ditopang page file,
SEC_IMAGE, masa hidup tampilan dan handle, serta konsistensi di antara tampilan yang menopang berkas yang sama. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 -
Microsoft Learn, Memory Protection. Tentang beberapa proses yang berbagi halaman fisik DLL yang sama, dan CoW yang menyalin ke halaman fisik baru lalu memperbarui PTE ketika salah satu sisi menulis. ↩ ↩2 ↩3
-
Microsoft Learn, SECTION_OBJECT_POINTERS structure. Tentang DataSectionObject, SharedCacheMap, dan ImageSectionObject yang mengikat pemetaan aliran berkas dan informasi cache ke memory manager / Cache Manager. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, MapViewOfFileEx function. Tentang CoW dengan
FILE_MAP_COPY; halaman privat yang ditopang page file; pembebanan Commit untuk seluruh tampilan; dan menyimpan offset alih-alih alamat virtual. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 -
Microsoft Learn, VirtualQuery function. Tentang Type yang tetap
MEM_MAPPED/MEM_IMAGEsetelah CoW, dan kemampuan mengonfirmasi privatisasi dengan bit Shared dariQueryWorkingSetEx. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, Managing Memory Sections. Tentang memori fisik yang tidak ditetapkan sampai tampilan diakses, dan page fault akses pertama yang membaca isi berkas. ↩
-
Microsoft Learn, Sharing Files and Memory. Tentang berbagi objek pemetaan berkas yang sama lewat nama atau handle; membuat memori bersama yang ditopang page file dengan
INVALID_HANDLE_VALUE; dan sinkronisasi yang diperlukan terpisah. ↩ ↩2 ↩3 -
Microsoft Learn, Process Explorer - Sysinternals. Tentang Process Explorer yang dapat menampilkan handle proses serta DLL / berkas terpetakan ke memori yang dimuat. ↩
-
Microsoft Learn, VMMap - Sysinternals. Tentang VMMap yang merinci memori virtual proses menjadi Image, Mapped File, Private, dan sejenisnya, serta menampilkan rincian Private/Shareable Working Set. ↩
Artikel terkait
Artikel terbaru dengan tag yang sama untuk mendalami topik-topik terdekat.
DllMain dan loader lock — alasan sebenarnya Anda diminta "jangan lakukan apa pun di inisialisasi DLL"
Mengapa Anda tidak boleh memanggil LoadLibrary atau menyinkronkan dengan thread lain dari DllMain. Berdasarkan sumber primer, artikel ini...
Kedalaman memori Windows (bagian 2) — Hidup sebuah halaman fisik: lima daftar dan kebenaran tentang file page
Artikel ini menghubungkan basis data PFN, Standby, Modified, kompresi memori, dan file page untuk menjelaskan ke mana halaman fisik pergi...
Kedalaman memori Windows (bagian 1) — Saat alamat virtual menjadi RAM fisik: page fault dari awal sampai akhir
Artikel ini menghubungkan VirtualAlloc, VAD, tabel halaman, TLB, demand-zero, dan hard fault untuk menjelaskan saat sebuah alamat virtual...
Kedalaman virtualisasi Windows (Bagian 3) — Mesin virtual yang boot dalam hitungan detik: mengapa WSL2, Windows Sandbox, dan kontainer begitu ringan
Mengapa WSL2 dan Windows Sandbox start dalam hitungan detik dan terasa begitu ringan? Artikel ini menjelaskan mekanismenya, dari dynamic ...
Kedalaman virtualisasi Windows (Bagian 2) — Memori yang bahkan kernel pun tidak bisa lihat: cara kerja VBS, HVCI, dan Credential Guard
Pada instalasi bersih ke perangkat keras yang kompatibel, VBS diaktifkan secara bawaan dan memakai hypervisor serta SLAT untuk membuat is...
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.
- Apakah setiap proses yang memakai DLL yang sama mendapat salinan penuh DLL itu di RAM?
- Biasanya tidak. Halaman yang belum diubah dari image yang sama dipetakan dari alamat virtual berbeda setiap proses ke halaman fisik yang sama. Hanya halaman yang perlu ditulis yang menjadi halaman fisik milik proses, lewat copy-on-write dan sejenisnya.
- Apakah CreateFileMapping mengalokasikan memori ke proses pada saat itu?
- CreateFileMapping membuat objek pemetaan berkas, tetapi MapViewOfFile-lah yang membuatnya terlihat di ruang virtual proses. Lebih lanjut, halaman fisik tampilan biasanya diwujudkan oleh page fault mulai dari halaman pertama yang diakses.
- Apa perbedaan FILE_MAP_WRITE dan FILE_MAP_COPY?
- Perubahan lewat FILE_MAP_WRITE adalah penulisan yang tercermin di sisi data berkas bersama. FILE_MAP_COPY berbagi halaman awal, tetapi hanya halaman yang ditulis yang menjadi salinan milik proses; perubahan tidak ditulis kembali ke berkas asli dan hilang saat tampilan dilepas.
- Setelah copy-on-write, apakah VirtualQuery mengembalikan MEM_PRIVATE?
- Tidak. Tampilan data tetap MEM_MAPPED dan tampilan image tetap MEM_IMAGE. Untuk melihat apakah halaman benar-benar diprivatisasi, jadikan halaman residens lalu lihat bit Shared dari QueryWorkingSetEx.
- Bolehkah menyimpan pointer mentah di memori bersama?
- Biasanya tidak. Meski section-nya sama, tidak ada jaminan tampilan setiap proses diletakkan di alamat virtual yang sama. Dalam struktur bersama, gunakan offset dari basis, integer lebar tetap, serta tata letak dan skema sinkronisasi yang eksplisit.
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.