Laborator 11: Exerciții

Pentru desfășurarea laboratorului pornim de la 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):

student@mjolnir:~$ cd so2/
student@mjolnir:~/so2$ wget http://elf.cs.pub.ro/so2/res/laboratoare/lab11-tasks.zip
student@mjolnir:~/so2$ unzip lab11-tasks.zip
student@mjolnir:~/so2$ tree lab11-tasks

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

student@mjolnir:~/so2$ cp /path/to/module.ko ~/so2/qemu-so2/fsimg/root/modules/

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

student@mjolnir:~/so2/qemu-so2$ make

După pornirea mașinii virtuale QEMU vom putea folosi comenzi în fereastra QEMU pentru a încărca și descărca modulul de kernel:

# insmod modules/module-name.ko
# rmmod module/module-name

unde module-name este numele modulului de kernel.

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:

  1. Î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.
  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/qemu-so2/.
  3. În al treilea tab de terminal pornim minicom sau un server UDP care să primească mesajele de netconsole. Nu contează în ce director ne aflăm. Folosim comanda
    student@mjolnir:~$ netcat -lup 6666

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.
  • 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 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:
      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 lin/test.
    • Compilați testul folosind comanda make.
    • 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 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 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 lin/test.
    • Compilați testul folosind comanda make.
    • 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 3.
    • Revedeți 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

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 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 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 lin/test.
    • Definiți TASK_4 în mmap_test.c.
    • Rulați testul.
      • 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
          pmap $(pidof mmap-test)

  • Pentru a accesa altă consolă în mașina virtuală 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: 2017/05/07 19:38 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