2016-04-15 11 views
13

In superficie, questa sembra essere una domanda stupida. Po 'di pazienza per favore .. :-) Am strutturare questo qs in 2 parti:In che modo gli indirizzi virtuali del kernel vengono convertiti nella RAM fisica?

Parte 1: Capisco pienamente che la piattaforma di RAM è mappato nel segmento kernel; specialmente sui sistemi a 64 bit funzionerà bene. Quindi ogni indirizzo virtuale del kernel è in effetti solo un offset dalla memoria fisica (DRAM).

Inoltre, è la mia comprensione che, come Linux è un moderno sistema operativo di memoria virtuale, (più o meno) tutti indirizzi vengono trattati come gli indirizzi virtuali e devono "andare" via hardware - il TLB/MMU - in fase di esecuzione e quindi ottenere tradotto dal TLB/MMU tramite tabelle di paging del kernel. Di nuovo, facile da capire per i processi in modalità utente.

TUTTAVIA, che dire degli indirizzi virtuali del kernel? Per efficienza, non sarebbe più semplice mapparli direttamente (e una mappatura dell'identità è effettivamente impostata da PAGE_OFFSET in poi). Tuttavia, in fase di runtime, l'indirizzo virtuale del kernel deve passare tramite TLB/MMU e ottenere tradotto a destra ??? È questo il vero caso? O è la traduzione addr virtuale del kernel solo un calcolo di offset ?? (Ma come può essere, dato che lo deve passare a tramite hardware TLB/MMU?). Come semplice esempio, consideriamo:

char *kptr = kmalloc(1024, GFP_KERNEL); 

Ora kptr è un indirizzo virtuale del kernel. Capisco che virt_to_phys() possa eseguire il calcolo dell'offset e restituire l'indirizzo fisico DRAM. Ma, , ecco la domanda reale: non può essere fatto in questo modo tramite software - che sarebbe pateticamente lento! Quindi, tornando al mio punto precedente: avrebbe dovuto essere tradotto via hardware (TLB/MMU). È questo il caso ??

Parte 2: Va bene, lascia per dire questo è il caso, e lo facciamo utilizzare paging nel kernel per fare questo, dobbiamo di impostazione naturalmente tavoli kernel paging; Capisco che sia rootato su swapper_pg_dir.

(Comprendo anche che vmalloc() a differenza di kmalloc() è un caso speciale: è una regione virtuale pura che viene supportata dai frame fisici solo in caso di errore di pagina).

Se (nella parte 1) concludiamo che la traduzione dell'indirizzo virtuale del kernel viene eseguita tramite le tabelle di paging del kernel, allora come esattamente la tabella di paging del kernel (swapper_pg_dir) viene "collegata" o "mappata" a un processo in modalità utente ?? Questo dovrebbe accadere nel codice di commutazione di contesto? Come? Dove?

Es. Su un x86_64, 2 processi A e B sono vivi, 1 cpu. A è in esecuzione, quindi è l'addr più alto canonico 0xFFFF8000 00000000 through 0xFFFFFFFF FFFFFFFF "map" per il segmento del kernel, ed è l'indice di basso canonico addr 0x0 through 0x00007FFF FFFFFFFF mappa al suo spazio utente privato.

Ora, se impostiamo il contesto A-> B, l'area inferiore di canonica di B è univoca. Ma deve "mappare" allo stesso kernel, ovviamente! Come avviene esattamente? Come fare "auto" riferimento alla tabella di paging del kernel quando in modalità kernel? O è una dichiarazione sbagliata?

Grazie per la vostra pazienza, apprezzerebbero davvero una risposta ben ponderata!

risposta

11

Prima un po 'di sfondo.

Questo è un settore dove c'è un sacco di potenziale variazione tra architetture, ma il manifesto originale ha indicato che è principalmente interessato a x86 e ARM, che condividono alcune caratteristiche:

  • nessun segmento hardware o simili partizionamento dello spazio di indirizzamento virtuale (se usato da Linux)
  • hardware tabella delle pagine di cammino
  • pagina formati multipli
  • cache fisicamente targhetta (a LEA t su ARM moderni)

Quindi, se ci limitiamo a questi sistemi, le cose si semplificano.

Una volta abilitata, la MMU non viene normalmente disattivata. Quindi tutti gli indirizzi della CPU sono virtuali e saranno tradotti in indirizzi fisici utilizzando la MMU. La MMU prima cercherà l'indirizzo virtuale nel TLB e solo se non lo trova nel TLB farà riferimento alla tabella di pagina - il TLB è una cache della tabella di pagina - e quindi possiamo ignora il TLB per questa discussione.

La tabella delle pagine descrive l'intero spazio virtuale indirizzo 32 o 64 bit, e include informazioni quali:

  • se l'indirizzo virtuale è valido
  • quale modalità (s) il processore deve essere in per essere valida
  • attributi speciali per le cose come memoria hardware mappato registri
  • e l'indirizzo fisico da usare

Linux divide lo spazio dell'indirizzo virtuale in due: la porzione inferiore è utilizzata per i processi utente e esiste una mappatura fisica virtuale diversa da per ogni processo.La parte superiore viene utilizzata per il kernel, e la mappatura è la stessa anche quando si passa tra diversi processi utente . Ciò semplifica le cose, dato che l'indirizzo nello spazio non ha bisogno di essere modificato quando entra o esce dal kernel, e il kernel può semplicemente dereferenziare i puntatori nello spazio utente per la corrente processo utente. In genere su processori a 32 bit la suddivisione è 3G kernel utente/1G, sebbene questo possa variare. Le pagine per la parte del kernel dello spazio indirizzo saranno contrassegnate come accessibili solo quando il processore è in modalità kernel per impedire che siano accessibili ai processi utente. La porzione dello spazio indirizzo del kernel identificata come mappata su RAM (indirizzi logici del kernel) verrà mappata utilizzando le pagine grandi quando possibile, che potrebbe consentire di ridurre la tabella delle pagine ma, più importante, riduce il numero di mancate risposte TLB.

Quando il kernel si avvia crea una singola tabella pagina di per se stessa (swapper_pg_dir) che ha appena descrive la porzione kernel dello spazio di indirizzamento virtuale e senza mappature per la parte utente dello spazio indirizzo. Quindi, ogni volta che viene creata una procedura utente, verrà generata una nuova tabella per quel processo, la parte che descrive la memoria del kernel sarà la stessa in ciascuna di queste tabelle di pagina. Questo potrebbe essere fatto copiando tutti la quota di competenza di swapper_pg_dir, ma perché le tabelle delle pagine sono normalmente a strutture ad albero, il kernel è spesso in grado di innestare la parte dell'albero che descrive lo spazio di indirizzamento kernel dal swapper_pg_dir in tabelle di pagina per ogni processo utente copiando solo alcune voci nel livello superiore della struttura della tabella di pagina . Oltre ad essere più efficiente nella memoria (e possibilmente nella cache ), rende più semplice mantenere coerenti i mapping. Questo è uno dei motivi per cui la suddivisione tra gli spazi degli indirizzi virtuali del kernel e degli spazi utente può avvenire solo a determinati indirizzi.

Per vedere come questo è fatto per un particolare aspetto dell'architettura nell'implementazione di pgd_alloc(). Per esempio ARM (arch/arm/mm/pgd.c) utilizza:

o x86 (arch/x86/mm/pgtable.c) pgd_alloc() chiama pgd_ctor():

static void pgd_ctor(struct mm_struct *mm, pgd_t *pgd) 
{ 
    /* If the pgd points to a shared pagetable level (either the 
     ptes in non-PAE, or shared PMD in PAE), then just copy the 
     references from swapper_pg_dir. */ 
     ... 
     clone_pgd_range(pgd + KERNEL_PGD_BOUNDARY, 
       swapper_pg_dir + KERNEL_PGD_BOUNDARY, 
       KERNEL_PGD_PTRS); 
    ... 
} 

Quindi, tornando alle domande originali:

Parte 1: Gli indirizzi virtuali del kernel sono realmente tradotti dal TLB/MMU?

Sì.

Parte 2: Come "swapper_pg_dir" collegato "a un processo in modalità utente.

Tutte le tabelle di pagina (sia swapper_pg_dir o quelle per i processi utente) hanno gli stessi mapping per la parte utilizzata per gli indirizzi virtuali del kernel .Quindi, mentre il contesto del kernel cambia tra i processi utente, cambiando la tabella della pagina corrente, i mapping per la parte del kernel dello spazio indirizzo rimangono gli stessi.

2

Lo spazio indirizzo del kernel è mappato a una sezione di ciascun processo, ad esempio sulla mappatura 3: 1 dopo l'indirizzo 0xC0000000. Se il codice utente tenta di accedere a questo spazio indirizzo, genererà un errore di pagina ed è protetto dal kernel. Lo spazio indirizzo del kernel è diviso in 2 parti, lo spazio degli indirizzi logici e lo spazio degli indirizzi virtuali. È definito dalla costante VMALLOC_START. La CPU usa sempre la MMU, nello spazio utente e nello spazio del kernel (non può accendere/spegnere). Lo spazio indirizzo virtuale del kernel viene mappato allo stesso modo della mappatura dello spazio utente. Lo spazio logico degli indirizzi è continuo ed è semplice tradurlo in fisico in modo che possa essere eseguito su richiesta utilizzando l'eccezione di errore MMU. Questo è il kernel che sta tentando di accedere a un indirizzo, la MMU genera un errore, il gestore degli errori mappa la pagina usando le macro __pa, __va e cambia il registro del pc della CPU con le istruzioni precedenti prima che si verificasse l'errore, ora è tutto ok. Questo processo è in realtà dipendente dalla piattaforma e in alcune architetture hardware è mappato allo stesso modo dell'utente (poiché il kernel non usa molta memoria).

+1

Grazie Liran. Per spazio logico di indirizzo presumo tu intenda la regione DRAM con mappatura diretta. Quindi, in effetti, stai dicendo che per la regione logica, il kernel "aggiusta" il phy addr tramite la gestione degli errori di pagina e fa _non_ cercare la tabella di paging del kernel (il che implica che non dobbiamo cambiare il valore del registro CR3/TTBRn)? E solo per gli errori vmalloc vengono effettivamente utilizzate le tabelle di paging del kernel? – kaiwan

+0

La mia comprensione è che abbiamo solo (pagina) errori nello spazio del kernel per le correzioni vmalloc ed eccezione (come copy_to | from_user). Qualsiasi altro errore in modalità kernel è un bug e si verifica un errore Oops .. – kaiwan

+0

Sì. Esattamente, è così che funziona nella maggior parte delle architetture. (Linux supporta 21 hardware arch e il suo specifico arco quindi forse in uno è fatto in modo diverso) –

Problemi correlati