Laborator 11 - Maparea memoriei

Obiectivele laboratorului

  • familiarizarea cu mecanismele de mapare a spațiilor de adresă
  • prezentarea structurilor cele mai importante din Linux, legate de memory management

Cuvinte cheie

  • spațiu de adresă
  • mmap
  • struct page
  • struct vm_area_struct
  • struct vm_struct
  • remap_pfn_range
  • SetPageReserved/ClearPageReserved

Materiale ajutătoare

Maparea memoriei în Linux

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

Structuri de lucru cu memoria

Î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 ''page''

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ă:

  • virt_to_page întoarce pagina asociată unei adrese virtuale;
  • pfn_to_page întoarce pagina asociată pentru un număr de pagină (page frame number);
  • page_address întoarce adresa virtuală a paginii transmise ca parametru.

Structura ''vm_area_struct''

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_start, vm_end, reprezintă începutul, respectiv, sfârşitul zonei de memorie (aceste câmpuri apar şi in /proc/*/maps);
  • vm_file, pointer-ul la structura de fişier asociată (dacă există);
  • vm_pgoff, offset-ul zonei în cadrul fişierului;
  • vm_flags, un set de indicatori;
  • vm_ops, un set de funcţii de lucru asupra acestei zone.
  • vm_next, vm_prev, vm_area-urile aferente unui proces sunt înlănțuite printr-o listă

Structura ''mm_struct''

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

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.

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.

Kernel-space

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.

''remap_pfn_range''

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 dreapta
  • size, 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ă.

''SetPageReserved''/''ClearPageReserved''

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

Resurse utile

Linux

so2/laboratoare/lab11.txt · Last modified: 2017/05/07 19:19 by octavian.purdila
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0