View Single Post
Old 07-01-2005, 15:56   #11
ilsensine
Senior Member
 
L'Avatar di ilsensine
 
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
Le tecniche di allocazione della memoria

Negli esempi precedenti ho utilizzato le funzioni kmalloc (o kzalloc)/kfree e vmalloc/vfree senza dare particolari spiegazioni. E' ora di ritornare sull'argomento.

Consiglio di leggere questo articolo per avere una idea più precisa di come funzionano le cose:
http://www.csn.ul.ie/~mel/projects/v...ml/understand/
Va ben oltre quello che può interessarvi, ma vale la pena leggerlo. nb Non tratta le tabelle di pagina di quarto livello (page upper directory, PUD), introdotte recentemente.

- Struttura della memory map del kernel:
Il kernel risiede nell'ultimo GB di memoria, da 0xc0000000 (PAGE_OFFSET) in poi. I 3 GB precedenti sono riservati per lo user space. E' possibile effettuare mappature diverse (io ad es. ho usato 2.5/1.5), oppure la totale separazione 4/4, ma il concetto è lo stesso.
Questo è il layout di indirizzi, visto dal kernel linux:

http://spazioinwind.libero.it/ilsens...ages/img18.png

La RAM fisica è divisa in tre zone: zona DMA "legacy", corrispondente ai primi 16MB di indirizzi fisici (utilizzata dai vecchi dispositivi ISA che potevano fare DMA solo in questi indirizzi); la zona NORMAL (utilizzabile anche per DMA a 32 bit), e la zona HIGHMEM. Le prime due zone sono permanentemente mappate in memoria: ovvero il kernel ha già indirizzi virtuali che corrispondono agli indirizzi fisici di queste regioni. La zona HIGHMEM invece è mappata, dinamicamente, solo quando necessario.
Per ogni pagina di memoria il kernel tiene una piccola struttura (struct page; v. linux/mm.h) ordinate nell'array mem_map. La funzione page_address(page) restituisce l'indirizzo di una pagina, se mappata (come accade per le zone NORMAL o DMA).
Le pagine sono gestite, al livello più basso, dal buddy allocator. Questo signore gestisce la memoria in pagine (la dimensione di una pagina è 4kb su x86), raggruppandole in blocchi contigui composti da un numero potenza di due di pagine.

Per richiedere delle pagine al buddy allocator si possono usare le funzioni alloc_pages/free_pages (linux/gfp.h):
Codice:
struct page *alloc_pages(unsigned int gfp_mask, unsigned int order)
dove "order" è l'ordine di pagine da allocare (significato: "voglio allocare 2^order di pagine" -- il buddy allocator lavora in potenze di 2), e gfp_mask contiene indicazioni da dove (preferibilmente) prendere le pagine, se l'allocazione deve essere atomica o se possiamo "aspettare un pò". Il valore restituito è la prima pagina del gruppo allocato. Se le pagine NON appartengono alla zona HIGHMEM, l'indirizzo virtuale corrispondente può essere ottenuto chiamando la funzione page_address(page). Per le pagine HIGHMEM questo non è possibile, in quanto non possiedono mappatura fissa (questo piccolo dettaglio è tra i motivi per cui è molto complicato per un sistema a 32 bit gestire decine di GB di memoria). Per gli interessati che cercano di accedere a pagine highmem, cercate le funzioni k(un)map[_atomic]. Non abusarne.
Nota importante: la memoria allocata da alloc_pages è memoria fisicamente continua. L'ordine massimo è MAX_ORDER (da 10 a 11 su x86, a seconda del kernel), che ci consente di allocare fino a PAGE_SIZE*2^MAX_ORDER byte di memoria fisicamente contigua. Per dimensioni superiori, occorrono altre tecniche (ovvero: è un casino). La memoria fisicamente contigua è importante per operazioni DMA.

Sotto il buddy allocator siede lo slab allocator (v. linux/slab.h e /proc/slabinfo), il cui scopo è consentire e gestire l'allocazione di oggetti più piccoli di una pagina. E' difficile che dobbiate usarlo direttamente.
Allo slab allocator accede la funzione kmalloc, la tecnica preferita di allocazione dentro al kernel: questa funzione consente di allocare velocemente piccole quantità di memoria (tipicamente fino a 128 KB) dalle zone NORMAL o DMA. Si tratta di memoria contigua fisicamente, volendo la si può usare per operazioni DMA. La sintassi è
kmalloc(size, GFP_KERNEL) per le allocazioni normali e
kmalloc(size, GFP_ATOMIC) per le operazioni atomiche (ad es. dentro un irq). Aggiungete GFP_DMA se vi serve memoria dalla zona DMA riservata alle schede legacy.
La variante kzalloc è equivalente a kmalloc, ma ritorna memoria già inizializzata a 0.
*Importante*: E' perfettamente normale (non è necessariamente un errore!) che una allocazione atomica possa fallire. Se usare GFP_ATOMIC, siate preparati. O meglio, cercate di evitarle se non è indispensabile.

Altra tecnica di allocazione è la funzione vmalloc/vfree. Caratteristiche:
- L'allocazione avviene solo per multipli di pagina (chiedete 1 byte, ne sprecate 4095)
- L'allocazione proviene da tutte le zone: non è importante se la memoria è già mappata o meno, viene comunque creata una nuova mappatura virtuale.
- La memoria restituita è virtualmente contigua, ma non fisicamente
- Può bloccare: non usare in contesti atomici!
- Può allocare regioni enormi, da VMALLOC_START a VMALLOC_END. Non esagerare con l'uso: appesantisce le tabelle di pagine.
- E' la funzione più simile a malloc: ciò nonostante, va usata solo quando necessario (nell'esempio sulla mmap ne ho in effetti "abusato").

Direi che non è il caso di addentrarsi oltre. Gli interessati possono leggere il link che ho consigliato, dove viene spiegato anche il significato delle regioni kmap e fixed map e tante altre cose.
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al
andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12

Ultima modifica di ilsensine : 23-02-2006 alle 15:55.
ilsensine è offline