This shows you the differences between two versions of the page.
so2:laboratoare:lab11:exercitii [2017/05/07 19:38] octavian.purdila [3. [2p] Operații de read/write în memoria mapată] |
so2:laboratoare:lab11:exercitii [2019/05/08 09:53] (current) constantin.ghioc [2. [3p] Mapare de memorie virtual contiguă în user space] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Laborator 11: Exerciții ====== | ====== Laborator 11: Exerciții ====== | ||
- | Pentru desfășurarea laboratorului pornim de la [[http://elf.cs.pub.ro/so2/res/laboratoare/lab11-tasks.zip|arhiva de sarcini a laboratorului]]. Descărcăm și decomprimăm arhiva în directorul ''so2/'' din directorul home al utilizatorului ''student'' de pe sistemul de bază (stația ''mjolnir''):<code bash> | + | |
- | student@mjolnir:~$ cd so2/ | + | ===== Pregătirea laboratorului ===== |
- | student@mjolnir:~/so2$ wget http://elf.cs.pub.ro/so2/res/laboratoare/lab11-tasks.zip | + | |
- | student@mjolnir:~/so2$ unzip lab11-tasks.zip | + | Pentru rezolvarea laboratorului, vom lucra în același director din care pornim mașina virtuală (''~/so2/linux/tools/labs''). |
- | student@mjolnir:~/so2$ tree lab11-tasks | + | |
+ | Pașii de rezolvare sunt următorii: | ||
+ | * pregătirea scheletului de laborator | ||
+ | * compilarea modulelor de Kernel | ||
+ | * copierea modulelor pe mașina virtuală | ||
+ | * pornirea mașinii virtuale și testarea modulelor | ||
+ | |||
+ | ==== Pregătirea scheletului de laborator ==== | ||
+ | |||
+ | Pentru rezolvarea laboratorului trebuie sa activam suportul de netfilter din kernel. In meniul deschis cu ''make menuconfig'' | ||
+ | activati optiunea ''Networking support/Networking options/Network packet filtering framework (Netfilter)''. | ||
+ | |||
+ | Scheletul de laborator este generat din sursele din directorul ''tools/labs/templates''. Putem genera scheletele pentru toate laboratoarele folosind următoarea comanda: | ||
+ | |||
+ | <code bash> | ||
+ | tools/labs $ make skels | ||
</code> | </code> | ||
- | În cadrul directorului ''lab11-tasks/'' se găsesc resursele necesare pentru dezvoltarea exercițiilor de mai jos: fișiere schelet de cod sursă, fișiere Makefile și Kbuild, scripturi și programe de test. | ||
- | Vom dezvolta exercițiile pe sistemul de bază (stația ''mjolnir'') și apoi le vom testa pe [[:so2:resurse:masini-virtuale|mașina virtuală QEMU]]. După editarea și compilarea unui modul de kernel îl vom copia în directorul dedicat pentru mașina virtuală QEMU folosind o comandă de forma<code bash> | + | Pentru a genera scheletul pentru un singur laborator, vom folosi variabila de mediu ''LABS'': |
- | student@mjolnir:~/so2$ cp /path/to/module.ko ~/so2/qemu-so2/fsimg/root/modules/ | + | |
- | </code> unde ''/path/to/module.ko'' este calea către fișierul obiect aferent modulului de kernel. Apoi vom porni, din directorul ''~/so2/qemu-so2/'' mașina virtuală QEMU folosind comanda<code bash> | + | <code bash> |
- | student@mjolnir:~/so2/qemu-so2$ make | + | tools/labs $ make clean |
+ | tools/labs $ LABS=<lab name> make skels | ||
</code> | </code> | ||
- | După pornirea mașinii virtuale QEMU vom putea folosi comenzi în fereastra QEMU pentru a încărca și descărca modulul de kernel:<code> | + | <note important> |
- | # insmod modules/module-name.ko | + | Numele laboratorului curent este ''memory_mapping''. |
- | # rmmod module/module-name | + | </note> |
- | </code> unde ''module-name'' este numele modulului de kernel. | + | |
+ | Similar, putem genera și scheletul pentru un singur exercițiu, atribuind valoarea ''<lab_name>/<task_name>'' variabilei ''LABS''. | ||
<note> | <note> | ||
- | Pentru dezvoltarea laboratorului, este recomandat să folosim trei terminale sau, mai bine, trei tab-uri de terminal. Pentru a deschide un nou tab de terminal folosim combinația de taste ''Ctrl+Alt+t''. Cele trei tab-uri de terminal îndeplinesc următoarele roluri: | + | Scheletul este generat în directorul ''tools/labs/skels''. |
- | - În primul tab de terminal dezvoltăm modulul de kernel: editare, compilare, copiere în directorul dedicat pentru mașina virtaulă QEMU. Lucrăm în directorul aferent rezultat în urma decomprimării arhivei de sarcini a laboratorului. | + | </note> |
- | - În al doilea tab de terminal pornim mașina virtuală QEMU și apoi testăm modulul de kernel: încărcare/descărcare modul, rulare teste. Lucrăm în directorul aferent mașinii virtuale: ''~/so2/qemu-so2/''. | + | |
- | - În al treilea tab de terminal pornim [[:so2:laboratoare:lab02#minicom|minicom]] sau un server UDP care să primească [[:so2:laboratoare:lab02#netconsole|mesajele de netconsole]]. Nu contează în ce director ne aflăm. Folosim comanda<code bash> | + | ==== Compilarea modulelor ==== |
- | student@mjolnir:~$ netcat -lup 6666 | + | |
+ | Comanda ''make build'' compilează toate modulele din directorul ''skels''. | ||
+ | |||
+ | <code bash> | ||
+ | student@eg106:~/so2/linux/tools/labs$ make build | ||
+ | echo "# autogenerated, do not edit " > skels/Kbuild | ||
+ | echo "ccflags-y += -Wno-unused-function -Wno-unused-label -Wno-unused-variable " >> skels/Kbuild | ||
+ | for i in ./memory_mapping/vmmap ./memory_mapping/kmmap; do echo "obj-m += $i/" >> skels/Kbuild; done | ||
+ | ... | ||
</code> | </code> | ||
+ | |||
+ | ==== Copierea modulelor pe mașina virtuală ==== | ||
+ | |||
+ | Putem copia modulele generate pe mașina virtuală folosind target-ul ''copy'' al comenzii make, atunci când mașina virtuală este oprită. | ||
+ | |||
+ | <code bash> | ||
+ | student@eg106:~/so2/linux/tools/labs$ make copy | ||
+ | student@eg106:~/so2/linux/tools/labs$ make boot | ||
+ | </code> | ||
+ | |||
+ | Alternativ, putem copia fișierele prin ''scp'', pentru e evita repornirea mașinii virtuale. Pentru detalii despre folosirea interacțiunea prin rețea cu mașina virtuală citiți [[https://ocw.cs.pub.ro/courses/so2/resurse/masini-virtuale#interactiunea_cu_masina_virtuala|Interacțiunea cu mașina virtuală]]. | ||
+ | |||
+ | ==== Testarea modulelor ==== | ||
+ | |||
+ | Modulele generate sunt copiate pe mașina virtuală în directorul ''/home/root/skels/<lab_name>''. | ||
+ | |||
+ | <code bash> | ||
+ | root@qemux86:~/skels/memory_mapping# ls | ||
+ | kmmap test vmmap | ||
+ | root@qemux86:~/skels/memory_mapping# ls vmmap/ | ||
+ | vmmap.ko | ||
+ | </code> | ||
+ | |||
+ | După pornirea mașinii virtuale QEMU vom putea folosi comenzi în fereastra QEMU (sau în ''minicom'') pentru a încărca și descărca modulul de kernel:<code> | ||
+ | root@qemux86:~# insmod skels/<lab_name>/<task_name>/<module_name>.ko | ||
+ | root@qemux86:~# rmmod skels/<lab_name>/<task_name>/<module_name>.ko | ||
+ | </code> | ||
+ | |||
+ | <note> | ||
+ | Pentru dezvoltarea laboratorului, este recomandat să folosim trei terminale sau, mai bine, trei tab-uri de terminal. Pentru a deschide un nou tab de terminal folosim combinația de taste ''Ctrl+Shift+t''. Cele trei tab-uri de terminal îndeplinesc următoarele roluri: | ||
+ | - În primul tab de terminal dezvoltăm modulul de kernel: editare, compilare, copiere în directorul dedicat pentru mașina virtuală QEMU. Lucrăm în directorul aferent rezultat în urma decomprimării arhivei de sarcini a laboratorului. | ||
+ | - În al doilea tab de terminal pornim mașina virtuală QEMU și apoi testăm modulul de kernel: încărcare/descărcare modul, rulare teste. Lucrăm în directorul aferent mașinii virtuale: ''~/so2/linux/tools/labs''. | ||
+ | - În al treilea tab de terminal accesăm directorul ''~/so2/linux/'' cu sursele nucleului unde putem folosi [[:so2:laboratoare:lab01#cscope|Vim și cscope]] pentru parcurgerea codului sursă.<code> | ||
+ | student@eg106-pc:~$ netcat -lup 6666 | ||
+ | </code> | ||
+ | |||
+ | </note> | ||
+ | |||
+ | ===== Exerciții ===== | ||
+ | |||
+ | <note important> | ||
+ | Înainte de începerea rezolvării laboratorului, rulați comanda ''%%git pull --rebase%%'' in directorul ''~/so2/linux'', pentru a obține ultima versiune a scheletului de laborator. | ||
</note> | </note> | ||
==== 1. [3p] Mapare de memorie fizic contiguă în user space ==== | ==== 1. [3p] 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. | + | * Utilizați scheletul de modul de kernel din directorul ''kmmap/'' pentru a crea o mapare a memoriei driver-ului în user-space. |
* Memoria driver-ului este alocată folosind ''kmalloc''. | * Memoria driver-ului este alocată folosind ''kmalloc''. | ||
* Completați zonele marcate cu ''TODO 1''. | * Completați zonele marcate cu ''TODO 1''. | ||
* Parcurgeți secțiunea [[:so2:laboratoare:lab11#Kernel-space|Maparea memoriei. Kernel-space]]. | * Parcurgeți secțiunea [[:so2:laboratoare:lab11#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''. | + | * Alocați, în funcția de inițializare a modulului, o zonă de memorie de ''NPAGES+2'' pagini folosind ''kmalloc''. |
* O pagină în kernel are dimensiunea [[http://elixir.free-electrons.com/linux/v4.9/source/arch/x86/include/asm/page_types.h#L9|PAGE_SIZE]]. | * O pagină în kernel are dimensiunea [[http://elixir.free-electrons.com/linux/v4.9/source/arch/x86/include/asm/page_types.h#L9|PAGE_SIZE]]. | ||
* Zona alocată din spațiul kernel este indicată de ''kmalloc_area''. | * Zona alocată din spațiul kernel este indicată de ''kmalloc_area''. | ||
- | * Dimensiunea ''NPAGES+1'' este necesară pentru aliniere. | + | * Dimensiunea ''NPAGES+2'' este necesară pentru aliniere. |
* Adresa ''kmalloc_ptr'' (variabilă globală) obținută în urma apelului ''kmalloc'' trebuie aliniată la adresa unei pagini. | * 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> | * Pentru aceasta, va trebui să alocați o pagină în plus față de numărul de pagini necesare și să folosiți formula:<code> | ||
Line 51: | Line 117: | ||
* Shiftați rezultatul cu [[http://elixir.free-electrons.com/linux/v4.9/source/arch/x86/include/asm/page_types.h#L8|PAGE_SHIFT]] biți pentru a obține ''pfn''. | * Shiftați rezultatul cu [[http://elixir.free-electrons.com/linux/v4.9/source/arch/x86/include/asm/page_types.h#L8|PAGE_SHIFT]] biți pentru a obține ''pfn''. | ||
* Încărcați modulul în kernel. | * Încărcați modulul în kernel. | ||
- | * Pentru testare utilizați testul din directorul ''lin/test''. | + | * Pentru testare utilizați testul din directorul ''test/''. |
- | * Compilați testul folosind comanda ''make''. | + | |
* Rulați testul folosind comanda ''./mmap-test''. | * Rulați testul folosind comanda ''./mmap-test''. | ||
* Dacă totul merge bine testul va afișa mesaje ''matched''. | * Dacă totul merge bine testul va afișa mesaje ''matched''. | ||
* Descărcați modulul din kernel. | * Descărcați modulul din kernel. | ||
- | ==== 2. [3p] Mapare de memorie virtual contiguă în user space ==== | + | ==== 2. [3p] Mapare de memorie fizic discontiguă î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. | + | * Utilizați scheletul de modul de kernel din directorul ''vmmap/'' pentru a crea o mapare a memoriei driver-ului în user-space. |
* Memoria driver-ului este alocată folosind ''vmalloc''. | * Memoria driver-ului este alocată folosind ''vmalloc''. | ||
- | * Completați zonele marcate cu ''TODO 2''. | + | * Completați zonele marcate cu ''TODO 1''. |
* Parcurgeți secțiunea [[:so2:laboratoare:lab11#Kernel-space|Maparea memoriei. Kernel-space]] din laborator. | * Parcurgeți secțiunea [[:so2:laboratoare:lab11#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''. | * Alocați o zonă de memorie de ''NPAGES'' pagini în funcția de inițializare a modulului folosind ''vmalloc''. | ||
Line 80: | Line 145: | ||
* Al treilea argument pentru [[http://elixir.free-electrons.com/linux/v4.9/source/mm/memory.c#L1760|remap_pfn_range]] este pfn-ul paginii virtuale din kernel-space care se dorește remapată. | * Al treilea argument pentru [[http://elixir.free-electrons.com/linux/v4.9/source/mm/memory.c#L1760|remap_pfn_range]] este pfn-ul paginii virtuale din kernel-space care se dorește remapată. | ||
* Încărcați modulul în kernel. | * Încărcați modulul în kernel. | ||
- | * Pentru testare utilizați testul din directorul ''lin/test''. | + | * Pentru testare utilizați testul din directorul ''test/''. |
- | * Compilați testul folosind comanda ''make''. | + | |
* Rulați testul folosind comanda ''./mmap-test''. | * Rulați testul folosind comanda ''./mmap-test''. | ||
* Dacă totul merge bine testul va afișa mesaje ''matched''. | * Dacă totul merge bine testul va afișa mesaje ''matched''. | ||
Line 91: | Line 155: | ||
* E vorba de un exercițiu didactic ca să vedem că același spațiu poate fi folosit și cu apel-ul ''mmap'' și cu apeluri de tipul ''read'' și ''write''. | * E vorba de un exercițiu didactic ca să vedem că același spațiu poate fi folosit și cu apel-ul ''mmap'' și cu apeluri de tipul ''read'' și ''write''. | ||
* Operațiile de citire și scriere vor acționa chiar asupra zonei de memorie alocate. | * Operațiile de citire și scriere vor acționa chiar asupra zonei de memorie alocate. | ||
- | * Completați zonele marcate cu ''TODO 3''. | + | * Completați zonele marcate cu ''TODO 2''. |
* Revedeți [[so2:laboratoare:lab04 | Laboratorul 4]] | * Revedeți [[so2:laboratoare:lab04 | Laboratorul 4]] | ||
* **Ignorați** parametrul ''offset'' trimis operației de read/write. | * **Ignorați** parametrul ''offset'' trimis operației de read/write. | ||
- | * Pentru testare utilizați testul din directorul ''lin/test''. | + | * Pentru testare utilizați testul din directorul ''test/''. |
- | * Definiți macroul ''TASK_3'' în ''mmap_test.c'' | + | * Rulați testul folosind comanda ''./mmap-test 3''. |
+ | |||
==== 4. [3p] Afișare memorie mapată în ''procfs'' ==== | ==== 4. [3p] 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. | * 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''. | + | * Completați zonele marcate cu ''TODO 3''. |
* 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://elixir.free-electrons.com/linux/v4.9/source/include/linux/proc_fs.h#L30|proc_create]]. | * 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://elixir.free-electrons.com/linux/v4.9/source/include/linux/proc_fs.h#L30|proc_create]]. | ||
* Pentru parametrul ''mode'' folosiți ''0'', iar pentru parametrul ''parent'' folosiți ''NULL''. | * Pentru parametrul ''mode'' folosiți ''0'', iar pentru parametrul ''parent'' folosiți ''NULL''. | ||
Line 120: | Line 186: | ||
* În funcția ''my_seq_open'' înregistrați funcția de afișare (''my_seq_show'') folosind apelul [[http://elixir.free-electrons.com/linux/v4.9/source/fs/seq_file.c#L566|single_open]]. | * În funcția ''my_seq_open'' înregistrați funcția de afișare (''my_seq_show'') folosind apelul [[http://elixir.free-electrons.com/linux/v4.9/source/fs/seq_file.c#L566|single_open]]. | ||
* Ca al treilea argument pentru ''single_open'' puteți folosi ''NULL''. | * Ca al treilea argument pentru ''single_open'' puteți folosi ''NULL''. | ||
- | * Pentru testare utilizați testul din directorul ''lin/test''. | + | * Pentru testare utilizați testul din directorul ''test/''. |
- | * Definiți ''TASK_4'' în ''mmap_test.c''. | + | * Rulați testul folosind comanda ''./mmap-test 4''. |
- | * Rulați testul. | + | |
* Testul așteaptă un timp (are o instrucțiune ''sleep'' internă). | * Testul așteaptă un timp (are o instrucțiune ''sleep'' internă). | ||
* Cât timp testul așteaptă, folosiți în altă consolă comanda ''pmap'' pentru a vedea mapările testului și a le compara cu cele obținute. | * Cât timp testul așteaptă, folosiți în altă consolă comanda ''pmap'' pentru a vedea mapările testului și a le compara cu cele obținute. | ||
* Puteți folosi comanda în forma<code> | * Puteți folosi comanda în forma<code> | ||
- | pmap $(pidof mmap-test) | + | cat /proc/$(pidof mmap-test)/maps |
</code> | </code> | ||
<note> | <note> | ||
- | * Pentru a accesa altă consolă în mașina virtuală folosiți combinația de taste ''Alt+F2''. | + | * Pentru a accesa altă consolă porniți mașina virtuală în mod grafic și folosiți combinația de taste ''Alt+F2''. |
* Pentru a reveni înapoi în prima consolă folosiți combinația de taste ''Alt+F1''. | * Pentru a reveni înapoi în prima consolă folosiți combinația de taste ''Alt+F1''. | ||
</note> | </note> |