Differences

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

Link to this comparison view

so2:laboratoare:lab06:exercitii [2016/03/30 13:04]
razvan.deaconescu [5. [1.5p] Workqueues]
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ăș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:​~/​so2tree 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-vm/fsimg/​root/​modules+ 
-</​code> ​unde ''​/​path/​to/​module.ko'' ​este calea către fișierul obiect aferent modulului de kernelApoi vom porni, din directorul ''​~/so2/qemu-vm/'', ​mașina virtuală ​QEMU folosind comanda<code bash> +<code bash> 
-student@asgard:~/so2/qemu-vm$ 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 virtualePentru 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>​
 Pentru dezvoltarea laboratorului,​ este recomandat să folosim trei terminale sau, mai bine, trei tab-uri de terminal. Pentru a deschide un nou tab de terminal folosim combinația de taste ''​Ctrl+Shift+t''​. Cele trei tab-uri de terminal îndeplinesc următoarele roluri: Pentru dezvoltarea laboratorului,​ este recomandat să folosim trei terminale sau, mai bine, trei tab-uri de terminal. Pentru a deschide un nou tab de terminal folosim combinația de taste ''​Ctrl+Shift+t''​. Cele trei tab-uri de terminal îndeplinesc următoarele roluri:
   - În primul tab de terminal dezvoltăm modulul de kernel: editare, compilare, copiere în directorul dedicat pentru mașina virtuală QEMU. Lucrăm în directorul aferent rezultat în urma decomprimării arhivei de sarcini a laboratorului.   - În primul tab de terminal dezvoltăm modulul de kernel: editare, compilare, copiere în directorul dedicat pentru mașina virtuală QEMU. Lucrăm în directorul aferent rezultat în urma decomprimării arhivei de sarcini a laboratorului.
-  - În al doilea tab de terminal pornim mașina virtuală QEMU și apoi testăm modulul de kernel: încărcare/​descărcare modul, rulare teste. Lucrăm în directorul aferent mașinii virtuale: ''​~/​so2/​qemu-vm/''​. +  - În al doilea tab de terminal pornim mașina virtuală QEMU și apoi testăm modulul de kernel: încărcare/​descărcare modul, rulare teste. Lucrăm în directorul aferent mașinii virtuale: ''​~/​so2/​qemu-so2/''​. 
-  - În al treilea tab de terminal pornim un server UDP care să primească [[:​so2:​laboratoare:​lab02#​netconsole|mesajele de netconsole]]. Nu contează în ce director ne aflăm. Folosim comanda<​code bash>+  - În al treilea tab de terminal pornim ​[[:​so2:​laboratoare:​lab02#​minicom|minicom]] sau un server UDP care să primească [[:​so2:​laboratoare:​lab02#​netconsole|mesajele de netconsole]]. Nu contează în ce director ne aflăm. Folosim comanda<​code bash>
 student@asgard:​~$ netcat -lup 6666 student@asgard:​~$ netcat -lup 6666
 </​code>​ </​code>​
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).
- +
-<note tip> +
-**Pe stațiile din sala de laborator EG106**, pentru a compila codul sursă de user space folosiți compilatorul ''​gcc-5''​. Pentru aceasta rulați comanda:<​code>​ +
-/​usr/​bin/​gcc-5 -m32 -static -Wall -g -o test test.c +
-</​code>​+
  
 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 140: 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 147: 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 o veți face din rutina de tratare a timer-ului, după **N** secunde, folosind funcția [[http://lxr.free-electrons.com/​source/​include/​linux/​workqueue.h?v=3.13#L555|schedule_work]]. **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 160: 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 166: 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 190: Line 254:
 Dorim să ne acomodăm cu modul de sincronizare între o rutină amânabilă (un timer) și contextul proces. Dorim să ne acomodăm cu modul de sincronizare între o rutină amânabilă (un timer) și contextul proces.
  
-Pentru aceasta, dorim să colectăm informații periodice despre procesul idle (''​swapper/​0''​) pe care să le scriem într-un buffer. Informațiile le vom colecta în momentul în care este invocată rutina de tratare a timer-ului. Informația colectată va fi timpul ​de rulare total de până atunci ​al procesului idle. Acest timp se găsește în construcția ​''​%%current->​cputime_expires.sum_exec_runtime%%'' ​de tipul unsigned long long.+Pentru aceasta, dorim să colectăm informații periodice despre procesul idle (''​swapper/​0''​), procesul cu PID-ul ''​0'', ​pe care să le scriem într-un buffer. Informațiile le vom colecta în momentul în care este invocată rutina de tratare a timer-ului. Informația colectată va fi numărul ​de schimbări involuntare ​de până atunci ​ale procesului idle. Acest număr este dat de câmpul ''​nivcsw''​ (de tipul ''​unsigned long''​) din cadrul structurii ''​task_struct'',​ adică folosind ​''​%%current->​nivcsw%%''​.
  
-În rutina de timer, dacă la orice moment de timp procesul întrerupt este procesul idle (adică are PID-ul ''​0''​),​ se stochează într-un buffer o nouă valoarea a timpului ​de rulare. Dacă buffer-ul este plin nu se stochează nimic.+În rutina de timer, dacă la orice moment de timp procesul întrerupt este procesul idle (adică are PID-ul ''​0''​),​ se stochează într-un buffer o nouă valoarea a numărului ​de schimbări involuntare. Dacă buffer-ul este plin nu se stochează nimic. 
 + 
 +<note tip> 
 +Citirea datelor o veți face la fiecare secundă dacă flag-ul dispozitivului este inițializat la valoarea ''​TIMER_TYPE_ACCT''​. Pentru inițializarea flag-ului veți comanda din user space dispozitivul (prin ''​ioctl''​) folosind executabilul de test, după încărcarea modulului și crearea dispozitivului,​ astfel:<​code>​ 
 +./test t 
 +</​code>​ 
 +</​note>​
  
 În rutina de read a modulului se transferă în user space datele din buffer și se resetează buffer-ul. În rutina de read a modulului se transferă în user space datele din buffer și se resetează buffer-ul.
Line 198: Line 268:
 Folosiți un spinlock și operațiile corespunzătoare pentru a asigura accesul corect la buffer din rutina de timer și din rutina de read. Folosiți un spinlock și operațiile corespunzătoare pentru a asigura accesul corect la buffer din rutina de timer și din rutina de read.
  
-În implementare porniți de la codul din subdirectorul ''​3-4-5-deferred/''​.+<note tip> 
 +Veți folosi un buffer temporar pentru a copia datele în user space. Nu puteți copia date în user space cu un spinlock deținut; rutina ''​copy_to_user''​ poate fi blocantă. 
 + 
 +Spinlock-ul,​ bufferul și bufferul temporar folosit pentru copiere sunt definite în structura dispozitivului. 
 + 
 +O parte din rutina de read este implementată;​ trebuie să implementați partea în care se face sincronizarea. 
 +</​note>​ 
 + 
 +Î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> 
 +Pentru a testa modulul de kernel space puteți citi date din dispozitiv folosind comanda<​code>​ 
 +od -s /​dev/​deferred 
 +</​code>​ 
 +</​note>​ 
 + 
 +==== [1 karma] Citire date din user space folosind read ==== 
 + 
 +Actualizați modulul de test din user space (''​user/​test.c''​) ca să ofere opțiunea ''​r''​. În momentul în care se transmite opțiunea către modulul de test acesta citește conținutul buffer-ului din kernel space folosind apelul ''​read()''​ și afișează în format zecimal (''​%lu''​) rezultatele primite (vector de ''​unsigned long''​). 
 ==== Soluții ==== ==== Soluții ====
  
 [[http://​elf.cs.pub.ro/​so2/​res/​laboratoare/​lab06-sol.zip|Soluții exerciții laborator 6]] [[http://​elf.cs.pub.ro/​so2/​res/​laboratoare/​lab06-sol.zip|Soluții exerciții laborator 6]]
  
so2/laboratoare/lab06/exercitii.1459332244.txt.gz · Last modified: 2016/03/30 13:04 by razvan.deaconescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0