struct page
struct vm_area_struct
struct vm_struct
remap_pfn_range
În kernel-ul Linux există posibilitatea mapării unui spaţiu de adresă kernel într-un spaţiu de adresă utilizator. În acest fel se elimină overhead-ul datorat copierii informaţiei din user-space în kernel-space (şi invers). Acest lucru poate fi realizat prin intermediul unui device driver şi a interfeţei dispozitiv (/dev
) a acestuia în user-space.
Această facilitate poate fi folosită prin completarea operaţiei mmap din structura struct file_operations asociată dispozitivului şi utilizând apelul mmap
din user-space.
Înainte de a discuta despre mecanismul de memory-mapping peste un dispozitiv, vom prezenta câteva din structurile de bază legate de subsistemul de management al memoriei în kernel-ul Linux.
Cateva din structurile importante sunt: struct page, struct vm_area_struct şi struct mm_struct.
Structura struct page este utilizată pentru a încorpora informaţii despre toate paginile fizice din sistem. Kernel-ul deţine o structură struct page
pentru toate paginile din sistem.
Există numeroase funcţii care interacţionează cu această structură:
page frame number
);
Structura struct vm_area_struct deţine informaţii despre o zonă de memorie virtuală contiguă. Zonele de memorie ale unui proces pot fi vizualizate inspectând procfs
:
$ cat /proc/1/maps 08048000-0804f000 r-xp 00000000 03:01 401624 /sbin/init 0804f000-08050000 rw-p 00007000 03:01 401624 /sbin/init 08050000-08071000 rw-p 08050000 00:00 0 40000000-40016000 r-xp 00000000 03:01 369654 /lib/ld-2.3.2.so 40016000-40017000 rw-p 00015000 03:01 369654 /lib/ld-2.3.2.so 40017000-40018000 rw-p 40017000 00:00 0 4001d000-40147000 r-xp 00000000 03:01 371432 /lib/tls/libc-2.3.2.so 40147000-40150000 rw-p 00129000 03:01 371432 /lib/tls/libc-2.3.2.so 40150000-40153000 rw-p 40150000 00:00 0 bffff000-c0000000 rw-p bffff000 00:00 0 ffffe000-fffff000 ---p 00000000 00:00 0
O zonă de memorie este caracterizată printr-o adresă de start, adresă de stop, lungime, permisiuni.
O structură vm_area_struct
este creată la fiecare apel mmap
din user-space. Un driver care are suport pentru operaţia de mmap
trebuie să completeze şi să iniţializeze structura vm_area_struct
asociată. Cele mai importante câmpuri ale acestei structuri sunt:
Structura struct mm_struct înglobează toate zonele de memorie asociate unui proces; folosind câmpul mm
al structurii task_struct se poate obţine structura mm_struct
asociată procesului curent.
Maparea memoriei (memory mapping
) este una dintre cele mai interesante caracteristici ale unui sistem Unix. Din punct de vedere al unui driver, facilitatea de memory-mapping
permite accesul direct al memoriei unui dispozitiv din user-space.
Pentru a asocia unui driver o operaţie mmap
, trebuie utilizat câmpul mmap din structura struct file_operations asociată dispozitivului. Metoda astfel asociată este utilizată în cazul unui apel mmap
din user-space.
Apelul mmap
din user-space realizează o mapare între spaţiul de adresă al unui proces şi un fişier şi are signatura:
void *mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset);
Pentru a mapa memorie între un dispozitiv şi user-space, se va deschide dispozitivul cu un apel open
şi se va pasa apelului mmap
descriptorul acestuia.
Operaţia mmap
din kernel-space se declară ca un membru al structurii struct file_operations
de forma:
int (*mmap) (struct file *filp, struct vm_area_struct *vma)
Câmpul filp
reprezintă pointer-ul la structura struct file creată o dată cu deschiderea dispozitivului din user-space. Câmpul vma
este utilizat pentru a indica spaţiul de adresă virtual folosit pentru maparea memoriei dispozitivului. Un driver va putea aloca memorie (folosind kmalloc
sau vmalloc
), urmând ca un set de procese să poată mapa în spaţiul propriu de adresă adresa alocată în driver.
Pentru maparea unui spaţiu de memorie fizică în spaţiul virtual utilizator, reprezentat de structura struct vm_area_struct
, se folosește apelul remap_pfn_range. Acesta va mapa un spaţiu de adresă fizic contiguu în spaţiul virtual reprezentat de struct vm_area_struct
:
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot);
Parametrii funcţiei sunt:
vma
, spaţiul de memorie virtuală în cadrul căruia se face maparea;addr
, spaţiul virtual de adresă de unde se începe remaparea; se vor construi tabele de pagini pentru spaţiul virtual de adresă cuprins intre addr
şi addr + size
;pfn
, numărul paginii fizice (page frame number
) în care se mapează adresa virtuală; de obicei acesta se obţine prin shifting-ul adresei fizice PAGE_SHIFT biţi la dreaptasize
, dimensiunea (în octeţi) a spaţiului de memorie care va fi remapat;prot
, caracteristicile de protecţie pentru noul spaţiu de adresă.Un exemplu de utilizare a acestei funcţii este:
struct vm_area_struct *vma; unsigned long phys_pgoff; int ret; ret = remap_pfn_range (vma, vma->vm_start, phys_pgoff, vma->vm_end - vma->vm_start, vma->vm_page_prot); if (ret < 0) { printk(KERN_ERR"could not map address area\n"); return -EIO; }
În exemplul de mai sus se realizează o mapare a zonei fizice începând de la numărul de pagină phys_pgoff
în întreg spaţiul virtual de adresă reprezentat de vma
.
Două funcţii care sunt utile sunt cele de translatare a unei adrese virtuale din spaţiul kernel în adrese fizice. Acestea sunt virt_to_phys pentru adrese alocate folosind kmalloc
şi vmalloc_to_pfn pentru adrese alocate folosind vmalloc
.
Astfel, numărul de pagină phys_pgoff
din exemplul de mai sus se va obţine în cazul unei adrese kmalloc_area
alocate folosind kmalloc
astfel:
static char* kmalloc_area; unsigned long phys_pgoff = virt_to_phys((void *)kmalloc_area) >> PAGE_SHIFT;
iar în cazul unei adrese alocate folosind vmalloc
:
static char *vmalloc_area; unsigned long phys_pgoff = vmalloc_to_pfn (vmalloc_area);
De cele mai multe ori, implementarea funcţiei mmap
descrisă mai sus se reduce la un apel al funcţiei remap_pfn_range
. Paginile alocate folosind vmalloc
nu sunt fizic contigue, astfel încât va trebui mapată fiecare pagină în parte (în cazul în care zona de memorie are mai multe pagini), apelând remap_pfn_range
pentru fiecare pagină virtuală.
Înainte de a fi utilizată, unei pagini îi va trebui activat bitul PG_reserved. Acest bit înseamnă că pagina nu poate fi evacuată pe disk (swap
). Activarea se realizează cu ajutorul macroului SetPageReserved. Macrodefiniţia primeşte ca parametru un pointer către structura de pagină, struct page
, care se obţine din adresa virtuală din kernel cu ajutorul funcţiei virt_to_page
, pentru adrese alocate folosind kmalloc
:
#define NPAGES 16 static char* kmalloc_area; int i; for (i = 0; i < NPAGES * PAGE_SIZE; i += PAGE_SIZE) { SetPageReserved(virt_to_page(((unsigned long)kmalloc_area) + i)); }
şi cu ajutorul funcţiei vmalloc_to_page pentru adrese alocate folosind vmalloc
:
#define NPAGES 16 static char* vmalloc_area; int i; for (i = 0; i < NPAGES * PAGE_SIZE; i += PAGE_SIZE) { SetPageReserved(vmalloc_to_page((void *)(((unsigned long)vmalloc_area) + i))); }
Înainte de eliberarea paginii (kfree
/vfree
), bitul de pagină rezervată trebuie dezactivat cu ajutorul macroului ClearPageReserved. Acesta primeşte ca parametru acelaşi pointer către o structura de pagină care a fost dat la SetPageReserved
.