This shows you the differences between two versions of the page.
so2:laboratoare:lab06:exercitii [2017/03/28 12:49] razvan.deaconescu [3. [2p] Controlare timer folosind ioctl] |
so2:laboratoare:lab06:exercitii [2018/03/28 09:17] (current) ionel.ghita [4. [1.5p] Operații blocante] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Laborator 6: Exerciții ====== | ====== Laborator 6: Exerciții ====== | ||
- | Pentru desfășurarea laboratorului pornim de la [[http://elf.cs.pub.ro/so2/res/laboratoare/lab06-tasks.zip|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 ''asgard''):<code bash> | + | ===== Pregătirea laboratorului ===== |
- | student@asgard:~$ cd so2/ | + | |
- | student@asgard:~/so2$ wget http://elf.cs.pub.ro/so2/res/laboratoare/lab06-tasks.zip | + | Pentru rezolvarea laboratorului, vom lucra în același director din care pornim mașina virtuală (''~/so2/linux/tools/labs''). |
- | student@asgard:~/so2$ unzip lab06-tasks.zip | + | |
- | student@asgard:~/so2$ tree lab06-tasks | + | Pașii de rezolvare sunt următorii: |
+ | * pregătirea scheletului de laborator | ||
+ | * compilarea modulelor de Kernel | ||
+ | * copierea modulelor pe mașina virtuală | ||
+ | * pornirea mașinii virtuale și testarea modulelor | ||
+ | |||
+ | ==== Pregătirea scheletului de laborator ==== | ||
+ | |||
+ | Scheletul de laborator este generat din sursele din directorul ''tools/labs/templates''. Putem genera scheletele pentru toate laboratoarele folosind următoarea comanda: | ||
+ | |||
+ | <code bash> | ||
+ | tools/labs $ make skels | ||
</code> | </code> | ||
- | În cadrul directorului ''lab06-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 ''asgard'') și apoi le vom testa pe [[:so2:resurse:masini-virtuale|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<code bash> | + | Pentru a genera scheletul pentru un singur laborator, vom folosi variabila de mediu ''LABS'': |
- | student@asgard:~/so2$ cp /path/to/module.ko ~/so2/qemu-so2/fsimg/root/modules/ | + | |
- | </code> 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<code bash> | + | <code bash> |
- | student@asgard:~/so2/qemu-so2$ make | + | tools/labs $ make clean |
+ | tools/labs $ LABS=<lab name> make skels | ||
+ | </code> | ||
+ | |||
+ | <note important> | ||
+ | Numele laboratorului curent este ''deferred_work''. | ||
+ | </note> | ||
+ | |||
+ | Similar, putem genera și scheletul pentru un singur exercițiu, atribuind valoarea ''<lab_name>/<task_name>'' variabilei ''LABS''. | ||
+ | |||
+ | <note> | ||
+ | Scheletul este generat în directorul ''tools/labs/skels''. | ||
+ | </note> | ||
+ | |||
+ | ==== Compilarea modulelor ==== | ||
+ | |||
+ | Comanda ''make build'' compilează toate modulele din directorul ''skels''. | ||
+ | |||
+ | <code bash> | ||
+ | student@eg106:~/so2/linux/tools/labs$ make build | ||
+ | echo "# autogenerated, do not edit " > skels/Kbuild | ||
+ | echo "ccflags-y += -Wno-unused-function -Wno-unused-label -Wno-unused-variable " >> skels/Kbuild | ||
+ | for i in ./deferred_work/6-kthread ./deferred_work/1-2-timer ./deferred_work/3-4-5-deferred/kernel; do echo "obj-m += $i/" >> skels/Kbuild; done | ||
+ | ... | ||
+ | </code> | ||
+ | |||
+ | ==== Copierea modulelor pe mașina virtuală ==== | ||
+ | |||
+ | Putem copia modulele generate pe mașina virtuală folosind target-ul ''copy'' al comenzii make, atunci când mașina virtuală este oprită. | ||
+ | |||
+ | <code bash> | ||
+ | student@eg106:~/so2/linux/tools/labs$ make copy | ||
+ | student@eg106:~/so2/linux/tools/labs$ make boot | ||
+ | </code> | ||
+ | |||
+ | Alternativ, putem copia fișierele prin ''scp'', pentru e evita repornirea mașinii virtuale. Pentru detalii despre folosirea interacțiunea prin rețea cu mașina virtuală citiți [[https://ocw.cs.pub.ro/courses/so2/resurse/masini-virtuale#interactiunea_cu_masina_virtuala|Interacțiunea cu mașina virtuală]]. | ||
+ | |||
+ | ==== Testarea modulelor ==== | ||
+ | |||
+ | Modulele generate sunt copiate pe mașina virtuală în directorul ''/home/root/skels/<lab_name>''. | ||
+ | |||
+ | <code bash> | ||
+ | root@qemux86:~/skels/deferred_work# ls | ||
+ | 1-2-timer 3-4-5-deferred 6-kthread | ||
+ | root@qemux86:~/skels/deferred_work# ls 1-2-timer/ | ||
+ | timer.ko | ||
</code> | </code> | ||
- | După pornirea mașinii virtuale QEMU vom putea folosi comenzi în fereastra QEMU pentru a încărca și descărca modulul de kernel:<code> | + | După pornirea mașinii virtuale QEMU vom putea folosi comenzi în fereastra QEMU (sau în ''minicom'') pentru a încărca și descărca modulul de kernel:<code> |
- | # insmod modules/module-name.ko | + | root@qemux86:~# insmod skels/<lab_name>/<task_name>/<module_name>.ko |
- | # rmmod module/module-name | + | root@qemux86:~# rmmod skels/<lab_name>/<task_name>/<module_name>.ko |
- | </code> unde ''module-name'' este numele modulului de kernel. | + | </code> |
<note> | <note> | ||
Line 41: | Line 96: | ||
===== [10.5p] Exerciții ===== | ===== [10.5p] Exerciții ===== | ||
+ | |||
+ | <note important> | ||
+ | Înainte de începerea rezolvării laboratorului, rulați comanda ''%%git pull --rebase%%'' in directorul ''~/so2/linux'', pentru a obține ultima versiune a scheletului de laborator. | ||
+ | </note> | ||
==== 1. [2p] Timer ==== | ==== 1. [2p] Timer ==== | ||
Line 46: | Line 105: | ||
Urmărim crearea unui modul simplu de kernel care să afișeze un mesaj la ''TIMER_TIMEOUT'' secunde de la încărcarea modulului în kernel. | Urmărim crearea unui modul simplu de kernel care să afișeze un mesaj la ''TIMER_TIMEOUT'' secunde de la încărcarea modulului în kernel. | ||
- | În directorul ''1-2-timer/'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab06-tasks.zip|arhiva de sarcini]] a laboratorului este scheletul de cod ''timer.c'' de unde să porniți pentru implementare. Urmăriți secțiunile marcate cu ''TODO 1'' în scheletul de laborator. | + | În directorul ''1-2-timer/'' este scheletul de cod ''timer.c'' de unde să porniți pentru implementare. Urmăriți secțiunile marcate cu ''TODO 1'' în scheletul de laborator. |
<note> | <note> | ||
Line 70: | Line 129: | ||
Ne propunem să afișăm informații despre procesul curent după **N** secunde de la primirea unei comezi de tipul ''ioctl'' apelată din user space. **N** este transmis ca paramentru prin ''ioctl''. | Ne propunem să afișăm informații despre procesul curent după **N** secunde de la primirea unei comezi de tipul ''ioctl'' apelată din user space. **N** este transmis ca paramentru prin ''ioctl''. | ||
- | Porniți de la fișierele din subdirectorul ''3-4-5-deferred/kernel/'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab06-tasks.zip|arhiva de sarcini]] a laboratorului. Urmăriți secțiunile marcate cu ''TODO 1'' în scheletul de laborator. | + | Porniți de la fișierele din subdirectorul ''3-4-5-deferred/kernel/''. Urmăriți secțiunile marcate cu ''TODO 1'' în scheletul de laborator. |
Va trebui să implementați următoarele operații ''ioctl''. | Va trebui să implementați următoarele operații ''ioctl''. | ||
Line 103: | Line 162: | ||
</note> | </note> | ||
- | Activați și dezactivați timer-ul prin apelul operațiilor ioctl din user-space. Utilizați programul ''3-4-5-deferred/user/test/'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab06-tasks.zip|arhiva de sarcini]]. Rulați programul pentru a testa planificarea și dezactivarea unui timer. Programul primește ca parametri în linie de comandă operația de tip ioctl și parametrii acesteia (dacă e cazul). | + | Activați și dezactivați timer-ul prin apelul operațiilor ioctl din user-space. Utilizați programul ''3-4-5-deferred/user/test/'' din scheletul laboratorului. Rulați programul pentru a testa planificarea și dezactivarea unui timer. Programul primește ca parametri în linie de comandă operația de tip ioctl și parametrii acesteia (dacă e cazul). |
Executabilul rezultat (''test'') trebuie să îl copiați pe mașina virtuală QEMU la fel ca modulul de kernel și să îl rulați pe mașina virtuală pentru a valida implementarea corectă a ''ioctl''. | Executabilul rezultat (''test'') trebuie să îl copiați pe mașina virtuală QEMU la fel ca modulul de kernel și să îl rulați pe mașina virtuală pentru a valida implementarea corectă a ''ioctl''. | ||
- | </note> | ||
<note tip> | <note tip> | ||
Line 135: | Line 193: | ||
Observați că programul cauzează eroare pentru că se apelează o funcție blocantă în context atomic (handler-ul de timer rulează în context amânabil/întrerupere). | Observați că programul cauzează eroare pentru că se apelează o funcție blocantă în context atomic (handler-ul de timer rulează în context amânabil/întrerupere). | ||
+ | <note tip> | ||
+ | Pentru a obține un pointer la datele private ale modulului (structura ''my_device_data''), puteți folosi macro-ul [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/timer.h#L143|from_timer]], | ||
+ | care este similar cu [[https://elixir.bootlin.com/linux/v4.15/source/tools/include/linux/kernel.h#L26|container_of]]. | ||
+ | <code c> | ||
+ | static void timer_handler(struct timer_list *tl) | ||
+ | { | ||
+ | struct my_device_data *data = from_timer(data, tl, timer); | ||
+ | ... | ||
+ | } | ||
+ | </code> | ||
+ | </note> | ||
==== 5. [1.5p] Workqueues ==== | ==== 5. [1.5p] Workqueues ==== | ||
Line 142: | Line 211: | ||
<note tip> | <note tip> | ||
- | Adăugați un câmp ''work'' de tipul ''struct work_struct'' în structura de dispozitiv. Inițializați acest câmp. Submiterea work-ului după **N** secunde o veți face din rutina de tratare a timer-ului, folosind funcția [[http://lxr.free-electrons.com/source/include/linux/workqueue.h?v=3.13#L555|schedule_work]]. Timer-ul va fi planificat să ruleze după **N** secunde, rutina de tratare a timer-ului va planifica work-ul și acesta va rula cât mai aproape de acel moment. **Nu** folosiți funcția [[http://lxr.free-electrons.com/source/include/linux/workqueue.h?v=3.13#L571|schedule_delayed_work_on]]. | + | Adăugați un câmp ''work'' de tipul ''struct work_struct'' în structura de dispozitiv. Inițializați acest câmp. Submiterea work-ului după **N** secunde o veți face din rutina de tratare a timer-ului, folosind funcția [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/workqueue.h#L533|schedule_work]]. Timer-ul va fi planificat să ruleze după **N** secunde, rutina de tratare a timer-ului va planifica work-ul și acesta va rula cât mai aproape de acel moment. **Nu** folosiți funcția [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/workqueue.h#L578|schedule_delayed_work_on]]. |
- | Folosiți **workqueue**-ul implicit (adică **nu** creați un workqueue, adică **nu** apelați [[http://lxr.free-electrons.com/source/include/linux/workqueue.h?v=3.13#L452|create_workqueue]]). | + | Folosiți **workqueue**-ul implicit (adică **nu** creați un workqueue, adică **nu** apelați [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/workqueue.h#L428|create_workqueue]]). |
</note> | </note> | ||
Line 155: | Line 224: | ||
Dorim să creăm un modul simplu în care să creăm un kernel thread cu ajutorul căruia să afișăm identificatorul procesului curent. | Dorim să creăm un modul simplu în care să creăm un kernel thread cu ajutorul căruia să afișăm identificatorul procesului curent. | ||
- | Porniți de la fișierele din subdirectorul ''6-kthread/'' din [[http://elf.cs.pub.ro/so2/res/laboratoare/lab06-tasks.zip|arhiva de sarcini]] a laboratorului și urmăriți TODO-urile din codul sursă. | + | Porniți de la fișierele din subdirectorul ''6-kthread/'' din scheletul laboratorului și urmăriți TODO-urile din codul sursă. |
Veți crea și veți porni kernel thread-ul la încărcarea modulului. | Veți crea și veți porni kernel thread-ul la încărcarea modulului. | ||
Line 161: | Line 230: | ||
<note tip> | <note tip> | ||
Pentru crearea unui thread aveți două opțiuni: | Pentru crearea unui thread aveți două opțiuni: | ||
- | * Apelați macro-ul [[http://lxr.free-electrons.com/source/include/linux/kthread.h?v=3.13#L22|kthread_run]]. | + | * Apelați macro-ul [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/kthread.h#L35|kthread_run]]. |
- | * Apelați macro-ul [[http://lxr.free-electrons.com/source/include/linux/kthread.h?v=3.13#L13|kthread_create]] care creează un thread suspendat și apoi apelați funcția [[http://lxr.free-electrons.com/source/kernel/sched/core.c?v=3.13#L1674|wake_up_process]] pentru rularea acestuia; așa cum se întâmplă în cadrul macro-ului [[http://lxr.free-electrons.com/source/include/linux/kthread.h?v=3.13#L22|kthread_run]]. | + | * Apelați macro-ul [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/kthread.h#L15|kthread_create]] care creează un thread suspendat și apoi apelați funcția [[https://elixir.bootlin.com/linux/v4.15/source/kernel/sched/core.c#L2137|wake_up_process]] pentru rularea acestuia; așa cum se întâmplă în cadrul macro-ului [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/kthread.h#L35|kthread_run]]. |
Parcurgeți secțiunea [[:so2:laboratoare:lab06#Kernel threads|Kernel threads]] din laborator. | Parcurgeți secțiunea [[:so2:laboratoare:lab06#Kernel threads|Kernel threads]] din laborator. | ||
Line 207: | Line 276: | ||
</note> | </note> | ||
- | În implementare porniți de la codul din subdirectorul ''3-4-5-deferred/''. Urmăriți secțiunile marcate cu ''TODO karma'' în scheletul de laborator. | + | În implementare porniți de la codul din subdirectorul ''3-4-5-deferred/''. Urmăriți secțiunile marcate cu ''TODO 4'' în scheletul de laborator. |
<note tip> | <note tip> |