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

Both sides previous revision Previous revision
Next revision
Previous revision
linux-kernel-dev:capitole:capitol-03 [2015/09/07 19:43]
razvan [Drivere de dispozitiv]
linux-kernel-dev:capitole:capitol-03 [2016/08/31 07:50] (current)
razvan [Resurse utile]
Line 1: Line 1:
-====== 3. Drivere de dispozitiv ​======+====== 3. Kernel API ======
  
 ===== Subiecte abordate ===== ===== Subiecte abordate =====
  
-  * API-ul de kernel: printing, lucrul cu memoria, liste +  * API-ul ​expus de kernel pentru module ​de kernel: printing, lucrul cu memoria, liste 
-  * Erori în kernel, depanarea erorilor: oops-uri și panic-uri, stack trace, objdump, addr2line +  * Erori în kernel, depanarea erorilor 
-  * Dispozitive văzute în user space: major, minor, /dev +
-  * Dispozitive virtuale +
-  * Operații pe dispozitive:​ citire, scriere, structura file operations, structura cdev +
-  * Generator de 1 +
-  * Reply device +
-  * Character numbering device+
  
 ===== Resurse utile ===== ===== Resurse utile =====
  
-  * TODO+  * [[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 ===== ===== Exerciții =====
Line 22: Line 19:
 </​code>​ </​code>​
  
-Alocarea de memorie în mod dinamic. Atenție la dezalocare.+==== Alocare ​dinamică a memoriei ====
  
-Dezalocare într-un alt modul+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).
  
-(tutorial) Afișarea listei ​de procese a sistemului+Î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ă.
  
-(exercițiu) Afișarea zonelor de memorie ale procesului+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>​
  
-(tutorial) Depanarea unui oops+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''​.
  
-(tutorial) Char device care face ioctl și afișare+==== Interpretarea unui stacktrace ====
  
-(exercițiu) Char device care face alocare ​și dezalocare cu ioctl+Î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.
  
-(tutorial) Generator ​de "​anaaremere"​+Captura mesajului ​de eroare, prin stacktrace-ul afișat, ne oferă informații despre ce cauzează eroarea.
  
-(exercițiu) Generator de caractere de "​1" ​(la nesfârșit)+<note tip> 
 +Ce se întâmplă cu variabila **locală** ''​work''​ în momentul în care funcția ''​scchedule_work()''​ se încheie? 
 +</​note>​
  
-(exercițiu) Reply device: ce scrii, apoi primești 
  
-(exercițiu) La citire întoarce lungimea ultimului șir scris 
linux-kernel-dev/capitole/capitol-03.1441644210.txt.gz · Last modified: 2015/09/07 19:43 by razvan