Laborator 11: Exerciții

Pregătirea laboratorului

Pentru rezolvarea laboratorului, vom lucra în același director din care pornim mașina virtuală (~/so2/linux/tools/labs).

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:

tools/labs $ make skels

Pentru a genera scheletul pentru un singur laborator, vom folosi variabila de mediu LABS:

tools/labs $ make clean
tools/labs $ LABS=<lab name> make skels

Numele laboratorului curent este memory_mapping.

Similar, putem genera și scheletul pentru un singur exercițiu, atribuind valoarea <lab_name>/<task_name> variabilei LABS.

Scheletul este generat în directorul tools/labs/skels.

Compilarea modulelor

Comanda make build compilează toate modulele din directorul skels.

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

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

student@eg106:~/so2/linux/tools/labs$ make copy
student@eg106:~/so2/linux/tools/labs$ make boot

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 Interacțiunea cu mașina virtuală.

Testarea modulelor

Modulele generate sunt copiate pe mașina virtuală în directorul /home/root/skels/<lab_name>.

root@qemux86:~/skels/memory_mapping# ls
kmmap  test   vmmap
root@qemux86:~/skels/memory_mapping# ls vmmap/
vmmap.ko

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:

root@qemux86:~# insmod skels/<lab_name>/<task_name>/<module_name>.ko
root@qemux86:~# rmmod skels/<lab_name>/<task_name>/<module_name>.ko

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:

  1. Î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.
  2. Î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.
  3. În al treilea tab de terminal accesăm directorul ~/so2/linux/ cu sursele nucleului unde putem folosi Vim și cscope pentru parcurgerea codului sursă.
    student@eg106-pc:~$ netcat -lup 6666

Exerciții

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

1. [3p] Mapare de memorie fizic contiguă î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.
  • 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 PAGE_SIZE.
    • Zona alocată din spațiul kernel este indicată de kmalloc_area.
    • Dimensiunea NPAGES+2 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:
      kmalloc_area = (char *)((((unsigned long)kmalloc_ptr) + PAGE_SIZE - 1) & PAGE_MASK);
  • 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 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.
  • Al treilea argument primit de remap_pfn_range este un număr de pagină fizică (pfnpage frame number).
    • Pentru conversia din adresă kernel virtuală (adică kmalloc_area) în adresă fizică utilizați 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 test/.
    • Rulați testul folosind comanda ./mmap-test.
    • Dacă totul merge bine testul va afișa mesaje matched.
  • Descărcați modulul din kernel.

2. [3p] Mapare de memorie fizic discontiguă î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.
    • Completați zonele marcate cu TODO 1.
    • Parcurgeți secțiunea 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 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 vmalloc_to_pfn.
    • 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ă.
      • Adică veți avea nevoie de un ciclu for și veți incrementa adresa virtuală kernel (vmalloc_area) cu câte o pagină și veți obține pentru fiecare adresă virtuală astfel obținută pfn-ul.
    • Folosiți vmalloc_area_ptr pentru parcurgerea spațiului virtual din kernel space.
      • Nu modificați variabila globală vmalloc_area.
  • Argumentul pentru vmalloc_to_pfn este o adresă virtuală din kernel space.
    • Al doilea argument pentru remap_pfn_range este o adresă virtuală din cadrul unui VMA.
    • Al treilea argument pentru 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 test/.
    • Rulați testul folosind comanda ./mmap-test.
    • Dacă totul merge bine testul va afișa mesaje matched.
  • Descărcați modulul din kernel.

3. [2p] 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.
    • 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.
    • Completați zonele marcate cu TODO 2.
    • Revedeți Laboratorul 4
  • Ignorați parametrul offset trimis operației de read/write.
  • Pentru testare utilizați testul din directorul test/.
    • Rulați testul folosind comanda ./mmap-test 3.

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.
    • 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 proc_create.
    • Pentru parametrul mode folosiți 0, iar pentru parametrul parent folosiți NULL.
    • 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 remove_proc_entry.
  • O utilizare (complexă) și descriere a interfeței seq_file se găsește aici acest exemplu.
  • Pentru acest exercițiu este suficientă doar o simplă utilizare a interfeței, descrisă aici. Căutați extra-simple.
  • În funcția de afișare my_seq_show va trebui să:
    • Obțineți structura mm_struct a procesului curent folosind funcția get_task_mm.
      • Procesul curent este indicat de variabila current de tip struct task_struct *.
    • Iterați prin toată lista de structuri vm_area_struct asociată procesului.
      • Folosiți variabila vma_iterator.
      • Porniți de la mm->mmap.
      • Folosiți câmpul vm_next al vm_area_struct pentru a parcurge lista de zone de memorie (vma-uri – virtual memory areas).
      • Opriți-vă când ajungeți la NULL.
    • 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.
    • Pentru a “elibera” structura mm_struct, decrementați reference counter-ul structurii folosind mmput.
    • Folosiți seq_printf pentru a scrie în fișier.
      • Afișați doar domensiunea totală, fără alt mesaj/șir de caractere. Nu afișați nici măcar newline (\n).
  • În funcția my_seq_open înregistrați funcția de afișare (my_seq_show) folosind apelul single_open.
    • Ca al treilea argument pentru single_open puteți folosi NULL.
  • Pentru testare utilizați testul din directorul test/.
    • Rulați testul folosind comanda ./mmap-test 4.
      • 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.
        • Puteți folosi comanda în forma
          cat /proc/$(pidof mmap-test)/maps

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

Soluții

Resurse utile

so2/laboratoare/lab11/exercitii.txt · Last modified: 2019/05/08 09:53 by constantin.ghioc
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