Kedalaman memori Windows (bagian 3) — Objek section dan copy-on-write: apa sebenarnya DLL dan pemetaan berkas

· · 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

  1. Bagian 1: Alamat virtual dan page fault
    Kita mengikuti saat halaman virtual yang sudah di-Commit memperoleh RAM fisik.
  2. Bagian 2: Hidup sebuah halaman fisik
    Kita mengikuti transisi keadaan halaman yang meninggalkan Working Set.
  3. 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 dan 0x000002... 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.
    Dengan SEC_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 memakai SharedCacheMap, pemetaan data memakai DataSectionObject, dan EXE/DLL memakai ImageSectionObject. Ketiganya menempel pada aliran berkas yang sama lewat SECTION_OBJECT_POINTERS, tetapi fault image tidak melewati Cache Manager.5
  • Private Bytes saja tidak dapat memastikan CoW terjadi.
    FILE_MAP_COPY membebankan Commit untuk seluruh tampilan di muka, terhadap kemungkinan setiap halaman nanti diprivatisasi. Untuk pemeriksaan per halaman, pakai bit Shared dari QueryWorkingSetEx.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.

Memetakan alamat virtual berbeda ke halaman fisik yang samaProses 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 samaProses A: 0x000001A00000 + 0x3000Offset section 0x3000Proses B: 0x000002700000 + 0x3000Halaman 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.

Bagaimana file-backed dan pagefile-backed berpisahMemberi 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 dihancurkanBerikan handle berkas nyataBerikan INVALID_HANDLE_VALUECreateFileMappingSection ditopang berkasSection ditopang page fileHalaman bersih dapat dibaca ulang dari berkas asliPage 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.

Keadaan bersama sebelum copy-on-writeSebelum penulisan terjadi, PTE proses A dan PTE proses B keduanya mencapai halaman bersama PFN X yang sama, dan pembacaan berhasil apa adanyaPTE proses APFN X bersama(baca / copy-on-write)PTE proses B

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.

  1. Memperoleh satu halaman fisik untuk proses A.
  2. Menyalin isi PFN X ke halaman baru PFN Y.
  3. Mengganti PTE proses A ke PFN Y.
  4. Mengubah perlindungan proses A menjadi baca/tulis biasa.
  5. Mengeksekusi ulang instruksi tulis yang gagal.
Keadaan terpisah setelah copy-on-writeSetelah 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 XDisalin saat menulisPTE proses APFN Y privat(R/W, setelah tulis)PTE proses BPFN X bersama(asli)

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.

  1. Akses halaman sasaran dan jadikan residens.
  2. Dapatkan informasi Working Set halaman dengan QueryWorkingSetEx.
  3. Lihat bit Shared.
  4. 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 data
  • SharedCacheMap: tampilan cache yang dilacak Cache Manager
  • ImageSectionObject: 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 / WriteFile yang di-cache memakai SharedCacheMap dan 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 ImageSectionObject dan I/O paging. Itu bukan jalur yang melewati SharedCacheMap Cache Manager.
Tiga jalur yang menempel pada aliran berkas yang samaReadFile/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_POINTERSReadFile / WriteFile yang di-cacheSharedCacheMapFault pemetaan dataDataSectionObjectFault image EXE/DLLImageSectionObjectAliran berkas yang sama(SECTION_OBJECT_POINTERS)

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.

  1. Jalankan Process Explorer sebagai administrator.
  2. Jalankan dua proses cmd.exe.
  3. Pilih View > Lower Pane View > DLLs.
  4. Konfirmasikan jalur dan pemetaan DLL yang sama di kedua proses.
  5. Buka setiap cmd.exe di 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, VirtualQuery masih mengembalikan MEM_MAPPED/MEM_IMAGE, jadi Anda mengonfirmasi dengan bit Shared dari QueryWorkingSetEx.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, dan ImageSectionObject.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

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

  1. 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

  2. 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

  3. 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

  4. 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

  5. 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

  6. 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

  7. Microsoft Learn, VirtualQuery function. Tentang Type yang tetap MEM_MAPPED/MEM_IMAGE setelah CoW, dan kemampuan mengonfirmasi privatisasi dengan bit Shared dari QueryWorkingSetEx 2 3 4

  8. Microsoft Learn, Managing Memory Sections. Tentang memori fisik yang tidak ditetapkan sampai tampilan diakses, dan page fault akses pertama yang membaca isi berkas. 

  9. 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

  10. Microsoft Learn, Process Explorer - Sysinternals. Tentang Process Explorer yang dapat menampilkan handle proses serta DLL / berkas terpetakan ke memori yang dimuat. 

  11. 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 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.

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.

Kembali ke blog