Chiều sâu bộ nhớ Windows (phần 1) — Khoảnh khắc địa chỉ ảo trở thành RAM vật lý: page fault từ đầu đến cuối
· Go Komura · Windows, Quản lý bộ nhớ, VirtualAlloc, Page fault, VAD, Giám sát hiệu năng
Truyền MEM_COMMIT cho VirtualAlloc làm Commit tăng ngay lúc đó. Working Set, tuy nhiên, không nhất thiết tăng cùng lượng. Vậy bộ nhớ bạn nghĩ đã cấp nằm ở đâu?
Câu trả lời là hầu hết trang chưa có RAM vật lý tương ứng. Windows trì hoãn gán trang vật lý cho đến khi ứng dụng thực sự chạm trang. Khi lần truy cập đầu khiến CPU nâng page fault, trình quản lý bộ nhớ xem VAD, PTE, thuộc tính bảo vệ và backing store, rồi gắn RAM từng trang nếu cần.1
Bài này lần theo đường «byte đầu tiên bạn chạm» đi đến RAM vật lý. Nếu muốn sắp xếp trước ý nghĩa các con số như Working Set và Commit, xem bài dẫn nhập «What Does Windows’ “Memory Usage” Actually Mean? — Correctly Reading Working Set, Private Bytes, Commit, and the Page File». Series này không định nghĩa lại thuật ngữ dùng ở đó; nó đào từ phía cơ chế «vì sao con số ra như vậy».
«Chiều sâu bộ nhớ Windows» — cả 3 phần
- Phần 1 (bài này): địa chỉ ảo và page fault
Ta lần theo khi nào vùng cấp bằngVirtualAllocnhận RAM vật lý. - Phần 2: đời một trang vật lý
Ta lần theo trang rời Working Set di chuyển qua Modified, Standby, Free và Zeroed như thế nào. - Phần 3: đối tượng section và copy-on-write
Ta lần theo vì sao DLL, ánh xạ tệp và bộ nhớ dùng chung có thể chia sẻ trang vật lý.
Câu hỏi phần 1 trả lời chỉ có một.
Vào khoảnh khắc nào địa chỉ ảo đã Commit trở thành RAM vật lý?
Độc giả dự kiến là nhà phát triển và vận hành muốn hiểu từ cơ chế mức dùng bộ nhớ ứng dụng Windows, page fault ngay sau khởi động, 0xC0000005, và các số trong VMMap cùng PerfMon. Điều kiện tiên quyết là Windows 10/11 hoặc Windows Server hiện hành, và nền cần có là con trỏ cùng cơ bản VirtualAlloc; không cần kinh nghiệm bố trí bit bảng trang hay trình gỡ lỗi nhân. Độ khó trung cấp. Ta dùng tên cấu trúc nội bộ, nhưng không giả định bố trí không tài liệu phụ thuộc bản dựng Windows cụ thể.
1. Kết luận trước
Luồng bộ nhớ riêng thông thường, một dòng, là thế này.
Reserve dành sẵn dải địa chỉ ảo, Commit ghi nhận commit charge vào giới hạn commit để bảo đảm chỗ giữ nội dung trong tương lai, và page fault ở lần truy cập đầu gán trang vật lý.
Nói cách khác, MEM_COMMIT không phải lệnh «cấp RAM ngay». Tài liệu Microsoft về VirtualAlloc cũng bảo đảm nội dung ban đầu của trang đã Commit là không, đồng thời giải thích trang vật lý thực không được gán cho đến khi địa chỉ ảo được truy cập.1
Dẫu vậy, cũng không chính xác khi khẳng định «Reserve/Commit chỉ ghi vào VAD». Thực tế, Reserve chủ yếu tạo VAD biểu diễn dải địa chỉ ảo và thuộc tính của nó, còn Commit tăng Commit Total hệ thống và ghi trạng thái đã commit của dải. Các tầng bảng trang trung gian và từng PTE được xây lười khi cần, và gắn kết cuối với RAM vật lý thường xảy ra ở lần truy cập đầu.
Commit không phải lời hứa rỗng; đó là lời hứa toàn hệ thống rằng nội dung có thể được giữ trong tương lai ở RAM hoặc backing store thích hợp. Điểm vào hiện thực hóa lời hứa đó từng trang một chính là page fault.
flowchart TB
accTitle: Điều xảy ra ở Reserve, Commit và lần truy cập đầu
accDescr: MEM_RESERVE ghi dải và thuộc tính vào VAD, MEM_COMMIT tiêu hao Commit Total để hứa lưu trữ, và page fault ở lần truy cập đầu gán trang vật lý rồi thêm vào Working Set
reserve["1. MEM_RESERVE"] --> commit["2. MEM_COMMIT"]
commit --> touch["3. Lần truy cập đầu(Touch)"]
reserve -.-> vad["Ghi dải và thuộc tính vào VAD"]
commit -.-> charge["Tiêu hao Commit Total(chưa có trang vật lý)"]
touch --> fault["Page fault"]
fault --> zero["Gắn trang vật lý đã xóa không vào PTE"]
zero --> ws["Thêm vào Working Set và thực thi lại lệnh"]
Hình 1: Reserve, Commit và Touch là các sự kiện riêng. RAM vật lý chỉ được gắn ở bước cuối, lần truy cập đầu.
2. Ba sổ cái theo dõi một trang ảo
Để hiểu đường từ địa chỉ ảo tới RAM vật lý, cần phân biệt ba loại sổ cái Windows giữ.
| Sổ cái | Đơn vị | Vai trò |
|---|---|---|
| VAD | Dải địa chỉ ảo | Quản lý vùng là gì, Reserve/Commit, bảo vệ và tương ứng section |
| Bảng trang / PTE | Trang ảo | Biểu diễn bản dịch hiện tại sang trang vật lý, hoặc trạng thái chưa hiện thực |
| Cơ sở PFN | Trang vật lý | Theo dõi sở hữu, tham chiếu và trạng thái từng trang RAM |
VAD giữ thông tin về dải, PTE về trang ảo, cơ sở PFN về trang vật lý. Bộ xử lý page fault đối chiếu chúng để quyết định truy cập có thể tiếp tục không.
flowchart TB
accTitle: Ba sổ cái từ địa chỉ ảo tới RAM vật lý
accDescr: Địa chỉ ảo do VAD quản lý ở độ hạt dải và PTE ở độ hạt trang ảo, còn cơ sở PFN theo dõi trang vật lý là đích bản dịch của PTE ở độ hạt trang vật lý
va["Địa chỉ ảo"] --> vad["VAD(sổ cái dải)"]
va --> pte["PTE(sổ cái trang ảo)"]
vad -.->|Đánh giá Reserve/Commit và bảo vệ| pte
pte -->|Bản dịch hợp lệ| pfn["Cơ sở PFN(sổ cái trang vật lý)"]
pfn --> ram["Trang RAM vật lý"]
Hình 2: Ba sổ cái ở các độ hạt khác nhau. Xử lý fault đối chiếu VAD và PTE rồi phản ánh kết quả sang phía PFN.
Nhân vật chính của bài này là VAD và PTE. Ta sẽ xem cơ sở PFN từ phía trang vật lý ở phần 2.
3. Reserve, Commit và Touch là các sự kiện riêng
3.1. Reserve — chiếm một địa chỉ
Trước hết, dành một dải địa chỉ ảo liên tục 256MiB.
void* base = VirtualAlloc(
nullptr,
256ull * 1024 * 1024,
MEM_RESERVE,
PAGE_NOACCESS);
Điều xảy ra tại điểm này chỉ là một địa chỉ được dành sẵn trong không gian ảo của tiến trình để các cấp phát khác không dùng dải này. MEM_RESERVE không gán lưu trữ vật lý trong RAM hay tệp trang.1
Vì tiến trình 64-bit có không gian ảo rất lớn, việc Reserve dải lớn trước rồi Commit chỉ các phần cần sau trở nên thực tế.
3.2. Commit — hứa rằng có thể giữ được
Tiếp theo, Commit dải đã dành.
void* committed = VirtualAlloc(
base,
256ull * 1024 * 1024,
MEM_COMMIT,
PAGE_READWRITE);
Khi thành công, lượng được hứa phản ánh trong Commit Total hệ thống — và thường trong Private Bytes của tiến trình — tăng. Dù vậy, 256MiB trang vật lý không xếp hàng cùng lúc. Các trang thông thường vẫn chưa được gán vật lý cho đến lần truy cập đầu.12
Vậy Commit để làm gì? Là khi hệ thống không nhận nổi lời hứa, nó có thể trả thất bại ngay lúc Commit, chứ không giữa lúc dùng bộ nhớ.
3.3. Touch — khi trang vật lý trở nên cần
Cuối cùng, phép gán sau ghi lần đầu vào trang đầu.
static_cast<unsigned char*>(base)[0] = 1;
CPU cố dịch địa chỉ ảo thành địa chỉ vật lý, nhưng PTE chưa có bản dịch hợp lệ sang trang vật lý. Page fault xảy ra ở đây.
Trình quản lý bộ nhớ nhận quyền điều khiển đánh giá đây là «lần truy cập đầu tới trang riêng đã Commit và ghi được», lấy một trang vật lý đã xóa không, gắn vào PTE và thêm vào Working Set. Rồi nó thực thi lại lệnh ghi đã thất bại.
Từ ứng dụng trông như một phép gán thường, nhưng bên trong điều khiển vào nhân giữa phép gán, trang vật lý được gán, và thực thi trở lại cùng lệnh.
4. VAD — sổ cái dải của không gian ảo
VAD viết tắt Virtual Address Descriptor, và Windows quản lý các dải địa chỉ đang dùng của tiến trình như cây VAD. Với lệnh !vad của WinDbg bạn có thể xem VPN đầu và cuối, Commit, thuộc tính bảo vệ, Private/Mapped, Control Area và hơn thế.3
Thông tin tiêu biểu VAD ghi gồm những mục sau.
- Đầu và cuối dải địa chỉ
- Loại như Private, Mapped hay Image
- Trạng thái Reserve/Commit
- Bảo vệ như đọc, ghi, thực thi và copy-on-write
- Tương ứng với tệp hoặc section
- Thuộc tính đặc biệt như trang canh
Lý do quản lý theo dải là hiệu quả. 256MiB là 65.536 trang ở 4KiB. Thay vì xây sẵn cấu trúc quản lý đầy đủ cho mọi trang, ít lãng phí hơn khi giữ «dải liên tục này là một chỗ dành» trong VAD và hiện thực hóa trang khi cần.
4.1. Tìm thấy VAD không bảo đảm phục hồi
«Nếu nằm trong VAD thì fault được giải; nếu không thì vi phạm truy cập» là lời giải thích nhập môn tiện, nhưng đơn giản hóa quá. Ngay cả khi tìm thấy VAD, truy cập thường không thể tiếp tục trong các trường hợp như sau.
- Chỉ Reserve, và trang đích chưa Commit
PAGE_NOACCESS- Ghi vào trang chỉ đọc
- Thực thi lệnh từ trang không thực thi được
- Lần chạm đầu trang canh
- Chạm ngoài dải hợp lệ của một section
Ngược lại, dù PTE không hợp lệ, nếu trạng thái phần mềm của VAD và PTE cho thấy truy cập hợp pháp, có thể giải bằng demand-zero, khôi phục Transition, page-in hoặc CoW. Chính xác hơn, câu trả lời là đánh giá cùng lúc VAD, PTE, thuộc tính bảo vệ và loại truy cập.
5. Bảng trang và TLB
Con trỏ ứng dụng giữ là địa chỉ ảo. Để CPU truy cập RAM, nó phải dịch số trang ảo thành số trang vật lý. Bảng dịch phân tầng đó là bảng trang, và mục lá là PTE (Page Table Entry).
PTE hợp lệ về khái niệm giữ PFN, bảo vệ đọc/ghi/thực thi, quyền chế độ người dùng, Accessed/Dirty và thông tin tương tự. Bố trí bit thực phụ thuộc CPU và phiên bản Windows.
Đi bảng trang mỗi lần sẽ quá chậm, nên CPU lưu bản dịch gần đây trong TLB (Translation Lookaside Buffer). Dịch địa chỉ diễn ra theo thứ tự này.
- Nếu TLB có bản dịch và truy cập khớp bảo vệ đó, dùng kết quả đó.
- Nếu TLB không có bản dịch, CPU đi bảng trang.
- Nếu có PTE hợp lệ và bảo vệ cũng khớp, đăng ký vào TLB và tiếp tục thực thi.
- Nếu không có bản dịch hợp lệ, hoặc có vi phạm bảo vệ, điều khiển đi tới điểm vào page fault. Kiểm tra bảo vệ cũng được thực hiện khi bản dịch đến từ TLB.
Như luồng này cho thấy, TLB miss và page fault là hai thứ khác nhau. Nếu vấn đề duy nhất là TLB không có bản dịch và PTE hợp lệ, chỉ xảy ra đi bảng trang. Ngược lại, dù TLB có bản dịch, vi phạm bảo vệ như ghi vào trang chỉ đọc hoặc thực thi lệnh trên trang không thực thi được vẫn đi tới điểm vào page fault. Đó là lý do ghi vào trang CoW có thể fault ngay cả khi bản dịch đã được lưu.
flowchart TB
accTitle: Luồng dịch địa chỉ và điểm vào page fault
accDescr: Dù TLB có bản dịch, không khớp bảo vệ vẫn đi tới điểm vào page fault. Nếu TLB không có bản dịch thì đi bảng trang; PTE hợp lệ cũng khớp bảo vệ được đăng ký vào TLB và tiếp tục, còn bản dịch không hợp lệ hoặc vi phạm bảo vệ đi tới điểm vào page fault
access["Truy cập bộ nhớ"] --> tlb{"TLB có bản dịch không?"}
tlb -->|Có| perm{"Truy cập khớp bảo vệ không?"}
perm -->|Khớp| go["Tiếp tục với bản dịch đó"]
perm -->|Vi phạm bảo vệ| entry["Tới điểm vào page fault"]
tlb -->|Không| walk["Đi bảng trang"]
walk --> valid{"PTE hợp lệ và bảo vệ cũng khớp?"}
valid -->|Có| register["Đăng ký TLB và tiếp tục(không fault)"]
valid -->|Không hợp lệ hoặc vi phạm bảo vệ| entry
Hình 3: TLB miss có thể giải bằng đi bảng trang. Điều khiển đi tới page fault khi bản dịch không hợp lệ hoặc có vi phạm bảo vệ, và vi phạm bảo vệ xảy ra cả khi TLB hit.
5.1. PTE không hợp lệ không chỉ là chỗ trống
Ngay cả PTE không hợp lệ cũng không rỗng. Từ trạng thái phần mềm của PTE không hợp lệ, Windows phân biệt các trường hợp như sau.
- Trang demand-zero chưa từng được hiện thực
- Trang Transition còn trong RAM
- Trang dùng chung trỏ tới Prototype PTE
- Trang riêng lưu trong tệp trang
- Vi phạm bảo vệ hoặc vùng không hợp lệ
Việc của CPU chỉ là quyết định «đây không phải bản dịch hợp lệ thường» và giao cho nhân; trình quản lý bộ nhớ cung cấp ý nghĩa từ đó.
6. Page fault từ đầu đến cuối
Hãy lần theo lần ghi đầu vào trang riêng đã Commit qua sáu giai đoạn.
- CPU cố ghi.
Nó kiểm tra TLB và bảng trang, nhưng PTE đích không có PFN hợp lệ. - CPU nâng page fault.
Nó chuyển địa chỉ ảo bị fault, loại đọc/ghi/thực thi, người dùng/nhân, và vấn đề là thiếu bản dịch hay vi phạm bảo vệ cho nhân. - Trình quản lý bộ nhớ xem VAD và PTE.
Nó quyết định trang đã Commit chưa, bảo vệ có khớp không, và cái nào trong demand-zero, Transition, dùng chung, page-in, CoW hay ngoại lệ áp dụng. - Nếu là demand-zero, lấy trang vật lý đã xóa không.
Trang vừa giao phải là không để dữ liệu tiến trình khác không bị rò. - Thông tin quản lý PTE và PFN được cập nhật.
PFN và bảo vệ được đặt trong PTE, trang vật lý thành Active, và được thêm vào Working Set của tiến trình. - Lệnh thất bại được thực thi lại.
Vì fault giải bình thường, không có ngoại lệ chế độ người dùng được giao, và ứng dụng tiếp tục phép gán như thường.
Sự kiện ETW page fault cũng ghi Transition, Demand Zero, Copy-on-Write, Guard Page, Hard Page Fault và Access Violation thành các loại riêng.4
Vậy page fault không phải từ có nghĩa «bất thường» từ đầu. Đó là điểm vào chung để nhờ hệ điều hành quyết khi CPU không dịch được trên đường thường.
flowchart LR
accTitle: Phân nhánh cách giải page fault
accDescr: Trình quản lý bộ nhớ đánh giá VAD, PTE, thuộc tính bảo vệ và loại truy cập, rồi chuyển tới demand-zero, nối lại trang còn trong RAM, hard fault từ backing store, copy-on-write, thông báo trang canh hoặc ngoại lệ
faultIn["Page fault xảy ra"] --> judge["Đánh giá VAD, PTE, bảo vệ, loại"]
judge -->|Lần truy cập đầu| dz["Demand-zero(mềm)"]
judge -->|Còn trong RAM| soft["Nối lại từ Standby(mềm)"]
judge -->|Cần đọc đĩa| hard["Hard fault(I/O đĩa)"]
judge -->|Ghi CoW| cow["Sao chép và đổi PTE"]
judge -->|Trang canh| guard["Gỡ canh và thông báo"]
judge -->|Không giải được| av["Ngoại lệ(0xC0000005 v.v.)"]
Hình 4: Fault vào cùng một điểm vào tách thành sáu loại kết cục tùy đánh giá. Chi tiết trang canh ở mục 9.
7. Demand-zero — soft fault không đọc đĩa
Demand-zero là soft fault tiêu biểu xảy ra khi lần đầu chạm trang riêng đã Commit. Tài liệu Working Set của Microsoft cũng liệt «tiến trình tham chiếu lần đầu tới trang ảo đã cấp» làm ví dụ soft fault.5
Demand-zero có các đặc điểm sau.
- Không cần đọc dữ liệu gốc từ đĩa
- Nội dung ban đầu là không
- Một trang vật lý sẵn có được gắn
- Working Set và Page Fault Count tích lũy tăng
- Chỉ xử lý này không tăng
Memory\\Pages Input/sec
Đó là lý do đỉnh Page Faults/sec ngay sau khởi động tự nó không có nghĩa lưu trữ là nút thắt.
Đánh đổi của cấp phát lười cũng đáng sắp xếp. Nếu bạn Commit 256MiB và thực sự chỉ dùng 8MiB, để 248MiB còn lại ngoài RAM là hợp lý. Đổi lại, lần truy cập đầu mang chi phí xử lý fault. Với công việc nhạy trễ có thiết kế chạm từng trang trước khi bắt đầu để prefault, nhưng đó là đánh đổi tăng cư trú RAM từ trước.
8. Soft fault và hard fault
8.1. Soft fault
Soft fault là fault giải được mà không cần I/O đọc tới backing store. Các ví dụ tiêu biểu gồm những mục sau.
- Demand-zero
- Nối lại trang còn trên Standby/Transition
- Nối trang dùng chung đang ở Working Set tiến trình khác
- Nối trang đã prefetch
- Copy-on-Write mà trang gốc còn cư trú
Vẫn có chi phí CPU cho chuyển nhân, khóa, cập nhật PTE/PFN, nhất quán TLB và tương tự, nhưng không chờ lưu trữ.5
8.2. Hard fault
Mặt khác, khi trang cần không ở đâu trong RAM và phải đọc từ backing store, đó là hard fault. Nguồn đọc không chỉ là tệp trang.
- Trang riêng đã ghi ra tệp trang
- Tệp ánh xạ bộ nhớ
- Ảnh EXE hoặc DLL
- Tệp dữ liệu mà bộ nhớ đệm tệp tham chiếu
Sự kiện ETW HardFault gồm FileObject, ReadOffset và ByteCount, nên bạn có thể lần nguồn đọc thực.6
Do đó Hard Fault = đọc pagefile.sys không đúng.
Khi cần đọc backing store, yêu cầu vào ngăn xếp I/O Windows. Luồng IRP và phát hành/hoàn tất được trình bày trong «The Depths of Windows I/O (Part 1)», và chỗ nối với bộ nhớ đệm tệp trong «The Depths of Windows I/O (Part 4)». Nếu trang ở RAM, trình quản lý bộ nhớ có thể tự trở về; nếu không, nó phát I/O và chờ luồng bị fault đến khi xong.
9. Fault không giải được trở thành ngoại lệ
Fault mà sau khi xem VAD và PTE không giải được như cấp phát hợp pháp, page-in hay CoW được giao cho chế độ người dùng như ngoại lệ.
Trường hợp tiêu biểu là STATUS_ACCESS_VIOLATION, mã ngoại lệ 0xC0000005. Nó xảy ra khi đọc, ghi hoặc thực thi địa chỉ không hợp lệ; tham số ngoại lệ thứ nhất chỉ loại truy cập, thứ hai chỉ địa chỉ vi phạm.7
Các mẫu điển hình gồm những mục sau.
- Đọc NULL, địa chỉ đã giải phóng hoặc địa chỉ ngoài mảng
- Ghi vào trang chỉ đọc
- Thực thi lệnh từ trang mà DEP/NX đã làm không thực thi được
- Chạm dải đã dành nhưng chưa Commit
PAGE_GUARD có nghĩa hơi khác. Đó là thông báo một lần về truy cập: nó nâng STATUS_GUARD_PAGE_VIOLATION và dùng cho việc như tăng stack.8
Cấp phát lười bình thường, page-in, CoW, thông báo canh và vi phạm truy cập cuối cùng, từ góc CPU, đều tụ tại cùng điểm vào page fault. Điều quyết kết cục là tổ hợp VAD, PTE, thuộc tính bảo vệ và loại truy cập.
10. Tự xem
Bạn có thể quan sát luồng đến đây trên máy mình. Chương trình C++ sau Reserve 256MiB, Commit, ghi một byte vào mỗi trang, rồi cuối cùng Release. Nó chờ Enter ở mỗi giai đoạn để bạn quan sát thay đổi trong VMMap và PerfMon.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>
#include <cstdio>
#include <cstdlib>
#pragma comment(lib, "Psapi.lib")
constexpr SIZE_T kSize = 256ull * 1024 * 1024;
void PrintMemory(const char* stage)
{
PROCESS_MEMORY_COUNTERS_EX c{};
c.cb = sizeof(c);
if (!GetProcessMemoryInfo(
GetCurrentProcess(),
reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&c),
sizeof(c))) {
std::printf("GetProcessMemoryInfo failed: %lu\n", GetLastError());
return;
}
std::printf(
"%-10s WS=%zu MiB Private=%zu MiB Faults=%lu\n",
stage,
c.WorkingSetSize / 1024 / 1024,
c.PrivateUsage / 1024 / 1024,
c.PageFaultCount);
}
void Pause(const char* message)
{
PrintMemory(message);
std::puts("Press Enter...");
(void)std::getchar();
}
int main()
{
SYSTEM_INFO si{};
GetSystemInfo(&si);
std::printf("PID=%lu, page=%lu bytes\n",
GetCurrentProcessId(), si.dwPageSize);
void* base = VirtualAlloc(nullptr, kSize, MEM_RESERVE, PAGE_NOACCESS);
if (!base) {
std::fprintf(stderr, "Reserve failed: %lu\n", GetLastError());
return EXIT_FAILURE;
}
Pause("reserved");
if (!VirtualAlloc(base, kSize, MEM_COMMIT, PAGE_READWRITE)) {
std::fprintf(stderr, "Commit failed: %lu\n", GetLastError());
VirtualFree(base, 0, MEM_RELEASE);
return EXIT_FAILURE;
}
Pause("committed");
auto* bytes = static_cast<volatile unsigned char*>(base);
for (SIZE_T offset = 0; offset < kSize; offset += si.dwPageSize) {
bytes[offset] = 1;
}
Pause("touched");
if (!VirtualFree(base, 0, MEM_RELEASE)) {
std::fprintf(stderr, "Release failed: %lu\n", GetLastError());
return EXIT_FAILURE;
}
Pause("released");
}
Từ x64 Native Tools Command Prompt của Visual Studio, bạn có thể dựng bằng lệnh sau.
cl /std:c++20 /EHsc /W4 memory_fault_demo.cpp
10.1. Nhìn gì trong VMMap
VMMap là công cụ hiển thị bộ nhớ ảo đã dành, Commit, Working Set, Private và Shareable theo loại.9 Thay đổi kỳ vọng ở mỗi giai đoạn như sau.
| Giai đoạn | Thay đổi kỳ vọng |
|---|---|
| Reserve | Address Space Size tăng, nhưng Commit/WS không tăng cùng lượng |
| Commit | Private Commit tăng khoảng 256MiB |
| Touch | Working Set và Private WS tăng rõ, Fault Count cũng tăng |
| Release | Dải đích biến mất, Commit và WS giảm |
Số thực tế thay đổi theo runtime, sản phẩm bảo mật, áp lực bộ nhớ và lúc bạn quan sát. Hãy nhìn số đi hướng nào giữa các giai đoạn, không phải có ra đúng 256MiB hay không.
10.2. Tách mềm và cứng trong PerfMon
Trong PerfMon, đặt các bộ đếm sau trên cùng trục thời gian.
Process(<target>)\\Page Faults/secMemory\\Pages Input/secMemory\\Page Reads/secMemory\\Available MBytesProcess(<target>)\\Working Set - PrivateProcess(<target>)\\Private Bytes
Process\\Page Faults/sec gồm cả soft fault và hard fault. Memory\\Pages Input/sec, ngược lại, là số trang đọc từ đĩa để giải hard fault.10
Ở giai đoạn Touch của chương trình này, Page Faults/sec nên nhảy trong khi Pages Input/sec không nên tăng nhiều. Các trang mới Commit được hiện thực bằng demand-zero, nên không cần đọc dữ liệu gốc từ đĩa.
Khi vài tiến trình chia cùng tên, số PerfMon như process#1 có thể đổi qua lần khởi động lại. Đối chiếu với bộ đếm hiện PID, hoặc nhận dạng theo PID bằng Process V2 hoặc ETW/WPA.
11. Ba cách đọc sai cần tránh trong thực tế
11.1. «Commit tăng nên đây là rò RAM»
Commit là lượng nội dung được hứa giữ; trang chưa chạm có thể không cư trú trong RAM. Để đánh giá rò, nhìn chuỗi thời gian Private Bytes, phân tách cấp phát, và số có trở về đường cơ sở sau khi xử lý xong không.
11.2. «Page Faults/sec cao nên đĩa chậm»
Soft fault không có I/O đĩa. Tách Page Faults/sec, Pages Input/sec và chờ lưu trữ, và nếu cần lần tệp nguồn cùng ngăn xếp bằng sự kiện ETW HardFault.
11.3. «Làm trống Working Set sẽ sửa rò»
Gỡ trang khỏi Working Set không giải phóng Commit hay sở hữu. Trang đi sang Standby hoặc Modified rồi fault trở lại sau. Sửa rò cần bộ cấp phát thực hiện VirtualFree, giải phóng heap, hủy đối tượng và tương tự.
Trang vật lý bị gỡ đi đâu là điều ta lần theo ở phần 2.
12. Tóm tắt
MEM_RESERVEdành dải địa chỉ ảo nhưng không gán vùng vật lý trong RAM hay tệp trang.1MEM_COMMITtiêu hao Commit và bảo đảm nội dung có thể được giữ trong tương lai, nhưng trang vật lý thường không được gán cho đến lần truy cập đầu.12- VAD là sổ cái dải, PTE sổ cái trang ảo, cơ sở PFN sổ cái trang vật lý.
- TLB miss không phải page fault. Nếu PTE hợp lệ, chỉ đi bảng trang là giải được.
- Demand-zero, khôi phục Transition và nối trang dùng chung là soft fault giải được không cần I/O đĩa.5
- Nếu cần đọc từ tệp trang, DLL, EXE hoặc tệp ánh xạ, đó là hard fault.6
- Nếu kiểm tra VAD, PTE và thuộc tính bảo vệ không giải được fault, bạn nhận ngoại lệ như
0xC0000005.7 - Để đánh giá hiệu năng, đừng chỉ nhìn
Page Faults/sec; nhìnPages Input/sec, Available, Working Set, Private Bytes và chờ lưu trữ trên cùng trục thời gian.
Tiếp theo phần 2, «Đời một trang vật lý: năm danh sách và sự thật về tệp trang».
Sau khi lời hứa Commit đã thành trang vật lý, ta lần theo trang đó đi đâu khi rời Working Set, từ cơ sở PFN và các danh sách trang.
Bài viết liên quan
- What Does Windows’ “Memory Usage” Actually Mean? — Correctly Reading Working Set, Private Bytes, Commit, and the Page File
- The Depths of Windows I/O (Part 1) — Every Read and Write Becomes an IRP: The Big Picture of the I/O System
- The Depths of Windows I/O (Part 4) — Cache Manager: When Does Your WriteFile Actually Reach the Disk?
- Reading Crash Dumps with WinDbg + SOS — A Practical Guide to Analysis After Collection
- An Introduction to Collecting Windows Crash Dumps - WER/ProcDump/WinDbg
Lĩnh vực tư vấn liên quan
KomuraSoft LLC xử lý điều tra mức dùng bộ nhớ ứng dụng Windows, vi phạm truy cập, trễ khởi động, phân trang và khiếm khuyết mã gốc.
- Phát triển ứng dụng Windows
- Điều tra lỗi và phân tích nguyên nhân gốc rễ
- Di chuyển tài sản kế thừa
- Liên hệ với chúng tôi
Liên kết tham khảo
-
Microsoft Learn, VirtualAlloc function. Về
MEM_RESERVEdành dải địa chỉ ảo mà không gán lưu trữ vật lý;MEM_COMMITghi nhận commit charge đối với bộ nhớ và tệp trang hệ thống; nội dung ban đầu của trang đã Commit là không; và trang vật lý thực không được gán cho đến khi được truy cập. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 -
Microsoft Learn, PERFORMANCE_INFORMATION structure. Về
CommitTotallà số trang Commit hiện tại của hệ thống, vàCommitLimitlà cận trên có thể Commit mà không mở rộng tệp trang. ↩ ↩2 -
Microsoft Learn, !vad (WinDbg). Về
!vadhiển thị cây VAD và cho phép xem VPN đầu và cuối, Commit, Mapped/Private, thuộc tính bảo vệ, Control Area và hơn thế. ↩ -
Microsoft Learn, PageFault_TypeGroup1 class. Về ETW phân biệt và ghi Transition Fault, Demand Zero Fault, Copy-on-Write, Guard Page Fault, Hard Page Fault và Access Violation. ↩
-
Microsoft Learn, Working Set. Về soft fault giải được mà không truy cập backing store, và xảy ra từ Working Set tiến trình khác, Transition, demand-zero tham chiếu đầu và tương tự. ↩ ↩2 ↩3
-
Microsoft Learn, PageFault_HardFault class. Về sự kiện HardFault gồm FileObject, ReadOffset, ByteCount, VirtualAddress và ID luồng, nên có thể lần nguồn đọc. ↩ ↩2
-
Microsoft Learn, Access Violation C0000005. Về
0xC0000005xảy ra khi đọc, ghi hoặc thực thi địa chỉ bộ nhớ không hợp lệ, và tham số ngoại lệ chỉ loại truy cập cùng địa chỉ vi phạm. ↩ ↩2 -
Microsoft Learn, Creating Guard Pages. Về
PAGE_GUARDcung cấp thông báo một lần về truy cập trang và nângSTATUS_GUARD_PAGE_VIOLATION. ↩ -
Microsoft Learn, VMMap - Sysinternals. Về VMMap tách bộ nhớ ảo đã Commit theo loại và hiển thị Working Set từng loại cùng bản đồ địa chỉ chi tiết. ↩
-
Microsoft Learn, Performance Analysis of Logs (PAL) Tool. Về
Memory\\Pages Input/seclà số trang đọc từ đĩa để giải hard page fault. ↩
Bài viết liên quan
Các bài viết gần đây có cùng thẻ để tìm hiểu sâu hơn những chủ đề lân cận.
Chiều sâu bộ nhớ Windows (phần 2) — Đời sống của một trang vật lý: năm danh sách và sự thật về tệp trang
Bài viết nối cơ sở PFN, Standby, Modified, nén bộ nhớ và tệp trang để giải thích trang vật lý đi đâu sau khi rời Working Set.
Chiều sâu bộ nhớ Windows (phần 3) — Đối tượng section và copy-on-write: DLL và ánh xạ tệp thực sự là gì
Bài này nối đối tượng section, ánh xạ image và dữ liệu, cache dùng chung và copy-on-write để giải thích cách DLL và bộ nhớ dùng chung chi...
Bên trong ảo hóa Windows (Phần 3) — Máy ảo khởi động trong vài giây: Vì sao WSL2, Windows Sandbox và container lại nhẹ
Vì sao WSL2 và Windows Sandbox khởi động trong vài giây và cảm giác nhẹ đến vậy? Bài viết này giải thích các cơ chế, từ ảnh cơ sở động và...
Bên trong ảo hóa Windows (Phần 2) — Bộ nhớ ngay cả kernel cũng không thấy: VBS, HVCI và Credential Guard
Khi cài sạch trên phần cứng tương thích, VBS được bật mặc định và dùng hypervisor cùng SLAT để tạo cô lập mạnh hơn kernel. Bài viết này g...
Bên trong ảo hóa Windows (Phần 1) — Windows của bạn thực sự chạy ở đâu? Hypervisor và phân vùng
Khi bạn bật Hyper-V, chính Windows máy chủ chạy trên hypervisor như phân vùng gốc. Bài viết này giải thích nền tảng ảo hóa qua vai trò củ...
Chủ đề liên quan
Các trang này đặt chủ đề trong bối cảnh rộng hơn của dịch vụ và quyết định.
Chủ đề kỹ thuật Windows
Cổng vào phát triển Windows, điều tra lỗi và khai thác tài sản hiện có.
Dịch vụ liên quan đến chủ đề này
Bài viết liên quan trực tiếp đến các dịch vụ sau.
Phát triển ứng dụng Windows
Ứng dụng nghiệp vụ, tích hợp thiết bị và công cụ liên lạc, từ yêu cầu đến phát triển.
Câu hỏi thường gặp
Các câu hỏi thường gặp khi tư vấn về chủ đề của bài viết.
- Truyền MEM_COMMIT cho VirtualAlloc có cấp RAM ngay lúc đó không?
- Trong bộ nhớ riêng thông thường, Commit tiêu hao dư địa commit của hệ thống, nhưng trang vật lý tương ứng chưa được gán cho đến lần truy cập đầu. Trang lần đầu chạm bằng ghi nhận trang vật lý trong lúc xử lý demand-zero fault.
- Page fault có nghĩa là có sự cố hoặc vấn đề hiệu năng không?
- Không. Soft fault không có I/O đĩa — như demand-zero hoặc trả trang từ Standby — là vận hành bình thường. Để đánh giá hiệu năng, đừng chỉ nhìn Page Faults/sec mà còn Pages Input/sec, chờ lưu trữ và Available MBytes.
- TLB miss và page fault có phải cùng một thứ không?
- Chúng khác nhau. Dù TLB không có bản dịch, nếu PTE của bảng trang hợp lệ CPU chỉ đi bảng và đăng ký lại bản dịch. Nó đi tới điểm vào page fault khi PTE không hợp lệ hoặc có vi phạm bảo vệ.
- Nếu dải địa chỉ nằm trong VAD thì không thể bị vi phạm truy cập?
- Không nhất thiết. Ngoài việc có VAD, trình quản lý bộ nhớ còn đánh giá Reserve so với Commit, bảo vệ đọc/ghi/thực thi, trang canh, trạng thái PTE và hơn thế. Nếu không giải được fault, bạn nhận ngoại lệ như 0xC0000005.
- Page Faults/sec cao có nghĩa hệ thống thiếu RAM không?
- Chỉ thế thì không biết được. Page Faults/sec cũng gồm rất nhiều soft fault. Cần đối chiếu trên cùng trục thời gian với Memory\Pages Input/sec, Memory\Page Reads/sec, Available MBytes và thời gian chờ đĩa.