Le profondità della memoria di Windows (parte 1) — L'istante in cui un indirizzo virtuale diventa RAM fisica: un page fault dall'inizio alla fine
· Go Komura · Windows, Gestione della memoria, VirtualAlloc, Page fault, VAD, Monitoraggio delle prestazioni
Passare MEM_COMMIT a VirtualAlloc aumenta il Commit in quel momento. Il Working Set, però, non aumenta necessariamente della stessa quantità. Allora dov’è la memoria che pensavate di aver allocato?
La risposta è che la maggior parte delle pagine non ha ancora RAM fisica corrispondente. Windows ritarda l’assegnazione di una pagina fisica finché l’applicazione non tocca davvero la pagina. Quando il primo accesso fa sollevare un page fault alla CPU, il gestore della memoria esamina il VAD, la PTE, gli attributi di protezione e il backing store, e se serve lega la RAM una pagina alla volta.1
Questo articolo segue il percorso che «il primo byte che toccate» compie fino alla RAM fisica. Se volete prima mettere in ordine il significato di cifre come Working Set e Commit, vedete l’articolo introduttivo «What Does Windows’ “Memory Usage” Actually Mean? — Correctly Reading Working Set, Private Bytes, Commit, and the Page File». Questa serie non ridefinisce i termini usati lì; scava dal lato del meccanismo sul «perché esce quella cifra».
«Le profondità della memoria di Windows» — tutte e 3 le parti
- Parte 1 (questo articolo): indirizzi virtuali e page fault
Seguiamo quando una regione allocata conVirtualAllocottiene RAM fisica. - Parte 2: la vita di una pagina fisica
Seguiamo come una pagina che esce dal Working Set si muove tra Modified, Standby, Free e Zeroed. - Parte 3: oggetti section e copy-on-write
Seguiamo perché DLL, mapping di file e memoria condivisa possono condividere pagine fisiche.
La domanda a cui risponde la parte 1 è una sola.
In quale istante un indirizzo virtuale già in Commit diventa RAM fisica?
I lettori previsti sono sviluppatori e operatori che vogliono capire, dal meccanismo, l’uso di memoria delle app Windows, i page fault subito dopo l’avvio, 0xC0000005 e i numeri di VMMap e PerfMon. I prerequisiti sono Windows 10/11 o Windows Server attuale, e lo sfondo richiesto è i puntatori e le basi di VirtualAlloc; non serve esperienza con i layout di bit delle tabelle delle pagine o con un debugger del kernel. La difficoltà è intermedia. Usiamo i nomi delle strutture interne, ma non assumiamo layout non documentati che dipendono da una build specifica di Windows.
1. Prima la conclusione
Il flusso ordinario della memoria privata, in una riga, è questo.
Reserve mette da parte un intervallo di indirizzi virtuali, Commit addebita una quota di commit per garantire un posto futuro in cui conservare il contenuto, e il page fault al primo accesso assegna la pagina fisica.
In altre parole, MEM_COMMIT non è un ordine che dice «assegna RAM subito». Anche la documentazione Microsoft di VirtualAlloc garantisce che il contenuto iniziale di una pagina in Commit è zero, spiegando al tempo stesso che la pagina fisica effettiva non viene assegnata finché non si accede all’indirizzo virtuale.1
Detto questo, è anche inesatto pretendere che «Reserve/Commit scrivano solo nel VAD». In pratica, Reserve crea soprattutto un VAD che rappresenta l’intervallo di indirizzi virtuali e i suoi attributi, e Commit aumenta il Commit Total del sistema e registra lo stato committato dell’intervallo. I livelli intermedi della tabella delle pagine e le singole PTE si costruiscono in modo pigro quando servono, e il legame finale con la RAM fisica avviene di solito al primo accesso.
Il Commit non è una promessa vuota; è una promessa a livello di sistema che il contenuto potrà essere conservato in futuro in RAM o in un backing store adeguato. Il punto di ingresso che concretizza quella promessa una pagina alla volta è il page fault.
flowchart TB
accTitle: Cosa succede a Reserve, Commit e al primo accesso
accDescr: MEM_RESERVE registra l'intervallo e gli attributi nel VAD, MEM_COMMIT consuma il Commit Total per promettere lo storage, e il page fault al primo accesso assegna una pagina fisica e la aggiunge al Working Set
reserve["1. MEM_RESERVE"] --> commit["2. MEM_COMMIT"]
commit --> touch["3. Primo accesso(Touch)"]
reserve -.-> vad["Registrare l'intervallo e gli attributi nel VAD"]
commit -.-> charge["Consumare il Commit Total(ancora nessuna pagina fisica)"]
touch --> fault["Page fault"]
fault --> zero["Legare una pagina fisica azzerata alla PTE"]
zero --> ws["Aggiungerla al Working Set e rieseguire l'istruzione"]
Figura 1: Reserve, Commit e Touch sono eventi distinti. La RAM fisica si lega solo all’ultimo passo, il primo accesso.
2. Tre registri che seguono una pagina virtuale
Per capire il percorso da un indirizzo virtuale alla RAM fisica, occorre distinguere i tre tipi di registro che Windows tiene.
| Registro | Unità | Ruolo |
|---|---|---|
| VAD | Intervallo di indirizzi virtuali | Gestisce che cosa è la regione, Reserve/Commit, la protezione e la corrispondenza di section |
| Tabella delle pagine / PTE | Pagina virtuale | Rappresenta la traduzione attuale verso una pagina fisica, o uno stato non realizzato |
| Database PFN | Pagina fisica | Traccia proprietà, riferimenti e stato di ogni pagina di RAM |
Il VAD tiene informazioni su un intervallo, la PTE su una pagina virtuale, e il database PFN su una pagina fisica. Il gestore del page fault li incrocia per decidere se l’accesso può continuare.
flowchart TB
accTitle: Tre registri da un indirizzo virtuale alla RAM fisica
accDescr: Un indirizzo virtuale è gestito dal VAD a granularità di intervallo e dalla PTE a granularità di pagina virtuale, e il database PFN traccia la pagina fisica bersaglio della traduzione della PTE a granularità di pagina fisica
va["Indirizzo virtuale"] --> vad["VAD(registro di intervallo)"]
va --> pte["PTE(registro di pagina virtuale)"]
vad -.->|Giudicare Reserve/Commit e protezione| pte
pte -->|Traduzione valida| pfn["Database PFN(registro di pagina fisica)"]
pfn --> ram["Pagina di RAM fisica"]
Figura 2: Tre registri a granularità diverse. La gestione del fault incrocia VAD e PTE, poi riflette il risultato sul lato PFN.
I protagonisti di questo articolo sono il VAD e la PTE. Guarderemo il database PFN dal lato della pagina fisica nella parte 2.
3. Reserve, Commit e Touch sono eventi distinti
3.1. Reserve — rivendicare un indirizzo
Per prima cosa, riservate un intervallo contiguo di 256 MiB di indirizzi virtuali.
void* base = VirtualAlloc(
nullptr,
256ull * 1024 * 1024,
MEM_RESERVE,
PAGE_NOACCESS);
Quello che è successo a questo punto è solo che un indirizzo è stato messo da parte nello spazio virtuale del processo così che altre allocazioni non possano usare questo intervallo. MEM_RESERVE non assegna storage fisico in RAM né nel file di paging.1
Poiché un processo a 64 bit ha uno spazio virtuale vastissimo, diventa pratico fare prima Reserve di un grande intervallo e Commit solo delle parti che servono dopo.
3.2. Commit — promettere che si potrà conservare
Poi, fate Commit dell’intervallo riservato.
void* committed = VirtualAlloc(
base,
256ull * 1024 * 1024,
MEM_COMMIT,
PAGE_READWRITE);
In caso di successo, aumenta la quantità promessa che si riflette nel Commit Total del sistema — e di solito nei Private Bytes del processo. Anche così, 256 MiB di pagine fisiche non si allineano tutte insieme. Le pagine ordinarie restano fisicamente non assegnate fino al primo accesso.12
Allora a che serve il Commit? Al fatto che quando il sistema non può assumere la promessa, può restituire un fallimento al momento del Commit, invece che a metà dell’uso della memoria.
3.3. Touch — quando diventa necessaria una pagina fisica
Infine, l’assegnazione seguente scrive per la prima volta nella prima pagina.
static_cast<unsigned char*>(base)[0] = 1;
La CPU prova a tradurre l’indirizzo virtuale in un indirizzo fisico, ma la PTE non ha ancora una traduzione valida verso una pagina fisica. Qui si verifica un page fault.
Il gestore della memoria che riceve il controllo giudica che si tratta di «un primo accesso a una pagina privata committata e scrivibile», ottiene una pagina fisica azzerata, la lega alla PTE e la aggiunge al Working Set. Poi riesegue l’istruzione di scrittura che è fallita.
Dall’app sembra una semplice assegnazione, ma internamente il controllo entra nel kernel a metà dell’assegnazione, viene assegnata una pagina fisica e l’esecuzione torna alla stessa istruzione.
4. Il VAD — il registro di intervalli dello spazio virtuale
VAD sta per Virtual Address Descriptor, e Windows gestisce gli intervalli di indirizzi in uso di un processo come un albero di VAD. Con il comando !vad di WinDbg potete ispezionare i VPN di inizio e fine, il Commit, gli attributi di protezione, Private/Mapped, la Control Area e altro.3
Le informazioni rappresentative che un VAD registra includono le seguenti.
- Inizio e fine dell’intervallo di indirizzi
- Tipo, come Private, Mapped o Image
- Stato Reserve/Commit
- Protezione come lettura, scrittura, esecuzione e copy-on-write
- Corrispondenza a un file o a una section
- Attributi speciali come le guard page
Il motivo per gestire per intervallo è l’efficienza. 256 MiB sono 65.536 pagine a 4 KiB. Piuttosto che costruire subito una struttura di gestione completa per ogni pagina, è meno spreco tenere «questo intervallo contiguo è una prenotazione» in un VAD e materializzare le pagine quando diventano necessarie.
4.1. Trovare un VAD non garantisce il recupero
«Se è in un VAD il fault si risolve; se no, si ottiene una violazione di accesso» è una spiegazione da primissimo livello comoda, ma semplifica troppo. Anche quando si trova un VAD, l’accesso ordinario non può continuare in casi come i seguenti.
- Solo Reserve, e la pagina di destinazione non è committata
PAGE_NOACCESS- Una scrittura su una pagina in sola lettura
- Esecuzione di un’istruzione da una pagina non eseguibile
- Primo tocco di una guard page
- Un tocco fuori dall’intervallo valido di una section
Al contrario, anche se la PTE è invalida, se lo stato software di VAD e PTE mostra un accesso legittimo, si può risolvere come demand-zero, ripristino Transition, page-in o CoW. Più precisamente, la risposta è giudicare insieme VAD, PTE, attributi di protezione e tipo di accesso.
5. Tabelle delle pagine e TLB
Il puntatore che un’app tiene è un indirizzo virtuale. Perché la CPU acceda alla RAM, deve tradurre un numero di pagina virtuale in un numero di pagina fisica. Quella tabella di traduzione gerarchica è la tabella delle pagine, e la voce foglia è la PTE (Page Table Entry).
Una PTE valida tiene concettualmente un PFN, protezione di lettura/scrittura/esecuzione, permesso in modalità utente, Accessed/Dirty e informazioni simili. Il layout effettivo dei bit dipende dalla CPU e dalla versione di Windows.
Percorrere la tabella delle pagine ogni volta sarebbe troppo lento, quindi la CPU mette in cache le traduzioni recenti nel TLB (Translation Lookaside Buffer). La traduzione degli indirizzi procede in quest’ordine.
- Se il TLB ha una traduzione e l’accesso corrisponde a quella protezione, si usa quel risultato.
- Se il TLB non ha una traduzione, la CPU percorre la tabella delle pagine.
- Se c’è una PTE valida e la protezione corrisponde, viene registrata nel TLB e l’esecuzione continua.
- Se non c’è una traduzione valida, o c’è una violazione di protezione, il controllo procede all’ingresso del page fault. Il controllo di protezione si esegue anche quando la traduzione veniva dal TLB.
Come mostra questo flusso, un miss del TLB e un page fault sono cose diverse. Se l’unico problema è che il TLB non ha una traduzione e la PTE è valida, avviene solo un walk della tabella delle pagine. Al contrario, anche se il TLB ha una traduzione, una violazione di protezione come una scrittura su una pagina in sola lettura o l’esecuzione di un’istruzione su una pagina non eseguibile procede all’ingresso del page fault. Per questo una scrittura su una pagina CoW può fare fault anche quando la traduzione è già in cache.
flowchart TB
accTitle: Flusso di traduzione degli indirizzi e ingresso del page fault
accDescr: Anche se il TLB ha una traduzione, un disallineamento di protezione procede all'ingresso del page fault. Se il TLB non ha una traduzione si percorre la tabella delle pagine; una PTE valida che corrisponde anche alla protezione viene registrata nel TLB e l'esecuzione continua, e una traduzione invalida o una violazione di protezione procedono all'ingresso del page fault
access["Accesso in memoria"] --> tlb{"Il TLB ha una traduzione?"}
tlb -->|Sì| perm{"L'accesso corrisponde alla protezione?"}
perm -->|Corrisponde| go["Continuare con quella traduzione"]
perm -->|Violazione di protezione| entry["All'ingresso del page fault"]
tlb -->|No| walk["Walk della tabella delle pagine"]
walk --> valid{"PTE valida e protezione anche corrispondente?"}
valid -->|Sì| register["Registrare nel TLB e continuare(nessun fault)"]
valid -->|Invalida o violazione di protezione| entry
Figura 3: Un miss del TLB si può risolvere con un walk della tabella delle pagine. Il controllo procede a un page fault quando la traduzione è invalida o c’è una violazione di protezione, e una violazione di protezione avviene anche su un hit del TLB.
5.1. Una PTE invalida non è solo un vuoto
Anche una PTE invalida non è vuota. Dallo stato software di una PTE invalida, Windows distingue casi come i seguenti.
- Una pagina demand-zero che non è mai stata materializzata
- Una pagina Transition che resta in RAM
- Una pagina condivisa che si riferisce a una Prototype PTE
- Una pagina privata salvata nel file di paging
- Una violazione di protezione o una regione invalida
Il lavoro della CPU è solo decidere «questa non è una traduzione valida ordinaria» e consegnarla al kernel; da lì il gestore della memoria fornisce il significato.
6. Un page fault dall’inizio alla fine
Seguiamo una prima scrittura su una pagina privata committata in sei fasi.
- La CPU prova a scrivere.
Controlla il TLB e la tabella delle pagine, ma la PTE di destinazione non ha un PFN valido. - La CPU solleva un page fault.
Passa al kernel l’indirizzo virtuale del fault, il tipo lettura/scrittura/esecuzione, utente/kernel, e se il problema è una traduzione mancante o una violazione di protezione. - Il gestore della memoria esamina VAD e PTE.
Decide se la pagina è committata, se la protezione corrisponde e quale tra demand-zero, Transition, condivisa, page-in, CoW o un’eccezione si applica. - Se è demand-zero, si ottiene una pagina fisica azzerata.
Una pagina appena consegnata deve essere zero perché non fuoriescano dati di un altro processo. - Si aggiornano le informazioni di gestione di PTE e PFN.
PFN e protezione si impostano nella PTE, la pagina fisica diventa Active e viene aggiunta al Working Set del processo. - Si riesegue l’istruzione fallita.
Poiché il fault si è risolto normalmente, non viene consegnata un’eccezione in modalità utente e l’app continua l’assegnazione come al solito.
Anche gli eventi ETW di page fault registrano Transition, Demand Zero, Copy-on-Write, Guard Page, Hard Page Fault e Access Violation come tipi distinti.4
Quindi un page fault non è una parola che significa «anomalo» fin dall’inizio. È il punto di ingresso comune per chiedere al sistema operativo di decidere quando la CPU non ha potuto tradurre sul percorso ordinario.
flowchart LR
accTitle: Ramificazione delle risoluzioni di page fault
accDescr: Il gestore della memoria giudica VAD, PTE, attributi di protezione e tipo di accesso, e indirizza a demand-zero, riconnessione di una pagina ancora in RAM, un fault hard da un backing store, copy-on-write, una notifica di guard page o un'eccezione
faultIn["Si verifica un page fault"] --> judge["Giudicare VAD, PTE, protezione, tipo"]
judge -->|Primo accesso| dz["Demand-zero(soft)"]
judge -->|Ancora in RAM| soft["Riconnettere da Standby(soft)"]
judge -->|Serve lettura da disco| hard["Fault hard(I/O disco)"]
judge -->|Scrittura CoW| cow["Copiare e scambiare la PTE"]
judge -->|Guard page| guard["Togliere la guard e notificare"]
judge -->|Irrisolvibile| av["Eccezione(0xC0000005 ecc.)"]
Figura 4: I fault che entrano dallo stesso punto di ingresso si dividono in sei tipi di esito a seconda del giudizio. I dettagli delle guard page sono nella sezione 9.
7. Demand-zero — un fault soft che non legge il disco
Il demand-zero è il fault soft rappresentativo che si verifica quando una pagina privata committata viene toccata per la prima volta. Anche la documentazione Working Set di Microsoft elenca «il processo si riferisce per la prima volta a una pagina virtuale allocata» come esempio di fault soft.5
Il demand-zero ha le seguenti caratteristiche.
- Non c’è bisogno di leggere dati originali dal disco
- Il contenuto iniziale è zero
- Si lega una pagina fisica disponibile
- Aumentano Working Set e il Page Fault Count cumulativo
- Questa gestione da sola non aumenta
Memory\\Pages Input/sec
Per questo un picco di Page Faults/sec subito dopo l’avvio non significa da solo che lo storage sia il collo di bottiglia.
Vale la pena mettere in ordine anche il compromesso dell’allocazione pigra. Se fate Commit di 256 MiB e ne usate davvero solo 8 MiB, lasciare i restanti 248 MiB fuori dalla RAM è ragionevole. In cambio, il primo accesso porta il costo della gestione del fault. Per lavoro sensibile alla latenza esiste un progetto che tocca ogni pagina prima di iniziare per fare prefault, ma è un compromesso che aumenta in anticipo la residenza in RAM.
8. Fault soft e fault hard
8.1. Fault soft
Un fault soft è un fault che si può risolvere senza I/O di lettura verso un backing store. Esempi rappresentativi includono i seguenti.
- Demand-zero
- Riconnettere una pagina che resta su Standby/Transition
- Collegare una pagina condivisa che è nel Working Set di un altro processo
- Collegare una pagina precaricata
- Copy-on-Write la cui pagina originale è residente
Resta un costo di CPU per la transizione al kernel, i lock, gli aggiornamenti PTE/PFN, la coerenza del TLB e simili, ma non c’è attesa di storage.5
8.2. Fault hard
Dall’altra parte, quando la pagina necessaria non è da nessuna parte in RAM e va letta da un backing store, è un fault hard. La fonte di lettura non è solo il file di paging.
- Una pagina privata scritta sul file di paging
- Un file mappato in memoria
- Un’immagine EXE o DLL
- Un file di dati referenziato dalla cache dei file
Gli eventi ETW HardFault includono FileObject, ReadOffset e ByteCount, quindi potete tracciare la fonte di lettura effettiva.6
Pertanto Hard Fault = una lettura di pagefile.sys non è vero.
Quando serve una lettura dal backing store, la richiesta entra nello stack I/O di Windows. Il flusso degli IRP e di emissione/completamento è trattato in «The Depths of Windows I/O (Part 1)», e il giunto con la cache dei file in «The Depths of Windows I/O (Part 4)». Se la pagina è in RAM il gestore della memoria può tornare da solo; se non lo è, emette I/O e attende il thread in fault fino al completamento.
9. Un fault irrisolvibile diventa un’eccezione
Un fault che, dopo aver esaminato VAD e PTE, non si può risolvere come allocazione legittima, page-in o CoW viene consegnato alla modalità utente come eccezione.
Il caso rappresentativo è STATUS_ACCESS_VIOLATION, codice di eccezione 0xC0000005. Si verifica su una lettura, una scrittura o un’esecuzione di un indirizzo invalido; il primo parametro dell’eccezione indica il tipo di accesso e il secondo l’indirizzo che viola.7
I modelli tipici includono i seguenti.
- Leggere NULL, un indirizzo liberato o un indirizzo fuori da un array
- Scrivere su una pagina in sola lettura
- Eseguire un’istruzione da una pagina che DEP/NX ha reso non eseguibile
- Toccare un intervallo riservato che non è committato
PAGE_GUARD ha un significato un po’ diverso. È una notifica una tantum di accesso: solleva STATUS_GUARD_PAGE_VIOLATION e si usa per cose come la crescita dello stack.8
Allocazione pigra normale, page-in, CoW, notifica di guard e una violazione di accesso finale si raccolgono, dal punto di vista della CPU, nello stesso ingresso del page fault. Ciò che decide l’esito è la combinazione di VAD, PTE, attributi di protezione e tipo di accesso.
10. Verificatelo voi
Potete osservare il flusso fin qui sulla vostra macchina. Il seguente programma C++ fa Reserve di 256 MiB, Commit, scrive un byte su ogni pagina e infine Release. Attende Invio a ogni fase così potete osservare i cambiamenti in VMMap e 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");
}
Dal prompt Native Tools x64 di Visual Studio potete compilare con il comando seguente.
cl /std:c++20 /EHsc /W4 memory_fault_demo.cpp
10.1. Cosa guardare in VMMap
VMMap è uno strumento che mostra memoria virtuale riservata, Commit, Working Set, Private e Shareable per tipo.9 I cambiamenti da aspettarsi a ogni fase sono i seguenti.
| Fase | Cambiamento atteso |
|---|---|
| Reserve | Address Space Size aumenta, ma Commit/WS non aumentano della stessa quantità |
| Commit | Private Commit aumenta di circa 256 MiB |
| Touch | Working Set e Private WS aumentano in modo sostanziale, e aumenta anche il Fault Count |
| Release | L’intervallo di destinazione scompare, e Commit e WS scendono |
I numeri effettivi variano con il runtime, i prodotti di sicurezza, la pressione di memoria e quando osservate. Guardate in che direzione si sono mossi i numeri tra le fasi, non se escono esattamente a 256 MiB.
10.2. Separare soft e hard in PerfMon
In PerfMon, mettete i seguenti contatori sulla stessa linea temporale.
Process(<target>)\\Page Faults/secMemory\\Pages Input/secMemory\\Page Reads/secMemory\\Available MBytesProcess(<target>)\\Working Set - PrivateProcess(<target>)\\Private Bytes
Process\\Page Faults/sec include sia i fault soft sia quelli hard. Memory\\Pages Input/sec, invece, è il numero di pagine lette dal disco per risolvere fault hard.10
Nella fase Touch di questo programma, Page Faults/sec dovrebbe saltare mentre Pages Input/sec non dovrebbe salire molto. Le pagine appena committate si materializzano con demand-zero, quindi non c’è bisogno di leggere dati originali dal disco.
Quando più processi condividono lo stesso nome, numeri PerfMon come process#1 possono cambiare tra un riavvio e l’altro. Incrociate con un contatore che mostra il PID, oppure identificate per PID con Process V2 o ETW/WPA.
11. Tre letture sbagliate da evitare nella pratica
11.1. «Il Commit è salito, quindi è una perdita di RAM»
Il Commit è la quantità promessa di contenuto da conservare; le pagine non toccate possono non essere residenti in RAM. Per giudicare una perdita, guardate la serie temporale dei Private Bytes, la scomposizione delle allocazioni e se la cifra torna a una linea di base dopo la fine dell’elaborazione.
11.2. «Page Faults/sec è alto, quindi il disco è lento»
I fault soft non implicano I/O su disco. Separate Page Faults/sec, Pages Input/sec e l’attesa di storage e, se serve, seguite il file di origine e lo stack con gli eventi ETW HardFault.
11.3. «Svuotare il Working Set sistema la perdita»
Togliere una pagina dal Working Set non rilascia Commit né proprietà. La pagina va in Standby o Modified e in seguito rientra con un fault. Sistemare una perdita richiede che l’allocatore esegua VirtualFree, un free dell’heap, la distruzione di oggetti e simili.
Dove va quella pagina fisica tolta è ciò che seguiamo nella parte 2.
12. Sintesi
MEM_RESERVEmette da parte un intervallo di indirizzi virtuali ma non assegna una regione fisica in RAM o nel file di paging.1MEM_COMMITconsuma Commit e garantisce che il contenuto potrà essere conservato in futuro, ma una pagina fisica ordinaria non viene assegnata fino al primo accesso.12- Il VAD è il registro di intervalli, la PTE il registro di pagina virtuale e il database PFN il registro di pagina fisica.
- Un miss del TLB non è un page fault. Se la PTE è valida, lo risolve da solo un walk della tabella delle pagine.
- Demand-zero, ripristino Transition e collegamento di una pagina condivisa sono fault soft risolvibili senza I/O su disco.5
- Se serve una lettura dal file di paging, da una DLL, da un EXE o da un file mappato, è un fault hard.6
- Se l’ispezione di VAD, PTE e attributi di protezione non può risolvere il fault, si ottiene un’eccezione come
0xC0000005.7 - Per un giudizio sulle prestazioni, non guardate
Page Faults/secda solo; guardatePages Input/sec, Available, Working Set, Private Bytes e l’attesa di storage sulla stessa linea temporale.
Continua nella parte 2, «La vita di una pagina fisica: cinque elenchi e la verità sul file di paging».
Dopo che la promessa di Commit è diventata una pagina fisica, seguiamo dove va quella pagina quando esce dal Working Set, dal database PFN e dagli elenchi di pagine.
Articoli correlati
- 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
- Introduzione alla raccolta di crash dump Windows - WER/ProcDump/WinDbg
Aree di consulenza correlate
KomuraSoft LLC si occupa di indagini sull’uso di memoria delle applicazioni Windows, violazioni di accesso, ritardi all’avvio, paging e difetti nel codice nativo.
- Sviluppo di app Windows
- Indagine sui difetti e analisi delle cause
- Riutilizzo di asset esistenti e supporto alla migrazione
- Contattaci
Riferimenti
-
Microsoft Learn, VirtualAlloc function. Sul fatto che
MEM_RESERVEriserva un intervallo di indirizzi virtuali senza assegnare storage fisico; cheMEM_COMMITaddebita una quota di commit contro la memoria e il file di paging del sistema; che il contenuto iniziale di una pagina committata è zero; e che la pagina fisica effettiva non viene assegnata finché non vi si accede. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 -
Microsoft Learn, PERFORMANCE_INFORMATION structure. Sul fatto che
CommitTotalè il numero attuale di pagine Commit del sistema, eCommitLimitè il limite superiore che si può committare senza estendere il file di paging. ↩ ↩2 -
Microsoft Learn, !vad (WinDbg). Sul fatto che
!vadmostra l’albero VAD e consente di ispezionare i VPN di inizio e fine, il Commit, Mapped/Private, gli attributi di protezione, la Control Area e altro. ↩ -
Microsoft Learn, PageFault_TypeGroup1 class. Sul fatto che ETW distingue e registra Transition Fault, Demand Zero Fault, Copy-on-Write, Guard Page Fault, Hard Page Fault e Access Violation. ↩
-
Microsoft Learn, Working Set. Sul fatto che un fault soft si può risolvere senza accedere a un backing store, e si verifica dal Working Set di un altro processo, da Transition, dal demand-zero al primo riferimento e simili. ↩ ↩2 ↩3
-
Microsoft Learn, PageFault_HardFault class. Sul fatto che un evento HardFault include FileObject, ReadOffset, ByteCount, VirtualAddress e un ID di thread, così da poter tracciare la fonte di lettura. ↩ ↩2
-
Microsoft Learn, Access Violation C0000005. Sul fatto che
0xC0000005si verifica su una lettura, una scrittura o un’esecuzione di un indirizzo di memoria invalido, e i parametri dell’eccezione indicano il tipo di accesso e l’indirizzo che viola. ↩ ↩2 -
Microsoft Learn, Creating Guard Pages. Sul fatto che
PAGE_GUARDfornisce una notifica una tantum di accesso alla pagina e sollevaSTATUS_GUARD_PAGE_VIOLATION. ↩ -
Microsoft Learn, VMMap - Sysinternals. Sul fatto che VMMap scompone la memoria virtuale committata per tipo e mostra il Working Set di ogni tipo e una mappa dettagliata degli indirizzi. ↩
-
Microsoft Learn, Performance Analysis of Logs (PAL) Tool. Sul fatto che
Memory\\Pages Input/secè il numero di pagine lette dal disco per risolvere fault di pagina hard. ↩
Articoli correlati
Articoli recenti con gli stessi tag per approfondire argomenti vicini.
Le profondità della memoria di Windows (parte 2) — La vita di una pagina fisica: cinque elenchi e la verità sul file di paging
Questo articolo collega il database PFN, Standby, Modified, la compressione della memoria e il file di paging per spiegare dove va una pa...
Le profondità della memoria Windows (parte 3) — Oggetti sezione e copia in scrittura: cosa sono davvero DLL e mapping di file
Questo articolo collega oggetti sezione, mapping di immagine e dati, cache condivisa e copia in scrittura per spiegare come DLL e memoria...
Le profondità della virtualizzazione Windows (parte 3) — Macchine virtuali che si avviano in pochi secondi: perché WSL2, Windows Sandbox e i container sono così leggeri
Perché WSL2 e Windows Sandbox partono in pochi secondi e sembrano così leggeri? Questo articolo spiega i meccanismi, dalle immagini di ba...
Le profondità della virtualizzazione Windows (parte 2) — Memoria che nemmeno il kernel può vedere: come funzionano VBS, HVCI e Credential Guard
Su un'installazione pulita su hardware compatibile, VBS è abilitata per default e usa l'hypervisor e SLAT per creare un isolamento più fo...
Le profondità della virtualizzazione Windows (parte 1) — Dove gira davvero il vostro Windows? L'hypervisor e le partizioni
Quando si abilita Hyper-V, il Windows host stesso gira sopra l'hypervisor come partizione root. Questo articolo spiega i fondamenti della...
Argomenti correlati
Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.
Argomenti tecnici Windows
Portale su sviluppo Windows, analisi dei problemi e valorizzazione delle risorse esistenti.
Servizi collegati all’argomento
L’articolo è direttamente collegato ai servizi seguenti.
Sviluppo di applicazioni Windows
Applicazioni aziendali, integrazione di dispositivi e strumenti di comunicazione, dai requisiti allo sviluppo.
Domande frequenti
Domande che ricorrono nelle consulenze sull’argomento dell’articolo.
- Passare MEM_COMMIT a VirtualAlloc alloca RAM in quel momento?
- Nella memoria privata ordinaria, il Commit consuma il margine di commit del sistema, ma la pagina fisica corrispondente non viene assegnata fino al primo accesso. Una pagina toccata per la prima volta da una scrittura ottiene la pagina fisica durante la gestione di un fault demand-zero.
- Un page fault significa che c'è un problema o un problema di prestazioni?
- No. I fault soft senza I/O su disco — come demand-zero o il ritorno di una pagina da Standby — sono funzionamento normale. Per un giudizio sulle prestazioni, non guardate solo Page Faults/sec ma anche Pages Input/sec, l'attesa di storage e Available MBytes.
- Un miss del TLB e un page fault sono la stessa cosa?
- Sono diversi. Anche se il TLB non ha una traduzione, se la PTE della tabella delle pagine è valida la CPU si limita a percorrere la tabella e a registrare di nuovo la traduzione. Si procede all'ingresso del page fault quando la PTE è invalida o c'è una violazione di protezione.
- Se l'intervallo di indirizzi è in un VAD, non si può avere una violazione di accesso?
- Non necessariamente. Oltre all'esistenza di un VAD, il gestore della memoria valuta Reserve rispetto a Commit, la protezione di lettura/scrittura/esecuzione, le guard page, lo stato della PTE e altro. Se il fault non si può risolvere, si ottiene un'eccezione come 0xC0000005.
- Un Page Faults/sec alto significa che al sistema manca RAM?
- Da solo non lo si può dire. Page Faults/sec include anche un gran numero di fault soft. Occorre correlarlo sulla stessa linea temporale con Memory\Pages Input/sec, Memory\Page Reads/sec, Available MBytes e il tempo di attesa del disco.
Profilo dell’autore
Pagina di presentazione dell’autore dell’articolo.
Go Komura
Rappresentante di KomuraSoft LLC
Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.