This shows you the differences between two versions of the page.
so2:laboratoare:lab11 [2014/05/02 10:48] daniel.baluta [Laborator 12 - Maparea memoriei] |
so2:laboratoare:lab11 [2017/05/07 19:19] (current) octavian.purdila update lxr links to use 4.9 |
||
---|---|---|---|
Line 18: | Line 18: | ||
===== Materiale ajutătoare ===== | ===== Materiale ajutătoare ===== | ||
- | *[[http://elf.cs.pub.ro/so2/res/laboratoare/lab12-slides.pdf|Slide-uri de suport pentru laborator]] | + | *[[http://elf.cs.pub.ro/so2/res/laboratoare/lab11-slides.pdf|Slide-uri de suport pentru laborator]] |
*[[http://elf.cs.pub.ro/so2/res/extra/so2_reference.pdf|SO2 Reference Card]] | *[[http://elf.cs.pub.ro/so2/res/extra/so2_reference.pdf|SO2 Reference Card]] | ||
Line 25: | Line 25: | ||
Î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. | Î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 [[http://lxr.linux.no/#linux+v2.6.35/include/linux/fs.h#L1497|mmap]] din structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/fs.h#L1485|struct file_operations]] asociată dispozitivului şi utilizând apelul ''mmap'' din user-space. | + | Această facilitate poate fi folosită prin completarea operaţiei [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/fs.h#L1708|mmap]] din structura [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/fs.h#L1696|struct file_operations]] asociată dispozitivului şi utilizând apelul ''mmap'' din user-space. |
==== Structuri de lucru cu memoria ==== | ==== Structuri de lucru cu memoria ==== | ||
Line 31: | Line 31: | ||
Î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. | Î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: [[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L34|struct page]], [[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L130|struct vm_area_struct]] şi [[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L222|struct mm_struct]]. | + | Cateva din structurile importante sunt: [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L45|struct page]], [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L300|struct vm_area_struct]] şi [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L396|struct mm_struct]]. |
==== Structura ''page'' ==== | ==== Structura ''page'' ==== | ||
- | Structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L34|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. | + | Structura [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L45|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: | + | |
- | *[[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L37|_count]], contorul de utilizare al paginii; când acesta devine 0 pagina este adăugată la lista de pagini libere; | + | |
- | *[[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L89|virtual]], adresa virtuală în spaţiul kernel asociată acestei pagini fizice; paginile din ''ZONE_DMA'' sau ''ZONE_NORMAL'' sunt tot timpul mapate; paginile din ''ZONE_HIHGMEM'' nu sunt tot timpul mapate; Aceste constante se găsesc în [[http://lxr.linux.no/#linux+v2.6.35/include/linux/mmzone.h#L191|enum zone_type]] | + | |
- | *[[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L35|flags]], un set de flag-uri care descriu atributele paginii. | + | |
Există numeroase funcţii care interacţionează cu această structură: | Există numeroase funcţii care interacţionează cu această structură: | ||
- | *[[http://lxr.linux.no/#linux+v2.6.35/arch/x86/include/asm/page.h#L51|virt_to_page]] întoarce pagina asociată unei adrese virtuale; | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/arch/x86/include/asm/page.h#L68|virt_to_page]] întoarce pagina asociată unei adrese virtuale; |
- | *[[http://lxr.linux.no/#linux+v2.6.35/include/asm-generic/memory_model.h#L73|pfn_to_page]] întoarce pagina asociată pentru un număr de pagină (''page frame number''); | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/asm-generic/memory_model.h#L81|pfn_to_page]] întoarce pagina asociată pentru un număr de pagină (''page frame number''); |
- | *[[http://lxr.linux.no/#linux+v2.6.35/mm/highmem.c#L331|page_address]] întoarce adresa virtuală a paginii transmise ca parametru. | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm.h#L1013|page_address]] întoarce adresa virtuală a paginii transmise ca parametru. |
==== Structura ''vm_area_struct'' ==== | ==== Structura ''vm_area_struct'' ==== | ||
- | Structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L130|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'': | + | Structura [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L300|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'': |
<code bash> | <code bash> | ||
Line 69: | Line 64: | ||
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: | 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: | ||
- | *[[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L132|vm_start]], [[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L133|vm_end]], reprezintă începutul, respectiv, sfârşitul zonei de memorie (aceste câmpuri apar şi in ''/proc/*/maps''); | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L303|vm_start]], [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L304|vm_end]], reprezintă începutul, respectiv, sfârşitul zonei de memorie (aceste câmpuri apar şi in ''/proc/*/maps''); |
- | *[[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L176|vm_file]], pointer-ul la structura de fişier asociată (dacă există); | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L351|vm_file]], pointer-ul la structura de fişier asociată (dacă există); |
- | *[[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L174|vm_pgoff]], offset-ul zonei în cadrul fişierului; | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L349|vm_pgoff]], offset-ul zonei în cadrul fişierului; |
- | *[[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L140|vm_flags]], un set de indicatori; | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L324|vm_flags]], un set de indicatori; |
- | *[[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L171|vm_ops]], un set de funcţii de lucru asupra acestei zone. | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L346|vm_ops]], un set de funcţii de lucru asupra acestei zone. |
- | *[[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L137|vm_next, vm_prev]], ''vm_area''-urile aferente unui proces sunt înlănțuite printr-o listă | + | *[[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L308|vm_next]], [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L308|vm_prev]], ''vm_area''-urile aferente unui proces sunt înlănțuite printr-o listă |
==== Structura ''mm_struct'' ==== | ==== Structura ''mm_struct'' ==== | ||
- | Structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/mm_types.h#L222|struct mm_struct]] înglobează toate zonele de memorie asociate unui proces; folosind câmpul ''mm'' al structurii [[http://lxr.linux.no/#linux+v2.6.35/include/linux/sched.h#L1173|task_struct]] se poate obţine structura ''mm_struct'' asociată procesului curent. | + | Structura [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm_types.h#L396|struct mm_struct]] înglobează toate zonele de memorie asociate unui proces; folosind câmpul ''mm'' al structurii [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/sched.h#L1475|task_struct]] se poate obţine structura ''mm_struct'' asociată procesului curent. |
==== Maparea memoriei ==== | ==== Maparea memoriei ==== | ||
Line 84: | Line 79: | ||
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. | 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 [[http://lxr.linux.no/#linux+v2.6.35/include/linux/fs.h#L1497|mmap]] din structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/fs.h#L1485|struct file_operations]] asociată dispozitivului. Metoda astfel asociată este utilizată în cazul unui apel ''mmap'' din user-space. | + | Pentru a asocia unui driver o operaţie ''mmap'', trebuie utilizat câmpul [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/fs.h#L1708|mmap]] din structura [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/fs.h#L1696|struct file_operations]] asociată dispozitivului. Metoda astfel asociată este utilizată în cazul unui apel ''mmap'' din user-space. |
==== User-space ==== | ==== User-space ==== | ||
Line 103: | Line 98: | ||
</code> | </code> | ||
- | Câmpul ''filp'' reprezintă pointer-ul la structura [[http://lxr.linux.no/#linux+v2.6.35/include/linux/fs.h#L914| 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. | + | Câmpul ''filp'' reprezintă pointer-ul la structura [[http://lxr.free-electrons.com/source/include/linux/fs.h#L866|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'' ===== | ===== ''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 [[http://lxr.linux.no/#linux+v2.6.35/mm/memory.c#L1851| remap_pfn_range]]. Acesta va mapa un spaţiu de adresă fizic contiguu în spaţiul virtual reprezentat de ''struct vm_area_struct'': | + | Pentru maparea unui spaţiu de memorie fizică în spaţiul virtual utilizator, reprezentat de structura ''struct vm_area_struct'', se folosește apelul [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm.h#L2193|remap_pfn_range]]. Acesta va mapa un spaţiu de adresă fizic contiguu în spaţiul virtual reprezentat de ''struct vm_area_struct'': |
<code C> | <code C> | ||
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, | int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, | ||
Line 116: | Line 111: | ||
*''vma'', spaţiul de memorie virtuală în cadrul căruia se face maparea; | *''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''; | *''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 [[http://lxr.linux.no/#linux+v2.6.35/arch/x86/include/asm/page_types.h#L7| PAGE_SHIFT]] biţi la dreapta | + | *''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 [[http://elixir.free-electrons.com/linux/v4.9/source/arch/x86/include/asm/page_types.h#L8|PAGE_SHIFT]] biţi la dreapta |
*''size'', dimensiunea (în octeţi) a spaţiului de memorie care va fi remapat; | *''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ă. | *''prot'', caracteristicile de protecţie pentru noul spaţiu de adresă. | ||
Line 136: | Line 131: | ||
Î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''. | Î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 [[http://lxr.linux.no/#linux+v2.6.35/arch/x86/include/asm/io.h#L118| virt_to_phys]] pentru adrese alocate folosind ''kmalloc'' şi [[http://lxr.linux.no/#linux+v2.6.35/mm/vmalloc.c#L237| vmalloc_to_pfn]] pentru adrese alocate folosind ''vmalloc''. | + | Două funcţii care sunt utile sunt cele de translatare a unei adrese virtuale din spaţiul kernel în adrese fizice. Acestea sunt [[http://elixir.free-electrons.com/linux/v4.9/source/arch/x86/include/asm/io.h#L118| virt_to_phys]] pentru adrese alocate folosind ''kmalloc'' şi [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/mm.h#L473|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: | 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: | ||
Line 155: | Line 150: | ||
===== ''SetPageReserved''/''ClearPageReserved'' ===== | ===== ''SetPageReserved''/''ClearPageReserved'' ===== | ||
- | Înainte de a fi utilizată, unei pagini îi va trebui activat bitul [[http://lxr.linux.no/#linux+v2.6.35/include/linux/page-flags.h#L86|PG_reserved]]. Acest bit înseamnă că pagina nu poate fi evacuată pe disk (''swap''). Activarea se realizează cu ajutorul macroului [[http://lxr.linux.no/#linux+v2.6.35/include/linux/page-flags.h#L144|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'': | + | Înainte de a fi utilizată, unei pagini îi va trebui activat bitul [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/page-flags.h#L85|PG_reserved]]. Acest bit înseamnă că pagina nu poate fi evacuată pe disk (''swap''). Activarea se realizează cu ajutorul macroului [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/page-flags.h#L194|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'': |
<code C> | <code C> | ||
#define NPAGES 16 | #define NPAGES 16 | ||
Line 166: | Line 161: | ||
} | } | ||
</code> | </code> | ||
- | şi cu ajutorul funcţiei [[http://lxr.linux.no/#linux+v2.6.35/mm/vmalloc.c#L206|vmalloc_to_page]] pentru adrese alocate folosind ''vmalloc'': | + | şi cu ajutorul funcţiei [[http://elixir.free-electrons.com/linux/v4.9/source/mm/vmalloc.c#L235|vmalloc_to_page]] pentru adrese alocate folosind ''vmalloc'': |
<code C> | <code C> | ||
#define NPAGES 16 | #define NPAGES 16 | ||
Line 178: | Line 173: | ||
</code> | </code> | ||
- | Înainte de eliberarea paginii (''kfree''/''vfree''), bitul de pagină rezervată trebuie dezactivat cu ajutorul macroului [[http://lxr.linux.no/#linux+v2.6.35/include/linux/page-flags.h#L148|ClearPageReserved]]. Acesta primeşte ca parametru acelaşi pointer către o structura de pagină care a fost dat la ''SetPageReserved''. | + | Înainte de eliberarea paginii (''kfree''/''vfree''), bitul de pagină rezervată trebuie dezactivat cu ajutorul macroului [[http://lxr.free-electrons.com/source/include/linux/page-flags.h?v=4.9#L185|ClearPageReserved]]. Acesta primeşte ca parametru acelaşi pointer către o structura de pagină care a fost dat la ''SetPageReserved''. |
- | ===== Exerciții ===== | ||
- | <note important> | ||
- | * Toate exercițiile de Linux vor fi realizate în cadrul **mașinii virtuale** de Linux (dacă nu este specificat altfel), iar cele de Windows în cadrul **mașinii virtuale** de Windows. | ||
- | * Recomandăm să porniți VMware într-un workspace separat. | ||
- | * Excepție face folosirea LXR, puteți folosi mașina fizică pentru căutări. În cazul folosirii cscope, folosiți mașina virtuală, mai precis directorul ''/usr/src/linux''. | ||
- | </note> | ||
- | |||
- | * Mașinile virtuale pot fi accesate, respectiv, prin Multicast DNS, folosind numele ''spook.local'' (Linux) și ''chooch.local'' Windows. | ||
- | * Pentru accesarea mașinilor virtuale puteți folosi SSH. Conturile mașinilor virtuale sunt: | ||
- | * Linux: root/student (adică utilizatorul ''root'' cu parola ''student''; conectare folosind comanda ''ssh root@spook.local''), student/student (''ssh student@spook.local'') | ||
- | * Windows: Administrator/student (''ssh Administrator@spook.local''), student/student (''ssh student@chooch.local'') | ||
- | * Fiind vorba de kernel programming/driver development veți folosi preponderent conturile privilegiate (''root'' respectiv ''Administrator''). | ||
- | * Există create două alias-uri SSH pentru conectare rapidă la mașinile virtuale: | ||
- | * Linux (root): ''ssh linux'' | ||
- | * Windows (Administrator): ''ssh windows'' | ||
- | * Pentru accesarea locală a sistemului de fișiere de pe mașinile virtuale puteți folosi Samba/CIFS, prin intermediul a două scripturi: | ||
- | * Montarea ''/root/share/'' de pe mașina virtuală Linux în ''/home/student/linux-share/'' pe sistemul local se face folosind comanda ''~/bin/mount-linux''. | ||
- | * Montarea ''/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''. | ||
- | |||
- | <note warning> | ||
- | Înainte de a rezolva un exercițiu, citiți cu **atenție** toate bullet-urile acestuia. | ||
- | </note> | ||
- | |||
- | ==== Linux ==== | ||
- | |||
- | * Folosiți directorul ''lin/'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab12-tasks.zip | arhiva de sarcini]] a laboratorului. | ||
- | * Punctaj total: **7 puncte** | ||
- | |||
- | <note important> | ||
- | * Pe mașina virtuală de Linux recomandăm: | ||
- | * Conectarea prin SSH de pe sistemul fizic folosind comanda ''ssh linux''. | ||
- | * Folosirea ''wget'' pentru descărcarea [[http://elf.cs.pub.ro/so2/res/laboratoare/lab12-tasks.zip | arhivei de sarcini]] a laboratorului. | ||
- | * În cazul apariției unui oops, reporniți mașina virtuală (sau faceți revert la un snapshot realizat anterior de voi). | ||
- | </note> | ||
- | |||
- | - (**2 puncte**) Mapare de memorie fizic contiguă în user space | ||
- | * Utilizați scheletul de modul de kernel din directorul ''lin/kmmap/'' pentru a crea o mapare a memoriei driver-ului în user-space. | ||
- | * Memoria driver-ului este alocată folosind ''kmalloc''. | ||
- | * Completați zonele marcate cu ''TODO 1''. | ||
- | * Parcurgeți secțiunea [[#Kernel-space|Maparea memoriei. Kernel-space]]. | ||
- | * Alocați, în funcția de inițializare a modulului, o zonă de memorie de ''NPAGES+1'' pagini folosind ''kmalloc''. | ||
- | * O pagină în kernel are dimensiunea [[http://lxr.linux.no/linux+*/arch/x86/include/asm/page_types.h#L8|PAGE_SIZE]]. | ||
- | * Zona alocată din spațiul kernel este indicată de ''kmalloc_area''. | ||
- | * Dimensiunea ''NPAGES+1'' este necesară pentru aliniere. | ||
- | * Adresa ''kmalloc_ptr'' (variabilă globală) obținută în urma apelului ''kmalloc'' trebuie aliniată la adresa unei pagini. | ||
- | * Pentru aceasta, va trebui să alocați o pagină în plus față de numărul de pagini necesare și să folosiți formula:<code> | ||
- | kmalloc_area = (char *)((((unsigned long)kmalloc_ptr) + PAGE_SIZE - 1) & PAGE_MASK); | ||
- | </code> | ||
- | * Activați/dezactivați bitul ''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). | ||
- | * Folosiți [[http://lxr.free-electrons.com/source/tools/virtio/linux/virtio.h?v=3.7#L32|virt_to_page]] pentru a traduce paginile virtuale în pagini fizice folosite de funcțiile ''SetPageReserved'' și ''ClearPageReserved''. | ||
- | * Pentru verificare (folosind testul indicat mai jos) completați primii 4 octeți din fiecare pagină alocată cu valorile, respectiv, ''0xaa'',''0xbb'',''0xcc'',''0xdd''. | ||
- | * Implementați funcția ''mmap'' pentru driver. | ||
- | * Pentru mapare folosiți funcția [[http://lxr.free-electrons.com/source/mm/memory.c?v=3.7#L2282|remap_pfn_range]]. | ||
- | * Al treilea argument primit de [[http://lxr.free-electrons.com/source/mm/memory.c?v=3.7#L2282|remap_pfn_range]] este un număr de pagină fizică (''pfn'' -- //page frame number//). | ||
- | * Pentru conversia din adresă kernel virtuală în adresă fizică utilizați [[http://lxr.free-electrons.com/source/arch/x86/include/asm/io.h?v=3.7#L98|virt_to_phys]]. | ||
- | * Shiftați rezultatul cu ''PAGE_SHIFT'' biți pentru a obține ''pfn''. | ||
- | * Încărcați modulul în kernel. | ||
- | * Pentru testare utilizați testul din directorul ''lin/test''. | ||
- | * Compilați testul folosind comanda ''make''. | ||
- | * Rulați testul folosind comanda ''./mmap-test''. | ||
- | * Descărcați modulul din kernel. | ||
- | - (**2 puncte**) Mapare de memorie virtual contiguă în user space | ||
- | * Utilizați scheletul de modul de kernel din directorul ''lin/vmmap/'' pentru a crea o mapare a memoriei driver-ului în user-space. | ||
- | * Memoria driver-ului este alocată folosind ''vmalloc''. | ||
- | * Completați zonele marcate cu ''TODO 2''. | ||
- | * Parcurgeți secțiunea [[#Kernel-space|Maparea memoriei. Kernel-space]] din laborator. | ||
- | * Alocați o zonă de memorie de ''NPAGES'' pagini în funcția de inițializare a modulului folosind ''vmalloc''. | ||
- | * Zona alocată din spațiul kernel este indicată de ''vmalloc_area'' (variabilă globală). | ||
- | * Alocarea cu ''vmalloc'' întoarce adrese aliniate la dimensiunea paginii; nu este nevoie de operații suplimentare. | ||
- | * Activați/dezactivați bitul ''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); | ||
- | * Folosiți [[http://lxr.free-electrons.com/source/mm/vmalloc.c?v=3.7#L202|vmalloc_to_page]] pentru a traduce paginile virtuale în pagini fizice folosite de funcțiile ''SetPageReserved'' și ''ClearPageReserved''. | ||
- | * Pentru verificare folosind testul, completați primii 4 octeți din fiecare pagină alocată cu următoarele valori: ''0xaa'',''0xbb'',''0xcc'',''0xdd''. | ||
- | * Implementați funcția ''mmap'' pentru driver. | ||
- | * Pentru conversia din adresă kernel virtuală în adresă fizică utilizați [[http://lxr.free-electrons.com/source/mm/vmalloc.c?v=3.7#L236|vmalloc_to_pfn]]. | ||
- | * [[http://lxr.free-electrons.com/source/mm/vmalloc.c?v=3.7#L236|vmalloc_to_pfn]] întoarce un index de pagină (//page frame number//), astfel că **nu** este nevoie de shift la dreapta. | ||
- | * Paginile alocate cu ''vmalloc'' nu sunt fizic contigue. | ||
- | * Apelați [[|remap_pfn_range]] pentru **fiecare** pagină virtuală. | ||
- | * Folosiți ''vmalloc_area_ptr'' pentru parcurgerea spațiului virtual din kernel space. | ||
- | * Nu modificați variabila globală ''vmalloc_area''. | ||
- | * Argumentul pentru [[http://lxr.free-electrons.com/source/mm/vmalloc.c?v=3.7#L236|vmalloc_to_pfn]] este o adresă virtuală din kernel space. | ||
- | * Al doilea argument pentru [[http://lxr.free-electrons.com/source/mm/memory.c?v=3.7#L2282|remap_pfn_range]] este o adresă virtuală din cadrul unui VMA. | ||
- | * Al treilea argument pentru [[http://lxr.free-electrons.com/source/mm/memory.c?v=3.7#L2282|remap_pfn_range]] este pfn-ul paginii virtuale din kernel-space care se dorește remapată. | ||
- | * Încărcați modulul în kernel. | ||
- | * Pentru testare utilizați testul din directorul ''lin/test''. | ||
- | * Compilați testul folosind comanda ''make''. | ||
- | * Rulați testul folosind comanda ''./mmap-test''. | ||
- | * Descărcați modulul din kernel. | ||
- | - (**1 punct**) Operații de read/write în memoria mapată | ||
- | * Modificați **unul dintre** modulele anterioare astfel încât să permiteți operații read/write pe dispozitiv. | ||
- | * Operațiile de citire și scriere vor acționa chiar asupra zonei de memorie alocate. | ||
- | * Completați zonele marcate cu ''TODO 3''. | ||
- | * Revedeți [[so2:laboratoare:lab04 | Laboratorul 4]] | ||
- | * **Ignorați** parametrul ''offset'' trimis operației de read/write. | ||
- | * Pentru testare utilizați testul din directorul ''lin/test''. | ||
- | * Definiți macroul ''TASK_3'' în ''mmap_test.c'' | ||
- | - (**2 puncte**) Afișare memorie mapată în ''procfs'' | ||
- | * Folosind **unul dintre** modulele anterioare, creați un fișier ''procfs'' în care să afișați totalul memoriei mapate de procesul apelant. | ||
- | * Completați zonele marcate cu ''TODO 4''. | ||
- | * Creați o intrare nouă în ''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. | ||
- | * Folosiți apelul [[http://lxr.free-electrons.com/source/fs/proc/generic.c?v=3.7#L701|create_proc_entry]]. | ||
- | * Operațiile sunt descrise în structura ''my_proc_file_ops''. | ||
- | * În funcția de ieșire a modulului ștergeți intrarea ''PROC_ENTRY_NAME'' folosind [[http://lxr.free-electrons.com/source/fs/proc/generic.c?v=3.7#L780|remove_proc_entry]]. | ||
- | * O utilizare (complexă) și descriere a interfeței [[http://lxr.free-electrons.com/source/include/linux/seq_file.h?v=3.7#L18|seq_file]] se găsește aici [[http://tldp.org/LDP/lkmpg/2.6/html/x861.html | acest exemplu]]. | ||
- | * Pentru acest exercițiu este suficientă doar o simplă utilizare a interfeței, descrisă [[http://lwn.net/Articles/22355/ | aici]]. | ||
- | * Căutați **extra-simple**. | ||
- | * În funcția de afișare ''my_seq_show'' va trebui să: | ||
- | * obțineți structura [[http://lxr.free-electrons.com/source/include/linux/mm_types.h?v=3.7#L311|mm_struct]] a procesului curent folosind funcția [[http://lxr.linux.no/#linux+v3.9.2/kernel/fork.c#L661|get_task_mm]]; | ||
- | * iterați prin toată lista de structuri [[http://lxr.free-electrons.com/source/include/linux/mm_types.h?v=3.7#L220|vm_area_struct]] asociată procesului; | ||
- | * vă bazați pe ''vm_start'' și ''vm_end'' pentru fiecare structură pentru a deduce dimensiunea totală; | ||
- | * afișați, folosind ''printk("%lx %lx\n, ... ''), ''vm_start'' și ''vm_end'' pentru fiecare structură ''vm_area_struct''. | ||
- | * decrementați reference counter-ul structurii [[http://lxr.free-electrons.com/source/include/linux/mm_types.h?v=3.7#L311|mm_struct]] folosind [[http://lxr.linux.no/#linux+v3.9.2/kernel/fork.c#L606|mmput]]; | ||
- | * folosiți [[http://lxr.linux.no/#linux+v2.6.38/include/linux/seq_file.h#L86|seq_printf]] pentru a scrie în fișier. | ||
- | * Afișați **doar** domensiunea totală, fără alt mesaj/șir de caractere. | ||
- | * Înregistrați funcția de afișare folosind apelul [[http://lxr.free-electrons.com/source/fs/seq_file.c?v=3.7#L581|single_open]]. | ||
- | * Pentru testare utilizați testul din directorul ''lin/test''. | ||
- | * Definiți ''TASK_4'' în ''mmap_test.c''. | ||
- | * Rulați testul în background după care folosiți comanda ''pmap'' pentru a vedea mapările testului și a le compara cu cele obținute. | ||
- | |||
- | |||
- | ==== Soluții ==== | ||
- | |||
- | * [[http://elf.cs.pub.ro/so2/res/laboratoare/lab12-sol.zip | Soluții exerciții laborator 12]] | ||
===== Resurse utile ===== | ===== Resurse utile ===== | ||
Line 315: | Line 188: | ||
- [[http://www.ecst.csuchico.edu/~beej/guide/ipc/mmap.html|Memory Mapped Files]] | - [[http://www.ecst.csuchico.edu/~beej/guide/ipc/mmap.html|Memory Mapped Files]] | ||
- [[http://en.wikipedia.org/wiki/Mmap|mmap]] | - [[http://en.wikipedia.org/wiki/Mmap|mmap]] | ||
- | |||
- |