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

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

  1. Parte 1 (questo articolo): indirizzi virtuali e page fault
    Seguiamo quando una regione allocata con VirtualAlloc ottiene RAM fisica.
  2. 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.
  3. 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.

Cosa succede a Reserve, Commit e al primo accessoMEM_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 Set1. MEM_RESERVE2. MEM_COMMIT3. Primo accesso(Touch)Registrare l'intervallo e gli attributi nel VADConsumare il Commit Total(ancora nessuna pagina fisica)Page faultLegare una pagina fisica azzerata alla PTEAggiungerla 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.

Tre registri da un indirizzo virtuale alla RAM fisicaUn 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 fisicaGiudicare Reserve/Commit e protezioneTraduzione validaIndirizzo virtualeVAD(registro di intervallo)PTE(registro di pagina virtuale)Database PFN(registro di pagina fisica)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.

  1. Se il TLB ha una traduzione e l’accesso corrisponde a quella protezione, si usa quel risultato.
  2. Se il TLB non ha una traduzione, la CPU percorre la tabella delle pagine.
  3. Se c’è una PTE valida e la protezione corrisponde, viene registrata nel TLB e l’esecuzione continua.
  4. 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.

Flusso di traduzione degli indirizzi e ingresso del page faultAnche 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 faultCorrispondeViolazione di protezioneNoInvalida o violazione di protezioneAccesso in memoriaIl TLB ha una traduzione?L'accesso corrisponde alla protezione?Continuare con quella traduzioneAll'ingresso del page faultWalk della tabella delle paginePTE valida e protezione anche corrispondente?Registrare nel TLB e continuare(nessun fault)

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.

  1. La CPU prova a scrivere.
    Controlla il TLB e la tabella delle pagine, ma la PTE di destinazione non ha un PFN valido.
  2. 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.
  3. 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.
  4. Se è demand-zero, si ottiene una pagina fisica azzerata.
    Una pagina appena consegnata deve essere zero perché non fuoriescano dati di un altro processo.
  5. 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.
  6. 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.

Ramificazione delle risoluzioni di page faultIl 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'eccezionePrimo accessoAncora in RAMServe lettura da discoScrittura CoWGuard pageIrrisolvibileSi verifica un page faultGiudicare VAD, PTE, protezione, tipoDemand-zero(soft)Riconnettere da Standby(soft)Fault hard(I/O disco)Copiare e scambiare la PTETogliere la guard e notificareEccezione(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/sec
  • Memory\\Pages Input/sec
  • Memory\\Page Reads/sec
  • Memory\\Available MBytes
  • Process(<target>)\\Working Set - Private
  • Process(<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_RESERVE mette da parte un intervallo di indirizzi virtuali ma non assegna una regione fisica in RAM o nel file di paging.1
  • MEM_COMMIT consuma 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/sec da solo; guardate Pages 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

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.

Riferimenti

  1. Microsoft Learn, VirtualAlloc function. Sul fatto che MEM_RESERVE riserva un intervallo di indirizzi virtuali senza assegnare storage fisico; che MEM_COMMIT addebita 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

  2. Microsoft Learn, PERFORMANCE_INFORMATION structure. Sul fatto che CommitTotal è il numero attuale di pagine Commit del sistema, e CommitLimit è il limite superiore che si può committare senza estendere il file di paging.  2

  3. Microsoft Learn, !vad (WinDbg). Sul fatto che !vad mostra 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. 

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

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

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

  7. Microsoft Learn, Access Violation C0000005. Sul fatto che 0xC0000005 si 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

  8. Microsoft Learn, Creating Guard Pages. Sul fatto che PAGE_GUARD fornisce una notifica una tantum di accesso alla pagina e solleva STATUS_GUARD_PAGE_VIOLATION

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

  10. 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 recenti con gli stessi tag per approfondire argomenti vicini.

Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.

L’articolo è direttamente collegato ai servizi seguenti.

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.

Torna al blog