Arhiva de suport pentru exerciții se găsește aici. Descărcați arhiva și apoi decomprimați-o folosind comanda
unzip cap-03-tasks.zip
Alocarea dinamică a memoriei în nucleul Linux se face, de regulă, folosind apelul kmalloc
. Acesta alocă memorie rezidentă (care ocupă în permanență memoria până la allocare).
În directorul alloc/
din arhiva de suport există un modul de kernel care alocă 128KB
de RAM într-o buclă for
. Întrucât modulul doar alocă memoria, nu o și dezalocă aceasta rămâne ocupată.
Compilați modulul și apoi încărcați-l și descărcați-l de mai multe ori în kernel. Observați după fiecare încărcare/descărcare dimensiunea memoriei ocupate. Pentru a afla această informație puteți folosi una dintre comenzile de mai jos:
free cat /proc/meminfo
Pentru anularea încărcării memoriei, trebuie să eliberăm memoria alocată dinamică.
Pentru aceasta definiți un vector în care rețineți pointerii către zonele de memorie alocate. Apoi, în funcția de exit
a modulului, folosiți funcția kfree()
pentru a elibera memoria astfel alocată.
exit
.
Nucleul se ocupă de gestionarea sistemului și una dintre structurile esențiale este lista. În general nucleul folosește liste dublu înlănțuite pentru a permite parcurgeri în ambele sensuri.
În general un modul de kernel va avea nevoie să parcurgă listele definite în kernel. În anumite situații va și adăuga și șterge elemente dar cea mai frecventă operație este parcurgerea.
În directorul print-processes/
din arhiva de suport a capitolului se găsește o implementare de modul de kernel care afișează PID-urile și comenzile proceselor sistemului (referite de pointeri la structuri de tipul task_struct
) atât în funcția de inițializare cât și în funcția de ieșire. În fiecare funcție folosește alt format: în inițializare parcurge în mod clasic lista de procese începând cu procesul curent, în vreme ce în funcția de ieșire folosește macro-ul for_each_process
.
Afișați pentru procesul init, PID-ul și comanda proceselor sale copil.
task_struct
a procesului init este indicată de variabila globală init_task
.
Santinela listei dublu înlănțuite este dată de câmpul children
al structurii task_struct
.
Folosiți macro-ul list_for_each
pentru a parcurge procesele copil și apoi macro-ul list_entry
pentru a obține structura task_struct
pentru fiecare proces din listă. Câmpul din cadrul structurii task_struct
care definește structura de tip listă care leagă toate procesele copil este sibling
.
Pentru acomodarea cu lucrul cu liste parcurgeți zonele de memorie virtuală ale procesului curent. Pentru fiecare zonă afișați adresa de start și cea de sfârșit.
vm_area_struct
, definită în include/linux/mm_types.h
.
Primul element al listei de zone este câmpul mmap
al structurii mm_struct
care definește spațiul virtual de adresă al procesului. Iar pointerul la structura care definește spațiul virtual de adresă al procesului este dat de câmpul mm
al structurii task_struct
.
Atunci când nucleul întâlnește o eroare afișează un mesaj de eroare similar Segmentation fault numit oops. Este o indicație că ceva nepotrivit a avut loc; sistemul de operare continuă execuția, dar este posibil ca eroarea să se propage.
Erorile grave se prezintă în formă de kernel panic și conduc la înghețarea sistemului.
În directorul oops/
din arhiva de suport a laboratorului se găsește implementat un modul care accesează un pointer NULL
rezultând în oops. Încărcați modulul pentru a observa acest lucru. Observați că nu se mai permite nucleului să fie descărcat pentru a nu cauza mai multe pagube.
Atunci când avem module de kernel complexe e dificil de verificat unde a apărut eroarea. Pentru aceasta putem folosi utilitarul addr2line
care realizează o asociere între adresa în care a apărut oops-ul și linia din fișier. Folosirea addr2line
depinde de două informații:
/proc/modules
.EIP
(instruction pointer).Se calculează offset-ul între cele două adrese, adică diferențaa dintre adresa unde a apărut oops-ul și adresa unde a fost încărcat modulul. Cu acest offset se rulează, pe mașina virtuală VirtualBox, comanda
addr2line -e <module-name.o> <offset>
unde <module-name>.o
este numele fișierului obiect cu extensia .o
(nu .ko
) iar <offset>
este offset-ul calculat anterior.
În cazul nostru comanda de rulat pe mașina virtuală VirtualBox va fi
addr2line -e oops_mod.o 0x17
Output-ul acestei comenzi este linia din cod care a cauzat oops-ul. Așa cum era de așteptat această linie este chiar linia în care se derefernțiază pointer-ul inițializat la NULL
.
În directorul workqueue-bad/
avem o implementare defectuoasă a afișării la 5 secunde a unui mesaj. Atunci când inserăm modulul și apoi îl eliminăm primim eroare. Motivul este că există încă planificat un work
iar modulul este eliminat ducând la eliberarea zonelor alocate. Consecința este mesajul de eroare.
Captura mesajului de eroare, prin stacktrace-ul afișat, ne oferă informații despre ce cauzează eroarea.
work
în momentul în care funcția scchedule_work()
se încheie?