This shows you the differences between two versions of the page.
so2:laboratoare:lab03:exercitii [2018/03/04 21:06] ionel.ghita [2. [1p] Folosirea de operații blocante în context atomic] |
so2:laboratoare:lab03:exercitii [2018/03/06 23:09] (current) anda.nicolae [2. [1p] Folosirea de operații blocante în context atomic] |
||
---|---|---|---|
Line 123: | Line 123: | ||
==== 2. [1p] Folosirea de operații blocante în context atomic ==== | ==== 2. [1p] Folosirea de operații blocante în context atomic ==== | ||
- | Intrați în directorul ''2-sched-spin/'' și parcurgeți conținutul fișierului ''sched-spin.c''. **Hint**: Funcția [[http://lxr.free-electrons.com/source/kernel/time/timer.c?v=4.9#L1697|schedule_timeout]], coroborată cu macro-ul [[http://lxr.free-electrons.com/source/include/linux/sched.h?v=4.9#L297|set_current_state]], forțează procesul curent să aștepte 5 secunde. | + | Intrați în directorul ''2-sched-spin/'' și parcurgeți conținutul fișierului ''sched-spin.c''. **Hint**: Funcția [[https://elixir.bootlin.com/linux/v4.15.7/source/kernel/time/timer.c#L1725|schedule_timeout]], coroborată cu macro-ul [[https://elixir.bootlin.com/linux/v4.15.7/source/include/linux/sched.h#L128|set_current_state]], forțează procesul curent să aștepte 5 secunde. |
- Compilați codul sursă într-un modul de kernel și copiați-l pe mașina virtuală, conform informațiilor de mai sus (''make build'' și ''make copy''). | - Compilați codul sursă într-un modul de kernel și copiați-l pe mașina virtuală, conform informațiilor de mai sus (''make build'' și ''make copy''). | ||
- Încărcați modulul în kernel folosind comanda ''insmod sched-spin.ko''. | - Încărcați modulul în kernel folosind comanda ''insmod sched-spin.ko''. | ||
* Observați că se așteaptă 5 secunde până la definitivarea comenzii de inserare. | * Observați că se așteaptă 5 secunde până la definitivarea comenzii de inserare. | ||
- | - Descărcați modului din kernel. | + | - Descărcați modulul din kernel. |
- | - Decomentați liniile care conțin operații cu spinlock-uri. Compilați din nou codul sursă și reîncărcați modulul în kernel. | + | - Modificați modulul în locurile marcate cu ''TODO 0'', astfel încât să includeți operația de sleep într-o secțiune atomică, folosind spinlock-ul deja definit. Compilați din nou codul sursă și reîncărcați modulul în kernel. **Hint**: ''spin_lock'' și ''spin_unlock''. |
Ați obținut eroare. Urmăriți stack trace-ul. Care este cauza erorii? **Hint**: Urmăriți, în mesajul de eroare, linia care conține ''BUG'' pentru o descriere a erorii. Nu aveți voie să realizați operații blocante în context atomic. Contextul atomic este dat de o secțiune aflată între o operație de ''lock'' și una de ''unlock'' pe un spinlock. | Ați obținut eroare. Urmăriți stack trace-ul. Care este cauza erorii? **Hint**: Urmăriți, în mesajul de eroare, linia care conține ''BUG'' pentru o descriere a erorii. Nu aveți voie să realizați operații blocante în context atomic. Contextul atomic este dat de o secțiune aflată între o operație de ''lock'' și una de ''unlock'' pe un spinlock. | ||
Line 140: | Line 140: | ||
Intrați în directorul ''3-memory/'' și parcurgeți conținutul fișierului ''memory.c''. Observați comentariile marcate cu TODO. | Intrați în directorul ''3-memory/'' și parcurgeți conținutul fișierului ''memory.c''. Observați comentariile marcate cu TODO. | ||
Trebuie să alocați 4 structuri de tipul ''struct task_info'' și să le inițializați (în funcția ''memory_init''), apoi să le afișați și dezalocați (în funcția ''memory_exit''). | Trebuie să alocați 4 structuri de tipul ''struct task_info'' și să le inițializați (în funcția ''memory_init''), apoi să le afișați și dezalocați (în funcția ''memory_exit''). | ||
- | - (**TODO 1**) Structurile vor conține, respectiv: | + | - (**TODO 1**) Alocați structura ''struct task_info'' și inițializați câmpurile acesteia: |
+ | * câmpul ''pid'' la PID-ul transmis ca parametru; | ||
+ | * câmpul ''timestamp'' la valoarea variabilei ''jiffies'', care menține timpul de activitate al sistemului. | ||
+ | * **Hint**: Detalii despre variabila ''jiffies'' se găsesc în [[http://ocw.cs.pub.ro/courses/so2/laboratoare/lab06#timere|laboratorul 6]]. | ||
+ | - (**TODO 2**) Folosiți funcția completată anterior pentru a inițializa cele 4 structuri cu următoarele informații: | ||
* PID-ul procesului curent, dat de macro-ul ''current'', de tipul ''struct task_struct *''. | * PID-ul procesului curent, dat de macro-ul ''current'', de tipul ''struct task_struct *''. | ||
- | * **Hint**: Căutați câmpul relevant pentru PID în structura [[http://lxr.free-electrons.com/source/include/linux/sched.h?v=4.9#L1475|task_struct]]. | + | * **Hint**: Căutați câmpul relevant pentru PID în structura [[https://elixir.bootlin.com/linux/v4.15.7/source/include/linux/sched.h#L520|task_struct]]. |
* PID-ul procesului părinte al procesului curent. | * PID-ul procesului părinte al procesului curent. | ||
* **Hints**: | * **Hints**: | ||
- | * Căutați câmpul relevant din structura [[http://lxr.free-electrons.com/source/include/linux/sched.h?v=4.9#L1475|task_struct]]. | + | * Căutați câmpul relevant din structura [[https://elixir.bootlin.com/linux/v4.15.7/source/include/linux/sched.h#L520|task_struct]]. |
* Căutați după șirul "parent". | * Căutați după șirul "parent". | ||
* PID-ul procesului următor din lista de procese. | * PID-ul procesului următor din lista de procese. | ||
* **Hints**: | * **Hints**: | ||
- | * Folosiți macro-ul [[http://lxr.free-electrons.com/source/include/linux/sched.h?v=4.9#L2990|next_task]]. | + | * Folosiți macro-ul [[https://elixir.bootlin.com/linux/v4.15.7/source/include/linux/sched/signal.h#L500|next_task]]. |
* Macro-ul întoarce pointer-ul la următorul proces, adică de tipul ''struct task_struct *''. | * Macro-ul întoarce pointer-ul la următorul proces, adică de tipul ''struct task_struct *''. | ||
* PID-ul următorului proces după următorul. | * PID-ul următorului proces după următorul. | ||
- | * **Hint**: Folosiți de două ori macro-ul [[http://lxr.free-electrons.com/source/include/linux/sched.h?v=4.9#L2990|next_task]]. | + | * **Hint**: Folosiți de două ori macro-ul [[https://elixir.bootlin.com/linux/v4.15.7/source/include/linux/sched/signal.h#L500|next_task]]. |
- | - (**TODO 2**) Alocați structura ''struct task_info'' și inițializați câmpurile acesteia: | + | |
- | * câmpul ''pid'' la PID-ul transmis ca parametru; | + | |
- | * câmpul ''timestamp'' la valoarea variabilei ''jiffies'', care menține timpul de activitate al sistemului. | + | |
- | * **Hint**: Detalii despre variabila ''jiffies'' se găsesc în [[http://ocw.cs.pub.ro/courses/so2/laboratoare/lab06#timere|laboratorul 6]]. | + | |
- (**TODO 3**) Afișați cele patru structuri. | - (**TODO 3**) Afișați cele patru structuri. | ||
* Folosiți ''printk'' pentru a afișa cele două câmpuri ale acestora: ''pid'' și ''timestamp''. | * Folosiți ''printk'' pentru a afișa cele două câmpuri ale acestora: ''pid'' și ''timestamp''. | ||
Line 176: | Line 176: | ||
==== 4. [2p] Lucrul cu liste în kernel ==== | ==== 4. [2p] Lucrul cu liste în kernel ==== | ||
- | Intrați în directorul ''4-list/''. Parcurgeți conținutul fișierului ''list.c'' și observați comentariile marcate cu TODO. Procesul curent va adăuga cele patru structuri menționate anterior într-o listă. Lista va fi construită în funcția ''list_init'' și în funcția ''task_info_add_for_current''. Lista va fi afișată și ștearsă în funcția ''list_exit'' și în funcția ''task_info_purge_list''. | + | Intrați în directorul ''4-list/''. Parcurgeți conținutul fișierului ''list.c'' și observați comentariile marcate cu TODO. Procesul curent va adăuga cele patru structuri menționate anterior într-o listă. Lista va fi construită în funcția ''list_init'' și în funcția ''task_info_add_for_current''. Lista va fi afișată în funcția ''task_info_print_list'' și ștearsă în funcția ''task_info_purge_list'', ambele fiind apelate din ''list_exit''. |
- | - (**TODO 0**) Copiați de la exercițiul anterior (''3-memory/'') funcțiile și secțiunile indicate. | + | - (**TODO 1**) Completați funcția ''task_info_add_to_list'' pentru a aloca o structură ''struct task_info'' și a o adăuga la listă. Funcția este deja apelată de 4 ori din ''task_info_add_for_current''. |
- | - (**TODO 1**) Completați funcția ''task_info_add_to_list'' pentru a aloca o structură ''struct task_info'' și a o adăuga la listă. | + | |
- (**TODO 2**) Completați funcția ''task_info_purge_list'' pentru a șterge toate elementele din listă. | - (**TODO 2**) Completați funcția ''task_info_purge_list'' pentru a șterge toate elementele din listă. | ||
- | - Compilați modulul de kernel. Încărcați și descărcați modulul urmărind mesajele afișate de kernel. | + | - Compilați modulul de kernel și copiați-l pe mașina virtuală. Încărcați și descărcați modulul urmărind mesajele afișate de kernel. |
| | ||
<note tip> | <note tip> | ||
Revedeți secțiunea [[:so2:laboratoare:lab03#Liste|Liste]] din laborator. | Revedeți secțiunea [[:so2:laboratoare:lab03#Liste|Liste]] din laborator. | ||
- | Atunci când ștergeți elemente din listă, va trebui să folosiți apelul [[http://lxr.free-electrons.com/source/include/linux/list.h?v=4.9#L422|list_for_each_safe]]. Puteți folosi și apelul [[http://lxr.free-electrons.com/source/include/linux/list.h?v=4.9#L516|list_for_each_entry_safe]]. | + | Atunci când ștergeți elemente din listă, va trebui să folosiți apelul [[https://elixir.bootlin.com/linux/v4.15.7/source/include/linux/list.h#L436|list_for_each_safe]]. Puteți folosi și apelul [[https://elixir.bootlin.com/linux/v4.15.7/source/include/linux/list.h#L543|list_for_each_entry_safe]]. |
</note> | </note> | ||
Line 201: | Line 200: | ||
* Facilitatea de expirare este implementată deja în funcția ''task_info_remove_expired''. | * Facilitatea de expirare este implementată deja în funcția ''task_info_remove_expired''. | ||
| | ||
- | - (**TODO 0**) Copiați de la ''3-memory'' și/sau ''4-list'' secțiuni de cod acolo unde sunt indicate astfel. | ||
- (**TODO 1**) Implementați funcția ''task_info_find_pid'' așa cum este descris mai sus și în comentariul funcției. | - (**TODO 1**) Implementați funcția ''task_info_find_pid'' așa cum este descris mai sus și în comentariul funcției. | ||
- (**TODO 2**) Modificați un câmp al unui element din listă astfel încât acesta să nu expire. **Hint**: Trebuie să nu satisfacă o parte a condiției de expirare din funcția ''task_info_remove_expired''. | - (**TODO 2**) Modificați un câmp al unui element din listă astfel încât acesta să nu expire. **Hint**: Trebuie să nu satisfacă o parte a condiției de expirare din funcția ''task_info_remove_expired''. | ||
- | - Compilați, încărcați și descărcați modulul urmărind mesajele afișate. Va dura încărcarea modulului întrucât este prezent un "sleep" prin intermediul funcției ''schedule_timeout''. | + | - Compilați, copiați, încărcați și descărcați modulul urmărind mesajele afișate. Va dura încărcarea modulului întrucât este prezent un "sleep" prin intermediul funcției ''schedule_timeout''. |
<note tip> | <note tip> | ||
Line 213: | Line 211: | ||
Intrați în directorul ''6-list-sync/''. | Intrați în directorul ''6-list-sync/''. | ||
- | - Copiați fișierul ''5-list-full/list-full.c'' (rezolvarea anterioară) în ''6-list-sync/list-sync.c''. | + | - Fișierul ''6-list-sync/list-sync.c'' conține aceeași funcționalitate ca cea adăugată la exercițiul anterior. |
- | - Folosiți un spinlock sau un read-write lock pentru a sincroniza accesul la lista folosită. | + | - Folosiți un spinlock sau un read-write lock pentru a sincroniza accesul la lista folosită. Urmăriți comentariile marcate cu **TODO**. |
- | - Compilați, încărcați și descărcați modulul de kernel. | + | - Compilați, copiați, încărcați și descărcați modulul de kernel. |
<note tip> | <note tip> | ||
Line 229: | Line 227: | ||
Pentru exportarea funcțiilor de mai sus, din modulul de la ''6-list-sync/'' este nevoie de următorii pași: | Pentru exportarea funcțiilor de mai sus, din modulul de la ''6-list-sync/'' este nevoie de următorii pași: | ||
- Funcțiile nu trebuie să fie statice. | - Funcțiile nu trebuie să fie statice. | ||
- | - Folosiți macro-ul [[http://lxr.free-electrons.com/source/include/linux/export.h?v=4.9#L70|EXPORT_SYMBOL]] pentru a exporta simbolurile în kernel. De exemplu: ''EXPORT_SYMBOL(task_info_remove_expired);''. Macro-ul trebuie folosit pentru fiecare funcție, **după** definirea funcției. | + | - Se folosește macro-ul [[https://elixir.bootlin.com/linux/v4.15.7/source/include/linux/export.h#L77|EXPORT_SYMBOL]] pentru a exporta simbolurile în kernel. De exemplu: ''EXPORT_SYMBOL(task_info_remove_expired);''. Macro-ul trebuie folosit pentru fiecare funcție, **după** definirea funcției. |
- | - Anunțați modulul de test de prezența funcțiilor exportate. După ce ați compilat modulul de la ''6-list-sync/'', analizaţi fişierul ''6-list-sync/Module.symvers'', după care copiaţi-l în ''7-list-test/''. | + | |
- Eliminați din modulul de la ''6-list-sync/'' secvența cu evitarea expirării unui element al listei (se bate cap în cap cu ce face testul). | - Eliminați din modulul de la ''6-list-sync/'' secvența cu evitarea expirării unui element al listei (se bate cap în cap cu ce face testul). | ||
- Compilați și încărcați modulul de la ''6-list-sync/''. Odată încărcat va expune funcțiile exportate și vor putea fi folosite de modulul de test. Puteţi verifica acest lucru căutând numele funcţiilor în ''/proc/kallsyms'' înainte şi după încărcarea modulului. | - Compilați și încărcați modulul de la ''6-list-sync/''. Odată încărcat va expune funcțiile exportate și vor putea fi folosite de modulul de test. Puteţi verifica acest lucru căutând numele funcţiilor în ''/proc/kallsyms'' înainte şi după încărcarea modulului. |