User Tools

Site Tools


linux-kernel-dev:capitole:capitol-03

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
linux-kernel-dev:capitole:capitol-03 [2015/09/05 19:18]
razvan created
linux-kernel-dev:capitole:capitol-03 [2016/08/31 07:50] (current)
razvan [Resurse utile]
Line 1: Line 1:
-====== ​Drivere ​de dispozitiv ​======+====== ​3. Kernel API ====== 
 + 
 +===== Subiecte abordate ===== 
 + 
 +  * API-ul expus de kernel pentru module de kernel: printing, lucrul cu memoria, liste 
 +  * Erori în kernel, depanarea erorilor 
 + 
 + 
 +===== Resurse utile ===== 
 + 
 +  * [[https://​www.kernel.org/​doc/​htmldocs/​kernel-api/​|The Linux Kernel API]] 
 +  * [[http://​ocw.cs.pub.ro/​courses/​so2/​laboratoare/​lab03|SO2:​ Kernel API]] 
 + 
 + 
 +===== Exerciții ===== 
 + 
 +Arhiva de suport pentru exerciții se găsește [[http://​koala.cs.pub.ro/​training/​res/​linux-kernel-dev/​arc/​cap-03-tasks.zip|aici]]. Descărcați arhiva și apoi decomprimați-o folosind comanda<​code>​ 
 +unzip cap-03-tasks.zip 
 +</​code>​ 
 + 
 +==== Alocare dinamică a memoriei ==== 
 + 
 +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:<​code>​ 
 +free 
 +cat /​proc/​meminfo 
 +</​code>​ 
 + 
 +<​note>​ 
 +Dacă s-ar aloca foarte multă memorie sau dacă bucla for ar avea o durată mai mare, sistemul va rămâne fără memorie și la un moment dat va îngheța. În momentul în care sistemul are puțină memorie se activează OOM (//Out of Memory//) care începe să omoare procese ca să elibereze memoria. 
 +</​note>​ 
 + 
 +==== Eliberarea memoriei alocate dinamic == 
 + 
 +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ă. 
 + 
 +<note tip> 
 +Vectorul trebuie definit ca variabilă globală ca să fie accesibil atât în funcția de inițializare cât și în funcția de ''​exit''​. 
 +</​note>​ 
 + 
 +==== Afișarea listei de procese a sistemului ==== 
 + 
 +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șarea listei de procese copil a procesului init ==== 
 + 
 +Afișați pentru procesul init, PID-ul și comanda proceselor sale copil. 
 + 
 +<note tip> 
 +Structura ''​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''​. 
 +</​note>​ 
 + 
 +==== Afișarea zonelor de memorie ale procesului ==== 
 + 
 +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. 
 + 
 +<note tip> 
 +O zona de memorie virtuală este dată de structura ''​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''​. 
 +</​note>​ 
 + 
 +==== Depanarea unui oops ==== 
 + 
 +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:​ 
 +  - Adresa unde a fost încărcat modulul, identificabilă din fișierul ''/​proc/​modules''​. 
 +  - Adresa unde a apărut oops-ul, identificabilă din mesajul de oops, parcurgând registrul ''​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<​code>​ 
 +addr2line -e <​module-name.o>​ <​offset>​ 
 +</​code>​ 
 +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<​code>​ 
 +addr2line -e oops_mod.o 0x17 
 +</​code>​ 
 + 
 +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''​. 
 + 
 +==== Interpretarea unui stacktrace ==== 
 + 
 +Î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. 
 + 
 +<note tip> 
 +Ce se întâmplă cu variabila **locală** ''​work''​ în momentul în care funcția ''​scchedule_work()''​ se încheie? 
 +</​note>​
  
-TODO 
  
linux-kernel-dev/capitole/capitol-03.1441469932.txt.gz · Last modified: 2015/09/05 19:18 by razvan