Differences

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

Link to this comparison view

so2:laboratoare:lab04 [2016/03/12 19:01]
sorin.baltateanu [read și write]
so2:laboratoare:lab04 [2018/03/13 14:10] (current)
ionel.ghita
Line 26: Line 26:
 ===== Concepte generale ===== ===== Concepte generale =====
  
-În UNIX, dispozitivele hardware sunt accesate de utilizator prin intermediul fișierelor speciale [[http://​en.wikipedia.org/​wiki/​Device_file_system |  de tip dispozitiv]] (device). Aceste fișiere sunt grupate în directorul ''/​dev'',​ iar apelurile de sistem ''​open'',​ ''​read'',​ ''​write'',​ ''​close'',​ ''​seek'',​ ''​mmap''​ etc. sunt redirecționate de sistemul de operare către device driverul asociat cu dispozitivul fizic. [[http://​en.wikipedia.org/​wiki/​Device_driver |  Device driverul]] este o componentă a nucleului (de obicei un modul) care interacționează cu un dispozitiv hardware.+În UNIX, dispozitivele hardware sunt accesate de utilizator prin intermediul fișierelor speciale [[http://​en.wikipedia.org/​wiki/​Device_file_system |  de tip dispozitiv]] (device). Aceste fișiere sunt grupate în directorul ''/​dev'',​ iar apelurile de sistem ''​open'',​ ''​read'',​ ''​write'',​ ''​close'',​ ''​lseek'',​ ''​mmap''​ etc. sunt redirecționate de sistemul de operare către device driverul asociat cu dispozitivul fizic. [[http://​en.wikipedia.org/​wiki/​Device_driver |  Device driverul]] este o componentă a nucleului (de obicei un modul) care interacționează cu un dispozitiv hardware.
  
 În lumea UNIX există două categorii de fișiere dispozitiv și, implicit, device drivere: de tip **caracter** și de tip **bloc**. Această împărțire este făcută după viteza, volumul și modul de organizare a datelor ce trebuie transferate de la dispozitiv către sistem și invers. În prima categorie intră dispozitivele lente, care gestionează un volum mic de date, iar accesul la date nu necesită operații de căutare (seek) frecvente. Exemple sunt dispozitive cum ar fi tastatura, mouse-ul, porturile seriale, placa de sunet, joystick-ul. În general operațiile cu aceste dispozitive (citire, scriere) se realizează secvențial,​ octet cu octet. Cea de-a doua categorie cuprinde dispozitive la care volumul de date este mare, datele sunt organizate pe blocuri și operațiile de căutare (seek) sunt frecvente. Exemple de dispozitive ce intră în această categorie sunt hard disk-urile, cdrom-urile,​ ram discurile, unitățile de bandă magnetică. În cazul acestor dispozitive,​ citirea și scrierea se realizează la nivel de **bloc** de date. În lumea UNIX există două categorii de fișiere dispozitiv și, implicit, device drivere: de tip **caracter** și de tip **bloc**. Această împărțire este făcută după viteza, volumul și modul de organizare a datelor ce trebuie transferate de la dispozitiv către sistem și invers. În prima categorie intră dispozitivele lente, care gestionează un volum mic de date, iar accesul la date nu necesită operații de căutare (seek) frecvente. Exemple sunt dispozitive cum ar fi tastatura, mouse-ul, porturile seriale, placa de sunet, joystick-ul. În general operațiile cu aceste dispozitive (citire, scriere) se realizează secvențial,​ octet cu octet. Cea de-a doua categorie cuprinde dispozitive la care volumul de date este mare, datele sunt organizate pe blocuri și operațiile de căutare (seek) sunt frecvente. Exemple de dispozitive ce intră în această categorie sunt hard disk-urile, cdrom-urile,​ ram discurile, unitățile de bandă magnetică. În cazul acestor dispozitive,​ citirea și scrierea se realizează la nivel de **bloc** de date.
Line 64: Line 64:
 ===== Structuri de date importante pentru un dispozitiv de tip caracter ===== ===== Structuri de date importante pentru un dispozitiv de tip caracter =====
  
-În kernel, un dispozitiv de tip caracter este reprezentat de structura [[http://lxr.free-electrons.com/​source/​include/​linux/​cdev.h?v=3.13#L12 |  cdev]], structură folosită la înregistrarea acestuia în sistem.+În kernel, un dispozitiv de tip caracter este reprezentat de structura [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​cdev.h#​L14 |  cdev]], structură folosită la înregistrarea acestuia în sistem.
  
 Majoritatea operațiilor cu drivere folosesc trei structuri importante: [[#​Structura file_operations|struct file_operations]],​ [[#​Structurile inode și file|struct file]] și [[#​Structurile inode și file|struct inode]]. Majoritatea operațiilor cu drivere folosesc trei structuri importante: [[#​Structura file_operations|struct file_operations]],​ [[#​Structurile inode și file|struct file]] și [[#​Structurile inode și file|struct inode]].
Line 70: Line 70:
 ==== Structura file_operations ==== ==== Structura file_operations ====
  
-După cum s-a precizat, device driverele de tip caracter primesc nealterate apelurile de sistem efectuate de utilizatori asupra fișierelor de tip dispozitiv. În consecință,​ pentru implementarea unui device driver vor trebui implementate apelurile de sistem de lucru cu fișiere: ''​open'',​ ''​close'',​ ''​read'',​ ''​write'',​ ''​lseek'',​ ''​mmap''​ etc. Aceste operații sunt descrise în câmpuri ale structurii [[http://lxr.free-electrons.com/​source/​include/​linux/​fs.h?v=3.13#L1521|file_operations]] ​+După cum s-a precizat, device driverele de tip caracter primesc nealterate apelurile de sistem efectuate de utilizatori asupra fișierelor de tip dispozitiv. În consecință,​ pentru implementarea unui device driver vor trebui implementate apelurile de sistem de lucru cu fișiere: ''​open'',​ ''​close'',​ ''​read'',​ ''​write'',​ ''​lseek'',​ ''​mmap''​ etc. Aceste operații sunt descrise în câmpuri ale structurii [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​fs.h#​L1692|file_operations]] ​
 ((struct file_operations - aici am prezentat doar o parte din operațiile din structură, cele care se implementează în mod uzual de către device drivere)): ((struct file_operations - aici am prezentat doar o parte din operațiile din structură, cele care se implementează în mod uzual de către device drivere)):
  
Line 108: Line 108:
 Un inode reprezintă un fișier din punctul de vedere al sistemului de fișiere. Atribute ale unui inode sunt dimensiunea,​ drepturile, timpii asociați fișierului. Un inode identifică în mod unic un fișier într-un sistem de fișiere((nume fisier - după cum ați observat, la atributele inode-ului nu am enumerat și numele. Aceasta pentru că în UNIX un fișier poate avea mai multe nume, datorită link-urilor soft sau hard)). ​ Un inode reprezintă un fișier din punctul de vedere al sistemului de fișiere. Atribute ale unui inode sunt dimensiunea,​ drepturile, timpii asociați fișierului. Un inode identifică în mod unic un fișier într-un sistem de fișiere((nume fisier - după cum ați observat, la atributele inode-ului nu am enumerat și numele. Aceasta pentru că în UNIX un fișier poate avea mai multe nume, datorită link-urilor soft sau hard)). ​
  
-[[http://lxr.free-electrons.com/​source/​include/​linux/​fs.h?v=3.13#L772  Structura file]] reprezintă tot un fișier, dar mai aproape de punctul de vedere al utilizatorului. Dintre atributele [[http://lxr.free-electrons.com/​source/​include/​linux/​fs.h?v=3.13#L772  structurii file]] enumerăm: inode-ul, numele fișierului,​ atributele de deschidere ale fișierului,​ poziția în fișier. Toate fișierele deschise la un moment dat au asociate o structură [[http://lxr.free-electrons.com/​source/​include/​linux/​fs.h?v=3.13#L772  file]].+[[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​fs.h#​L852 | Structura file]] reprezintă tot un fișier, dar mai aproape de punctul de vedere al utilizatorului ​care a deschis fișierul, deci conține atribute care descriu starea unui fișier deschis. Dintre atributele [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​fs.h#​L852 | structurii file]] enumerăm: inode-ul, numele fișierului,​ atributele de deschidere ale fișierului,​ poziția ​curentă ​în fișier. Toate fișierele deschise la un moment dat au asociate o structură [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​fs.h#​L852 | file]], care sunt stocate într-o tabelă pentru fiecare proces. File descriptor-ul este un index în această tabelă.
  
 Pentru a înțelege diferențele dintre ''​inode''​ și ''​file'',​ vom folosi o analogie din programarea orientată pe obiecte: dacă vom considera un inode o clasă, atunci file-urile sunt obiecte, adică instanțe ale clasei inode. Inode-ul reprezintă imaginea statică a fișierului (**inode-ul nu are stare**), pe când **file reprezintă imaginea dinamică** a fișierului (file-ul are stare). Pentru a înțelege diferențele dintre ''​inode''​ și ''​file'',​ vom folosi o analogie din programarea orientată pe obiecte: dacă vom considera un inode o clasă, atunci file-urile sunt obiecte, adică instanțe ale clasei inode. Inode-ul reprezintă imaginea statică a fișierului (**inode-ul nu are stare**), pe când **file reprezintă imaginea dinamică** a fișierului (file-ul are stare).
Line 114: Line 114:
 Revenind la device drivere, cele două entități au aproape întotdeauna modalități standard de folosire: inode-ul se folosește pentru a determina majorul și minorul device-ului asupra căruia se face operația, iar file-ul se folosește pentru a determina flag-urile cu care a fost deschis fișierul, dar și pentru a memora și accesa (mai târziu) date private. Revenind la device drivere, cele două entități au aproape întotdeauna modalități standard de folosire: inode-ul se folosește pentru a determina majorul și minorul device-ului asupra căruia se face operația, iar file-ul se folosește pentru a determina flag-urile cu care a fost deschis fișierul, dar și pentru a memora și accesa (mai târziu) date private.
  
-[[http://lxr.free-electrons.com/​source/​include/​linux/​fs.h?v=3.13#L772 |  Structura file]] conține, printre multe câmpuri, și:+[[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​fs.h#​L852 |  Structura file]] conține, printre multe câmpuri, și:
   *''​f_mode'',​ care specifică permisiunile pentru citire (''​FMODE_READ''​) sau scriere (''​FMODE_WRITE''​);​   *''​f_mode'',​ care specifică permisiunile pentru citire (''​FMODE_READ''​) sau scriere (''​FMODE_WRITE''​);​
-  *''​f_flags'',​ care specifică [[http://lxr.free-electrons.com/​source/​include/​uapi/​asm-generic/​fcntl.h?​v=3.13 ​|  flag-urile de deschidere]] a fișierului (''​O_RDONLY'',​ ''​O_NONBLOCK'',​ ''​O_SYNC'',​ ''​O_APPEND'',​ ''​O_TRUNC''​ etc.); +  *''​f_flags'',​ care specifică [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​uapi/​asm-generic/​fcntl.h#L19 |  flag-urile de deschidere]] a fișierului (''​O_RDONLY'',​ ''​O_NONBLOCK'',​ ''​O_SYNC'',​ ''​O_APPEND'',​ ''​O_TRUNC''​ etc.); 
-  *''​f_op'',​ care specifica operațiile asociate fișierului (pointer către structura [[http://lxr.free-electrons.com/​source/​include/​linux/​fs.h?v=3.13#L1521  file_operations]]);​+  *''​f_op'',​ care specifica operațiile asociate fișierului (pointer către structura [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​fs.h#​L1692 | file_operations]]);​
   *''​private_data'',​ un pointer care poate fi folosit de programator pentru a păstra date specifice dispozitivului;​ pointerul va fi inițializat la adresa unei zone de memorie alocate de programator.   *''​private_data'',​ un pointer care poate fi folosit de programator pentru a păstra date specifice dispozitivului;​ pointerul va fi inițializat la adresa unei zone de memorie alocate de programator.
   *''​f_pos'',​ offsetul în cadrul fișierului   *''​f_pos'',​ offsetul în cadrul fișierului
  
-[[http://lxr.free-electrons.com/​source/​include/​linux/​fs.h?v=3.13#L524  Structura inode]] conține, printre multe informații,​ un câmp ''​i_cdev'',​ care este un pointer către [[http://lxr.free-electrons.com/​source/​include/​linux/​cdev.h?v=3.13#L12  structura care definește dispozitivul]] de tip caracter (atunci când inode-ul corespunde unui dispozitiv de tip caracter).+[[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​fs.h#​L570 | Structura inode]] conține, printre multe informații,​ un câmp ''​i_cdev'',​ care este un pointer către [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​cdev.h#​L14 | structura care definește dispozitivul]] de tip caracter (atunci când inode-ul corespunde unui dispozitiv de tip caracter).
  
 ==== Implementarea operațiilor ==== ==== Implementarea operațiilor ====
  
-Pentru implementarea unui device driver, se recomandă crearea unei structuri care să conțină informații despre dispozitivul dat, informații utilizate în cadrul modulului. În cazul unui driver pentru un dispozitiv de tip caracter, structura va conține un câmp de tipul [[http://lxr.free-electrons.com/​source/​include/​linux/​cdev.h?v=3.13#L12 |  struct cdev]] pentru a referi dispozitivul. Exemplul de mai jos folosește în acest sens structura ''​struct my_device_data'':​+Pentru implementarea unui device driver, se recomandă crearea unei structuri care să conțină informații despre dispozitivul dat, informații utilizate în cadrul modulului. În cazul unui driver pentru un dispozitiv de tip caracter, structura va conține un câmp de tipul [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​cdev.h#​L14 |  struct cdev ]] pentru a referi dispozitivul. Exemplul de mai jos folosește în acest sens structura ''​struct my_device_data'':​
  
 <code c> <code c>
Line 154: Line 154:
 </​code>​ </​code>​
  
-O structură de tipul ''​my_device_data''​ va conține datele asociate unui dispozitiv. Câmpul ''​cdev''​ (de tipul [[http://lxr.free-electrons.com/​source/​include/​linux/​cdev.h?v=3.13#L12 |  struct cdev]]) reprezintă un dispozitiv de tip caracter și este folosit la înregistrarea acestuia în sistem și identificarea dispozitivului. Pointer-ul către membrul ''​cdev''​ se poate afla cu ajutorul câmpului ''​i_cdev''​ al [[http://lxr.free-electrons.com/​source/​include/​linux/​fs.h?v=3.13#L524  structurii inode]] (cu ajutorul macro-ului [[http://lxr.free-electrons.com/​source/​include/​linux/​kernel.h?v=3.13#L791 |  container_of]]). În câmpul ''​private_data''​ al [[http://lxr.free-electrons.com/​source/​include/​linux/​fs.h?v=3.13#L772 |  structurii file]] se pot memora informații la ''​open''​ care apoi sunt disponibile în rutinele ''​read'',​ ''​write'',​ ''​release''​ etc.+O structură de tipul ''​my_device_data''​ va conține datele asociate unui dispozitiv. Câmpul ''​cdev''​ (de tipul [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​cdev.h#​L14 |  struct cdev]]) reprezintă un dispozitiv de tip caracter și este folosit la înregistrarea acestuia în sistem și identificarea dispozitivului. Pointer-ul către membrul ''​cdev''​ se poate afla cu ajutorul câmpului ''​i_cdev''​ al [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​fs.h#​L570 | structurii inode]] (cu ajutorul macro-ului [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​kernel.h#​L922 |  container_of]]). În câmpul ''​private_data''​ al [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​fs.h#​L852 |  structurii file]] se pot memora informații la ''​open''​ care apoi sunt disponibile în rutinele ''​read'',​ ''​write'',​ ''​release''​ etc.
  
 ===== Înregistrarea și deînregistrarea dispozitivelor de tip caracter ===== ===== Înregistrarea și deînregistrarea dispozitivelor de tip caracter =====
  
-Înregistrarea/​deînregistrarea unui dispozitiv se realizează prin specificarea majorului și minorului acestuia. Tipul ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​types.h?v=3.13#L15|dev_t]]''​ este folosit pentru a păstra identificatorii unui dispozitiv (atât majorul, cât și minorul) și se poate obține cu ajutorul macro-ului [[http://lxr.free-electrons.com/​source/​include/​linux/​kdev_t.h?v=3.13#L11|MKDEV]].+Înregistrarea/​deînregistrarea unui dispozitiv se realizează prin specificarea majorului și minorului acestuia. Tipul ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​types.h#​L16|dev_t]]''​ este folosit pentru a păstra identificatorii unui dispozitiv (atât majorul, cât și minorul) și se poate obține cu ajutorul macro-ului [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​kdev_t.h#​L12|MKDEV]].
  
-Pentru alocarea și dezalocarea statică a identificatorilor unui dispozitiv, se folosesc funcțiile [[http://lxr.free-electrons.com/​source/​fs/​char_dev.c?v=3.13#L196|register_chrdev_region]],​ respectiv [[http://lxr.free-electrons.com/​source/​fs/​char_dev.c?v=3.13#L167|unregister_chrdev_region]]:​+Pentru alocarea și dezalocarea statică a identificatorilor unui dispozitiv, se folosesc funcțiile [[https://elixir.bootlin.com/​linux/​v4.15/​source/​fs/​char_dev.c#​L195|register_chrdev_region]],​ respectiv [[https://elixir.bootlin.com/​linux/​v4.15/​source/​fs/​char_dev.c#​L306|unregister_chrdev_region]]:​
  
 <code c> <code c>
Line 169: Line 169:
 </​code>​ </​code>​
  
-Este recomandat ca identificatorii de dispozitiv să fie alocați dinamic cu funcția [[http://lxr.free-electrons.com/​source/​fs/​char_dev.c?v=3.13#L222|alloc_chrdev_region]].+Este recomandat ca identificatorii de dispozitiv să fie alocați dinamic cu funcția [[https://elixir.bootlin.com/​linux/​v4.15/​source/​fs/​char_dev.c#​L229|alloc_chrdev_region]].
  
 Secvența de mai jos rezervă ''​my_minor_count''​ dispozitive,​ începând de la dispozitivul cu majorul ''​my_major''​ și minorul ''​my_first_minor''​ (dacă se depășește valoare maximă pentru minor, se trece la următorul major): Secvența de mai jos rezervă ''​my_minor_count''​ dispozitive,​ începând de la dispozitivul cu majorul ''​my_major''​ și minorul ''​my_first_minor''​ (dacă se depășește valoare maximă pentru minor, se trece la următorul major):
Line 185: Line 185:
     }     }
     //...     //...
-</​code>​((my_device_driver - "​my_device_driver"​ este numele device-ului asociat cu intervalul de identificatori și care va apărea în ''/​proc/​devices''​))+</​code>​((my_device_driver ​-- "​my_device_driver"​ este numele device-ului asociat cu intervalul de identificatori și care va apărea în ''/​proc/​devices''​))
  
-După atribuirea identificatorilor,​ dispozitivul de tip caracter va trebui inițializat ([[http://lxr.free-electrons.com/​source/​fs/​char_dev.c?v=3.13#L553|cdev_init]]) și va trebui informat nucleul de existența lui ([[http://lxr.free-electrons.com/​source/​fs/​char_dev.c?v=3.13#L465|cdev_add]]). Funcția [[http://lxr.free-electrons.com/​source/​fs/​char_dev.c?v=3.13#L465|cdev_add]] trebuie apelată doar după ce dispozitivul este pregătit sa primească apeluri. Eliminarea unui dispozitiv se realizează folosind funcția [[http://lxr.free-electrons.com/​source/​fs/​char_dev.c?v=3.13#L497|cdev_del]].+După atribuirea identificatorilor,​ dispozitivul de tip caracter va trebui inițializat ([[ https://elixir.bootlin.com/​linux/​v4.15/​source/​fs/​char_dev.c#​L644 | cdev_init]]) și va trebui informat nucleul de existența lui ([[ https://elixir.bootlin.com/​linux/​v4.15/​source/​fs/​char_dev.c#​L473 | cdev_add]]). Funcția [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​fs/​char_dev.c#​L473| cdev_add]] trebuie apelată doar după ce dispozitivul este pregătit sa primească apeluri. Eliminarea unui dispozitiv se realizează folosind funcția [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​fs/​char_dev.c#​L584 |cdev_del]].
  
 <code c> <code c>
Line 276: Line 276:
 Toate macro-urile/​funcțiile întorc 0 în caz de succes și altă valoare în caz de eroare și au următoarele roluri: Toate macro-urile/​funcțiile întorc 0 în caz de succes și altă valoare în caz de eroare și au următoarele roluri:
  
-  *[[http://lxr.free-electrons.com/​source/​include/​asm-generic/​uaccess.h?v=3.13#L164 |  put_user]] pune în user-space la adresa ''​address''​ valoarea ''​val'';​ tipul poate fi unul pe 8, 16, 32, 64 de biți (tipul maxim suportat depinde de platforma hardware);​ +  * [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​uaccess.h#​L77 |  put_user]] pune în user-space la adresa ''​address''​ valoarea ''​val'';​ tipul poate fi unul pe 8, 16, 32, 64 de biți (tipul maxim suportat depinde de platforma hardware);​ 
-  *[[http://lxr.free-electrons.com/​source/​include/​asm-generic/​uaccess.h?v=3.13#L186 |  get_user]] analog cu funcția precedentă,​ numai că ''​val''​ va fi setată la o valoare identică cu valoarea de la adresa user-space dată prin ''​address'';​ +  * [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​uaccess.h#​L139 |  get_user]] analog cu funcția precedentă,​ numai că ''​val''​ va fi setată la o valoare identică cu valoarea de la adresa user-space dată prin ''​address'';​ 
-  *[[http://lxr.free-electrons.com/​source/​include/​asm-generic/uaccess.h?v=3.13#L265 |  copy_to_user]] copiază din kernel-space de la adresa referită de ''​from''​ în user-space la adresa referită de ''​to'',​ ''​size''​ octeți; +  * [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/uaccess.h#L152 |  copy_to_user]] copiază din kernel-space de la adresa referită de ''​from''​ în user-space la adresa referită de ''​to'',​ ''​size''​ octeți; 
-  *[[http://lxr.free-electrons.com/​source/​include/​asm-generic/uaccess.h?v=3.13#L255 |  copy_from_user]] copiază din user-space de la adresa referită de ''​from''​ în kernel-space la adresa referită de ''​to'',​ size octeți.+  * [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/uaccess.h#L144 |  copy_from_user]] copiază din user-space de la adresa referită de ''​from''​ în kernel-space la adresa referită de ''​to'',​ size octeți.
  
 O secțiune uzuală de cod care lucrează cu aceste funcții este: O secțiune uzuală de cod care lucrează cu aceste funcții este:
Line 286: Line 286:
 #include <​asm/​uaccess.h>​ #include <​asm/​uaccess.h>​
  
 +/*
 + * Copy at most size bytes to user space.
 + * Return ''​0''​ on success and some other value on error.
 + */
 if (copy_to_user(user_buffer,​ kernel_buffer,​ size)) if (copy_to_user(user_buffer,​ kernel_buffer,​ size))
     return -EFAULT;     return -EFAULT;
Line 334: Line 338:
 ==== read și write ==== ==== read și write ====
  
-Funcțiile ''​read''​ și ''​write''​ transferă date între dispozitiv și user-space: funcția ''​read''​ citește datele de la dispozitiv și le transferă în user-space, în timp ce ''​write''​ citește datele din user-space și le scrie pe dispozitiv. Buffer-ul primit ca parametru reprezintă un pointer în user-space, motiv pentru care este necesară folosirea funcțiilor [[http://lxr.free-electrons.com/​source/​include/​asm-generic/uaccess.h?v=3.13#L255 |  copy_to_user]] sau [[http://lxr.free-electrons.com/​source/​include/​asm-generic/uaccess.h?v=3.13#L265 |  copy_from_user]]. ​+Funcțiile ''​read''​ și ''​write''​ transferă date între dispozitiv și user-space: funcția ''​read''​ citește datele de la dispozitiv și le transferă în user-space, în timp ce ''​write''​ citește datele din user-space și le scrie pe dispozitiv. Buffer-ul primit ca parametru reprezintă un pointer în user-space, motiv pentru care este necesară folosirea funcțiilor [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/uaccess.h#L152 |  copy_to_user]] sau [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/uaccess.h#L144 |  copy_from_user]]. ​
  
-Valoarea întoarsă de ''​read''​ sau write poate fi:+Valoarea întoarsă de ''​read''​ sau ''​write'' ​poate fi:
   * numărul de bytes transferați;​ dacă valoarea întoarsă este mai mică decât parametrul ''​size''​ (numărul de octeți ceruți), atunci înseamnă că s-a realizat un transfer parțial. De cele mai multe ori, aplicația din user-space apelează din nou funcția corespunzătoare apelului de sistem (read sau write) până când se transferă numărul de date cerut.   * numărul de bytes transferați;​ dacă valoarea întoarsă este mai mică decât parametrul ''​size''​ (numărul de octeți ceruți), atunci înseamnă că s-a realizat un transfer parțial. De cele mai multe ori, aplicația din user-space apelează din nou funcția corespunzătoare apelului de sistem (read sau write) până când se transferă numărul de date cerut.
   * ''​0''​ pentru marcarea sfârșitului fișierului în cazul lui ''​read'';​ dacă ''​write''​ întoarce valoarea ''​0''​ atunci înseamnă că nici un byte nu a fost scris și că nu s-a produs nici o eroare; în acest caz, aplicația din user-space reîncearcă de cele mai multe ori scrierea.   * ''​0''​ pentru marcarea sfârșitului fișierului în cazul lui ''​read'';​ dacă ''​write''​ întoarce valoarea ''​0''​ atunci înseamnă că nici un byte nu a fost scris și că nu s-a produs nici o eroare; în acest caz, aplicația din user-space reîncearcă de cele mai multe ori scrierea.
Line 342: Line 346:
  
 Pentru a realiza un transfer de date format din mai multe transferuri parțiale, vor trebui realizate următoarele operații: Pentru a realiza un transfer de date format din mai multe transferuri parțiale, vor trebui realizate următoarele operații:
-  *se transferă numărul maxim de octeți posibil între buffer-ul primit ca parametru și dispozitiv (scrierea pe dispozitiv/​citirea de pe dispozitiv se va face începând de la offset-ul primit ca parametru);​ +  * se transferă numărul maxim de octeți posibil între buffer-ul primit ca parametru și dispozitiv (scrierea pe dispozitiv/​citirea de pe dispozitiv se va face începând de la offset-ul primit ca parametru);​ 
-  *se actualizează offset-ul primit ca parametru la poziția de la care va începe următoarea citire / scriere a datelor; +  * se actualizează offset-ul primit ca parametru la poziția de la care va începe următoarea citire / scriere a datelor; 
-  *se întoarce numărul de octeți transferați.+  * se întoarce numărul de octeți transferați.
  
 Secvența de mai jos prezintă un exemplu de apel simplu al funcției ''​read''​. Apelul nu actualizează câmpul ''​offset''​ astfel că tot timpul va întoarce mesajul de la începutul buffer-ului. O implementare corectă trebuie să țină cont de parametrul ''​offset''​ și să-l actualizeze după citire. Secvența de mai jos prezintă un exemplu de apel simplu al funcției ''​read''​. Apelul nu actualizează câmpul ''​offset''​ astfel că tot timpul va întoarce mesajul de la începutul buffer-ului. O implementare corectă trebuie să țină cont de parametrul ''​offset''​ și să-l actualizeze după citire.
Line 363: Line 367:
 </​code>​ </​code>​
  
-Structura funcției ''​write''​ este similară: citește date din user-space folosind funcția [[http://lxr.free-electrons.com/​source/​include/​asm-generic/uaccess.h?v=3.13#L255 |  copy_from_user]] și le scrie pe dispozitiv.+Structura funcției ''​write''​ este similară: citește date din user-space folosind funcția [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/uaccess.h#L144 |  copy_from_user]] și le scrie pe dispozitiv.
  
 <code c> <code c>
Line 413: Line 417:
  
 Înainte de a implementa funcția ''​ioctl'',​ vor trebui alese numerele ce corespund comenzilor. O metodă este de a alege numere consecutive începând de la 0, dar se recomandă folosirea macrodefiniției ''​_IOC(dir,​ type, nr, size)''​((IOC -  alternativ, se pot folosi macrodefinițiile _IO (pentru o comandă fără parametri), _IOR (pentru o comandă de citire), _IOW (pentru o comandă de scriere) sau _IOWR (pentru o comandă de transfer bidirecțional) )) pentru generarea codurilor ioctl. Parametrii macrodefiniției sunt după cum urmează: Înainte de a implementa funcția ''​ioctl'',​ vor trebui alese numerele ce corespund comenzilor. O metodă este de a alege numere consecutive începând de la 0, dar se recomandă folosirea macrodefiniției ''​_IOC(dir,​ type, nr, size)''​((IOC -  alternativ, se pot folosi macrodefinițiile _IO (pentru o comandă fără parametri), _IOR (pentru o comandă de citire), _IOW (pentru o comandă de scriere) sau _IOWR (pentru o comandă de transfer bidirecțional) )) pentru generarea codurilor ioctl. Parametrii macrodefiniției sunt după cum urmează:
-  *''​dir''​ reprezintă direcția de transfer a datelor (''​_IOC_NONE'',​ ''​_IOC_READ'',​ ''​_IOC_WRITE''​)((dir - direcția este precizată din punct de vedere al aplicației:​ _IOC_READ când se citește de pe device, iar _IOC_WRITE când se scrie pe device)); +  *''​dir''​ reprezintă direcția de transfer a datelor (''​_IOC_NONE'',​ ''​_IOC_READ'',​ ''​_IOC_WRITE''​)((dir - direcția este precizată din punct de vedere al aplicației: ​''​_IOC_READ'' ​când se citește de pe device, iar ''​_IOC_WRITE'' ​când se scrie pe device)); 
-  *''​type''​ reprezintă numărul magic ([[http://lxr.free-electrons.com/​source/​Documentation/​ioctl/​ioctl-number.txt?​v=3.13 |  Documentation/​ioctl-number.txt]]);​+  *''​type''​ reprezintă numărul magic ([[https://elixir.bootlin.com/​linux/​v3.15/​source/​Documentation/​ioctl/​ioctl-number.txt]]);​
   *''​nr''​ este numărul codului ''​ioctl''​ specific dispozitivului;​   *''​nr''​ este numărul codului ''​ioctl''​ specific dispozitivului;​
   *''​size''​ este dimensiunea datelor transferate.   *''​size''​ este dimensiunea datelor transferate.
Line 458: Line 462:
 ===== Sincronizare - cozi de așteptare ===== ===== Sincronizare - cozi de așteptare =====
  
-Cozile de așteptare sunt mecanisme utile în probleme de sincronizare. De multe ori este necesar ca un thread să aștepte terminarea unei operații, dar este de dorit ca această așteptare să nu fie busy-wating. Folosind cozi de așteptare și funcții care schimbă starea thread-ului din planificabil în neplanificabil și invers se pot rezolva astfel de probleme. În Linux, o coadă de așteptare este o listă în care sunt trecute procesele care așteaptă un anumit eveniment. O coadă de așteptare este definită cu tipul [[http://lxr.free-electrons.com/​source/​include/​linux/​wait.h?v=3.13#L35 |  wait_queue_head_t]] și poate fi folosită de funcțiile/​macro-urile:​+Cozile de așteptare sunt mecanisme utile în probleme de sincronizare. De multe ori este necesar ca un thread să aștepte terminarea unei operații, dar este de dorit ca această așteptare să nu fie busy-waiting. Folosind cozi de așteptare și funcții care schimbă starea thread-ului din planificabil în neplanificabil și invers se pot rezolva astfel de probleme. În Linux, o coadă de așteptare este o listă în care sunt trecute procesele care așteaptă un anumit eveniment. O coadă de așteptare este definită cu tipul [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​wait.h#​L38 |  wait_queue_head_t]] și poate fi folosită de funcțiile/​macro-urile:​
  
 <code c> <code c>
Line 482: Line 486:
 Rolurile macro-urilor/​funcțiilor de mai sus sunt: Rolurile macro-urilor/​funcțiilor de mai sus sunt:
  
-  *[[http://lxr.free-electrons.com/​source/​include/​linux/​wait.h?v=3.13#L70 |  init_waitqueue_head]] inițializează coada de așteptare; dacă se dorește inițializarea cozii la compilare, se poate folosi macroul [[http://lxr.free-electrons.com/​source/​include/​linux/​wait.h?v=3.13#L59  DECLARE_WAIT_QUEUE_HEAD]];​+  *[[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​wait.h#​L63 |  init_waitqueue_head]] inițializează coada de așteptare; dacă se dorește inițializarea cozii la compilare, se poate folosi macroul [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​wait.h#​L58 | DECLARE_WAIT_QUEUE_HEAD]];​
  
-  *[[http://lxr.free-electrons.com/​source/​include/​linux/​wait.h?v=3.13#L233  wait_event]] și [[http://lxr.free-electrons.com/​source/​include/​linux/​wait.h?v=3.13#L311  wait_event_interruptible]] adaugă thread-ul curent la coada de așteptare cât timp condiția este falsă, îi setează starea la ''​TASK_UNINTERRUPTIBLE''​ sau ''​TASK_INTERRUPTIBLE''​ și apelează scheduler-ul pentru planificarea unui nou thread; așteptarea va fi întreruptă atunci când un alt thread va apela funcția [[http://lxr.free-electrons.com/​source/​include/​linux/​wait.h?v=3.13#L159  wake_up]];+  *[[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​wait.h#​L273 | wait_event]] și [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​wait.h#​L426 | wait_event_interruptible]] adaugă thread-ul curent la coada de așteptare cât timp condiția este falsă, îi setează starea la ''​TASK_UNINTERRUPTIBLE''​ sau ''​TASK_INTERRUPTIBLE''​ și apelează scheduler-ul pentru planificarea unui nou thread; așteptarea va fi întreruptă atunci când un alt thread va apela funcția [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​wait.h#​L195 | wake_up]];
  
-  *[[http://lxr.free-electrons.com/​source/​include/​linux/​wait.h?v=3.13#L257  wait_event_timeout]] și [[http://lxr.free-electrons.com/​source/​include/​linux/​wait.h?v=3.13#L339  wait_event_interruptible_timeout]] au același efect ca funcțiile de mai sus, doar că așteptarea poate fi întreruptă la încheierea timeout-ului primit ca parametru;+  *[[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​wait.h#​L338 | wait_event_timeout]] și [[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​wait.h#​L455 | wait_event_interruptible_timeout]] au același efect ca funcțiile de mai sus, doar că așteptarea poate fi întreruptă la încheierea timeout-ului primit ca parametru;
  
-  *[[http://lxr.free-electrons.com/​source/​include/​linux/​wait.h?v=3.13#L159 |  wake_up]] pune toate thread-urile oprite din starea ''​TASK_INTERRUPTIBLE''​ și ''​TASK_UNINTERRUPTIBLE''​ în starea ''​TASK_RUNNING'';​ scoate aceste thread-uri din coada de așteptare;+  *[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​wait.h#​L195 |  wake_up]] pune toate thread-urile oprite din starea ''​TASK_INTERRUPTIBLE''​ și ''​TASK_UNINTERRUPTIBLE''​ în starea ''​TASK_RUNNING'';​ scoate aceste thread-uri din coada de așteptare;
  
-  *[[http://lxr.free-electrons.com/​source/​include/​linux/​wait.h?v=3.13#L165 |  wake_up_interruptible]] aceeași acțiune, însă se folosesc doar thread-urile cu starea ''​TASK_INTERRUPTIBLE''​.+  *[[ https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​wait.h#​L201 |  wake_up_interruptible]] aceeași acțiune, însă se folosesc doar thread-urile cu starea ''​TASK_INTERRUPTIBLE''​.
  
 Un exemplu simplu este cel al unui thread care așteaptă modificarea valorii unui flag. Inițializările se realizează prin secvența: Un exemplu simplu este cel al unui thread care așteaptă modificarea valorii unui flag. Inițializările se realizează prin secvența:
Line 518: Line 522:
     wake_up_interruptible(&​wq);​     wake_up_interruptible(&​wq);​
 </​code>​ </​code>​
-===== Quiz ===== 
- 
-Pentru auto-evaluare înainte de laborator răspundeți la întrebările din [[http://​elf.cs.pub.ro/​so2/​wiki/​laboratoare/​lab04/​quiz | quiz]]. 
  
 ===== Resurse utile ===== ===== Resurse utile =====
Line 531: Line 532:
   * [[http://​tldp.org/​LDP/​lkmpg/​2.6/​html/​x569.html |  Character Device Drivers]]   * [[http://​tldp.org/​LDP/​lkmpg/​2.6/​html/​x569.html |  Character Device Drivers]]
   * [[http://​tldp.org/​LDP/​lkmpg/​2.6/​html/​x892.html |  Talking to Device Files (writes and IOCTLs)]]   * [[http://​tldp.org/​LDP/​lkmpg/​2.6/​html/​x892.html |  Talking to Device Files (writes and IOCTLs)]]
-  * [[http://​janitor.kernelnewbies.org/​docs/​driver-howto.html |  Linux Device Driver Dos and Don'​ts]]+  * [[http://kernel-janitor.sourceforge.net/​kernel-janitor/​docs/​driver-howto.html |  Linux Device Driver Dos and Don'​ts]]
   * [[http://​lwn.net/​Articles/​119652/​ | The new way of ioctl()]]   * [[http://​lwn.net/​Articles/​119652/​ | The new way of ioctl()]]
so2/laboratoare/lab04.1457802115.txt.gz · Last modified: 2016/03/12 19:01 by sorin.baltateanu
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