This is an old revision of the document!
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.
Printre câmpurile importante ale acestei structuri amintim:
ZONE_DMA
sau ZONE_NORMAL
sunt tot timpul mapate; paginile din ZONE_HIHGMEM
nu sunt tot timpul mapate; Aceste constante se găsesc în enum zone_typeExistă 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:
vm_area
-urile aferente unui proces sunt înlănțuite printr-o listă
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
.
/usr/src/linux
.
spook.local
(Linux) și chooch.local
Windows.root
cu parola student
; conectare folosind comanda ssh root@spook.local
), student/student (ssh student@spook.local
)ssh Administrator@spook.local
), student/student (ssh student@chooch.local
)root
respectiv Administrator
).ssh linux
ssh windows
/root/share/
de pe mașina virtuală Linux în /home/student/linux-share/
pe sistemul local se face folosind comanda ~/bin/mount-linux
./home/Administrator/share/
(Cygwin) de pe mașina virtuală Windows în /home/student/windows-share/
pe sistemul local se face folosind comanda ~/bin/mount-windows
.
lin/
din arhiva de sarcini a laboratorului.
ssh linux
.wget
pentru descărcarea arhivei de sarcini a laboratorului.
lin/kmmap/
pentru a crea o mapare a memoriei driver-ului în user-space.kmalloc
.TODO 1
.NPAGES+1
pagini folosind kmalloc
.kmalloc_area
.NPAGES+1
este necesară pentru aliniere.kmalloc_ptr
(variabilă globală) obținută în urma apelului kmalloc
trebuie aliniată la adresa unei pagini.kmalloc_area = (char *)((((unsigned long)kmalloc_ptr) + PAGE_SIZE - 1) & PAGE_MASK);
PG_reserved
al fiecărei pagini cu ajutorul funcțiilor SetPageReserved
(în funcția de inițializare a modulului), respectiv ClearPageReserved
(în funcția de ieșire a modulului).SetPageReserved
și ClearPageReserved
.0xaa
,0xbb
,0xcc
,0xdd
.mmap
pentru driver.pfn
– page frame number).PAGE_SHIFT
biți pentru a obține pfn
.lin/test
.make
../mmap-test
.lin/vmmap/
pentru a crea o mapare a memoriei driver-ului în user-space.vmalloc
.TODO 2
.NPAGES
pagini în funcția de inițializare a modulului folosind vmalloc
.vmalloc_area
(variabilă globală).vmalloc
întoarce adrese aliniate la dimensiunea paginii; nu este nevoie de operații suplimentare.PG_reserved
al fiecărei pagini cu ajutorul funcțiilor SetPageReserved
(în funcția de inițializare a modulului), respectiv ClearPageReserved
(în funcția de ieșire a modulului);SetPageReserved
și ClearPageReserved
.0xaa
,0xbb
,0xcc
,0xdd
.mmap
pentru driver.vmalloc
nu sunt fizic contigue.vmalloc_area_ptr
pentru parcurgerea spațiului virtual din kernel space.vmalloc_area
.lin/test
.make
../mmap-test
.TODO 3
.offset
trimis operației de read/write.lin/test
.TASK_3
în mmap_test.c
procfs
procfs
în care să afișați totalul memoriei mapate de procesul apelant.TODO 4
.procfs
(PROC_ENTRY_NAME
, definit în mmap-test.h
) care va afișa totalul memoriei mapate de procesul care a apelat read-ul pe acel fișier.my_proc_file_ops
.PROC_ENTRY_NAME
folosind remove_proc_entry.my_seq_show
va trebui să:vm_start
și vm_end
pentru fiecare structură pentru a deduce dimensiunea totală;printk(”%lx %lx\n, …
), vm_start
și vm_end
pentru fiecare structură vm_area_struct
.lin/test
.TASK_4
în mmap_test.c
.pmap
pentru a vedea mapările testului și a le compara cu cele obținute.