Differences

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

Link to this comparison view

so2:laboratoare:lab03 [2016/03/02 19:09]
madalina.hristache [Alocare memorie]
so2:laboratoare:lab03 [2018/03/04 20:32] (current)
ionel.ghita [Resurse utile]
Line 18: Line 18:
 ===== Materiale ajutătoare ===== ===== Materiale ajutătoare =====
  
-  *[[http://​elf.cs.pub.ro/​so2/​res/​laboratoare/​lab03-slides.pdf|Slide-uri de suport pentru laborator]] +  * [[http://​elf.cs.pub.ro/​so2/​res/​laboratoare/​lab03-slides.pdf|Slide-uri de suport pentru laborator]] 
-  *[[http://​elf.cs.pub.ro/​so2/​res/​extra/​so2_reference.pdf|SO2 Reference Card]]+  * [[http://​elf.cs.pub.ro/​so2/​res/​extra/​so2_reference.pdf|SO2 Reference Card]]
  
 ===== Noțiuni generale ===== ===== Noțiuni generale =====
Line 28: Line 28:
  
 O diferență importantă în programarea kernel este modul de accesare și alocare a memoriei. Din cauza faptului că programarea kernel se face la un nivel foarte aproape de mașina fizică, există reguli importante în ceea ce privește gestiunea memoriei. În primul rând, se lucrează cu mai multe tipuri de memorie: O diferență importantă în programarea kernel este modul de accesare și alocare a memoriei. Din cauza faptului că programarea kernel se face la un nivel foarte aproape de mașina fizică, există reguli importante în ceea ce privește gestiunea memoriei. În primul rând, se lucrează cu mai multe tipuri de memorie:
-  *memorie fizică +  * memorie fizică 
-  *memorie virtuală din spațiul de adresare kernel +  * memorie virtuală din spațiul de adresare kernel 
-  *memorie virtuală din spațiul de adresare al unui proces +  * memorie virtuală din spațiul de adresare al unui proces 
-  *memorie rezidentă -- știm sigur că paginile accesate sunt prezente în memoria fizică+  * memorie rezidentă -- știm sigur că paginile accesate sunt prezente în memoria fizică
  
 Memoria virtuală din spațiul de adresare al unui proces nu poate fi considerată rezidentă din cauza mecanismelor de memorie virtuală implementate de sistemul de operare: paginile pot fi în swap, sau pur și simplu pot să nu fie prezente în memoria fizică drept rezultat al mecanismului de demand paging. Memoria din spațiul de adresare kernel poate fi rezidentă sau nu. Atât segmentele de date și cod ale unui modul, cât și stiva kernel a unui proces sunt rezidente (în Windows, dacă se dorește, și acestea se pot swapa). Memoria dinamică poate fi sau nu rezidentă, în funcție de modul în care se alocă. Memoria virtuală din spațiul de adresare al unui proces nu poate fi considerată rezidentă din cauza mecanismelor de memorie virtuală implementate de sistemul de operare: paginile pot fi în swap, sau pur și simplu pot să nu fie prezente în memoria fizică drept rezultat al mecanismului de demand paging. Memoria din spațiul de adresare kernel poate fi rezidentă sau nu. Atât segmentele de date și cod ale unui modul, cât și stiva kernel a unui proces sunt rezidente (în Windows, dacă se dorește, și acestea se pot swapa). Memoria dinamică poate fi sau nu rezidentă, în funcție de modul în care se alocă.
Line 39: Line 39:
 Memoria virtuală a unui proces nu se poate accesa direct din kernel. În general este descurajată total accesarea spațiului de adresă al unui process, dar există situații în care un device driver trebuie să o facă. Cazul tipic este cel în care device driver-ul trebuie să acceseze un buffer din user-space. În acest caz, device driverul trebuie să folosească funcții speciale și nu să acceseze direct bufferul. Acest lucru este necesar pentru a preveni accesarea unor zone invalide de memorie. Memoria virtuală a unui proces nu se poate accesa direct din kernel. În general este descurajată total accesarea spațiului de adresă al unui process, dar există situații în care un device driver trebuie să o facă. Cazul tipic este cel în care device driver-ul trebuie să acceseze un buffer din user-space. În acest caz, device driverul trebuie să folosească funcții speciale și nu să acceseze direct bufferul. Acest lucru este necesar pentru a preveni accesarea unor zone invalide de memorie.
  
-O altă diferență față de programarea din userspace, relativ la lucrul cu memoria, este datorată stivei, stivă a cărei dimensiune este fixă și limitată. În nucleul Linux 2.6.x se folosește implicit o stivă de ''​4K'',​ iar în Windows se folosește o stivă de ''​12K''​. Din această cauză, trebuie evitate alocarea unor structuri de mari dimensiuni pe stivă sau folosirea apelurilor recursive.+O altă diferență față de programarea din userspace, relativ la lucrul cu memoria, este datorată stivei, stivă a cărei dimensiune este fixă și limitată. În nucleul Linux se folosește implicit o stivă de ''​4K'',​ iar în Windows se folosește o stivă de ''​12K''​. Din această cauză, trebuie evitate alocarea unor structuri de mari dimensiuni pe stivă sau folosirea apelurilor recursive.
  
 ==== Contexte de execuție ==== ==== Contexte de execuție ====
Line 73: Line 73:
 </​code>​ </​code>​
  
-Lista exhaustivă a erorilor și o sumară explicație găsiți în [[http://lxr.free-electrons.com/​source/​include/​uapi/​asm-generic/​errno.h?​v=3.13| include/​asm-generic/​errno-base.h]] și [[http://lxr.free-electrons.com/​source/​include/​uapi/​asm-generic/​errno.h?​v=3.13 ​| include/​asm-generic/​ernno.h]].+Lista exhaustivă a erorilor și o sumară explicație găsiți în [[http://elixir.bootlin.com/linux/v4.9/​source/​include/​uapi/​asm-generic/​errno-base.h?| include/​asm-generic/​errno-base.h]] și [[http://elixir.bootlin.com/linux/v4.9/​source/​include/​uapi/​asm-generic/​errno.h?​ | include/​asm-generic/​ernno.h]].
  
 ==== Șiruri de caractere ==== ==== Șiruri de caractere ====
  
-În Linux, programatorului de kernel i se pun la dispoziție funcțiile uzuale de lucru pe șiruri: ''​strcpy'',​ ''​strncpy'',​ ''​strlcpy'',​ ''​strcat'',​ ''​strncat'',​ ''​strlcat'',​ ''​strcmp'',​ ''​strncmp'',​ ''​strnicmp'',​ ''​strchr'',​ ''​strnchr'',​ ''​strrchr'',​ ''​strstr'',​ ''​strlen'',​ ''​memset'',​ ''​memcpy'',​ ''​memmove'',​ ''​memscan'',​ ''​memcmp'',​ ''​memchr''​. Aceste funcții sunt declarate în headerul [[http://lxr.free-electrons.com/​source/​lib/string.c?v=3.13 ​| include/​linux/​string.h]] și sunt implementate în kernel.+În Linux, programatorului de kernel i se pun la dispoziție funcțiile uzuale de lucru pe șiruri: ''​strcpy'',​ ''​strncpy'',​ ''​strlcpy'',​ ''​strcat'',​ ''​strncat'',​ ''​strlcat'',​ ''​strcmp'',​ ''​strncmp'',​ ''​strnicmp'',​ ''​strchr'',​ ''​strnchr'',​ ''​strrchr'',​ ''​strstr'',​ ''​strlen'',​ ''​memset'',​ ''​memcpy'',​ ''​memmove'',​ ''​memscan'',​ ''​memcmp'',​ ''​memchr''​. Aceste funcții sunt declarate în headerul [[http://elixir.bootlin.com/linux/v4.9/source/include/​linux/string.h?​|include/​linux/​string.h]] și sunt implementate în kernel ​în fișierul [[http://​elixir.bootlin.com/​linux/​v4.9/​source/​lib/​string.c?​|lib/​string.c]].
  
 ==== printk ==== ==== printk ====
  
-Echivalentul ''​printf''​ în kernel este ''​printk'',​ definit în [[http://lxr.linux.no/#linux+v3.3.5/​include/​linux/​kernel.h | include/​linux/​kernel.h]]. Sintaxa printk seamănă foarte mult cu cea a printf. Primul parametru al printk decide categoria de mesaje în care se încadrează mesajul curent:+Echivalentul ''​printf''​ în kernel este ''​printk'',​ definit în [[http://elixir.bootlin.com/linux/v4.9/source/​include/​linux/​printk.h?|include/​linux/​printk.h]]. Sintaxa printk seamănă foarte mult cu cea a printf. Primul parametru al printk decide categoria de mesaje în care se încadrează mesajul curent:
  
 <code C> <code C>
Line 98: Line 98:
 </​code>​ </​code>​
 În cazul în care nivelul de logging lipsește din apelul printk, se realizează logging cu nivelul implicit de la momentul apelului. Un lucru ce trebuie reținut este că mesajele trimise cu printk sunt vizibile doar pe consolă ((În Linux consola este terminalul virtual curent; din această cauză, atunci când folosiți X Windows, aceste mesaje nu or sa apară în emulatorul de terminal ''​xterm''​. Le puteți afișa, însă, folosind comanda ''​dmesg''​ sau accesând fișierul de logging ''/​var/​log/​syslog''​.)) și doar dacă nivelul lor depășește nivelul implicit setat pe consolă ((Pentru mai multe detalii despre configurări pentru logging consultați [[:​so2:​laboratoare:​lab02 | Laboratorul 2]].)). În cazul în care nivelul de logging lipsește din apelul printk, se realizează logging cu nivelul implicit de la momentul apelului. Un lucru ce trebuie reținut este că mesajele trimise cu printk sunt vizibile doar pe consolă ((În Linux consola este terminalul virtual curent; din această cauză, atunci când folosiți X Windows, aceste mesaje nu or sa apară în emulatorul de terminal ''​xterm''​. Le puteți afișa, însă, folosind comanda ''​dmesg''​ sau accesând fișierul de logging ''/​var/​log/​syslog''​.)) și doar dacă nivelul lor depășește nivelul implicit setat pe consolă ((Pentru mai multe detalii despre configurări pentru logging consultați [[:​so2:​laboratoare:​lab02 | Laboratorul 2]].)).
 +
 +
 +Pentru a reduce dimensiunea linilor atunci când se folosește printk, se recomandă folosirea următoarelor funcții ajutătoare,​ în loc de a folosi direct apelul printk:
 +
 +<code C>
 +pr_emerg(fmt,​ ...); /* echivalent cu printk(KERN_EMERG pr_fmt(fmt),​ ...); */
 +pr_alert(fmt,​ ...); /* echivalent cu printk(KERN_ALERT pr_fmt(fmt),​ ...); */
 +pr_crit(fmt,​ ...); /* echivalent cu printk(KERN_CRIT pr_fmt(fmt),​ ...); */
 +pr_err(fmt, ...); /* echivalent cu printk(KERN_ERR pr_fmt(fmt),​ ...); */
 +pr_warning(fmt,​ ...); /* echivalent cu printk(KERN_WARNING pr_fmt(fmt),​ ...); */
 +pr_warn(fmt,​ ...); /* echivalent cu cu printk(KERN_WARNING pr_fmt(fmt),​ ...); */
 +pr_notice(fmt,​ ...); /* echivalent cu printk(KERN_NOTICE pr_fmt(fmt),​ ...); */
 +pr_info(fmt,​ ...); /* echivalent cu printk(KERN_INFO pr_fmt(fmt),​ ...); */
 +</​code>​
 +
 +Un caz special este ''​pr_debug''​ care apeleaza funcția printk doar atunci când macroul ''​DEBUG''​ este definit sau dacă se folosește dynamic debugging.
  
 ==== Alocare memorie ==== ==== Alocare memorie ====
  
-În Linux se poate aloca doar memorie **rezidentă**,​ cu ajutorul apelului [[http://lxr.free-electrons.com/​source/​include/​linux/​slab.h?​v=3.13#L441 | kmalloc]]. Un apel tipic kmalloc este prezentat în continuare:+În Linux se poate aloca doar memorie **rezidentă**,​ cu ajutorul apelului [[http://elixir.bootlin.com/linux/v4.9/​source/​include/​linux/​slab.h?#​L425 | kmalloc]]. Un apel tipic kmalloc este prezentat în continuare:
  
 <code C> <code C>
Line 117: Line 133:
   *''​GFP_ATOMIC''​ - atunci când se folosește această valoare se garantează ca funcția kmalloc nu suspendă procesul curent; poate fi folosită oricând.   *''​GFP_ATOMIC''​ - atunci când se folosește această valoare se garantează ca funcția kmalloc nu suspendă procesul curent; poate fi folosită oricând.
  
-Complementara funcției ''​kmalloc''​ este [[http://lxr.free-electrons.com/​source/​mm/​slab.c?​v=3.13#L3650 | kfree]], funcție ce primește ca argument o zonă alocată de ''​kmalloc''​. Această funcție nu suspendă procesul curent și, în consecință,​ poate fi apelată din orice context.+Complementara funcției ''​kmalloc''​ este [[http://elixir.bootlin.com/linux/v4.9/​source/​mm/​slab.c?#​L3804 | kfree]], funcție ce primește ca argument o zonă alocată de ''​kmalloc''​. Această funcție nu suspendă procesul curent și, în consecință,​ poate fi apelată din orice context.
 ==== Liste ==== ==== Liste ====
  
-Pentru că listele înlănțuite sunt deseori folosite, Linux kernel API pune la dispoziție o modalitate unitară de definire și folosire a listelor. Aceasta implică folosirea unui element de tipul ''​struct list_head''​ în cadrul structurii pe care vrem să o folosim ca element ​al unei liste. Structura ''​list_head''​ este definită în [[ http://lxr.free-electrons.com/​source/​include/​linux/​list.h?​v=3.13 ​/include/​linux/​list.h]] alături de toate celelalte funcții ce lucrează pe liste. Codul următor arată definiția structurii ''​list_head''​ și folosirea unui element din acest tip într-o altă structură bine cunoscută din kernelul ​de Linux:+Pentru că listele înlănțuite sunt deseori folosite, Linux kernel API pune la dispoziție o modalitate unitară de definire și folosire a listelor. Aceasta implică folosirea unui element de tipul ''​struct list_head''​ în cadrul structurii pe care vrem să o considerăm nod al unei liste. Structura ''​list_head''​ este definită în [[ http://elixir.bootlin.com/linux/v4.9/​source/​include/​linux/​list.h?​ | include/​linux/​list.h]] alături de toate celelalte funcții ce lucrează pe liste. Codul următor arată definiția structurii ''​list_head''​ și folosirea unui element din acest tip într-o altă structură bine cunoscută din kernelul Linux:
  
 <code C> <code C>
Line 143: Line 159:
   *''​list_for_each_safe(pos,​ n, head)''​ iterează o listă, folosind ''​pos''​ drept cursor și ''​n''​ cursor temporar. Acest macro este folosit în cazul în care se dorește ștergerea unui element din listă.   *''​list_for_each_safe(pos,​ n, head)''​ iterează o listă, folosind ''​pos''​ drept cursor și ''​n''​ cursor temporar. Acest macro este folosit în cazul în care se dorește ștergerea unui element din listă.
  
-Următorul cod arată modul de folosire ​al acestor rutine:+Următorul cod arată modul de folosire ​acestor rutine:
  
 <code C> <code C>
Line 203: Line 219:
 {{:​so2:​laboratoare:​lab03:​list.png?​680|Structura listelor kernel}} {{:​so2:​laboratoare:​lab03:​list.png?​680|Structura listelor kernel}}
  
-Se observă comportamentul de tip stivă introdus de macro-ul ''​list_add''​ precum și folosirea unei santinele.+Se observă comportamentul de tip stivă introdus de macro-ul ''​list_add''​precum și folosirea unei santinele.
  
-Din exemplul de mai susse observă că modalitatea de definire și folosire a unei liste (dublu înlănțuite) este generică și în același timp nu introduce un overhead suplimentar. Structura ''​list_head''​ este folosită pentru a menține legăturile între elementele listei. Se observă, de asemenea, că iterarea prin listă se face tot cu ajutorul acestei structuri, iar obținerea elementelor din listă se face cu ajutorul ''​list_entry''​. Această idee de implementare și folosire a unei liste nu este nouă, ea fiind descrisă în ''​The Art of Computer Programming''​ de Donald Knuth în anii '80.+Din exemplul de mai sus se observă că modalitatea de definire și folosire a unei liste (dublu înlănțuite) este generică șiîn același timpnu introduce un overhead suplimentar. Structura ''​list_head''​ este folosită pentru a menține legăturile între elementele listei. Se observă, de asemenea, că iterarea prin listă se face tot cu ajutorul acestei structuri, iar obținerea elementelor din listă se face cu ajutorul ''​list_entry''​. Această idee de implementare și folosire a unei liste nu este nouă, ea fiind descrisă în ''​The Art of Computer Programming''​ de Donald Knuth în anii '80.
  
-Mai multe funcții și macrodefiniții de lucru cu liste kernel sunt prezentate și explicate în headerul [[ http://lxr.free-electrons.com/​source/​include/​linux/​list.h?​v=3.13 ​| include/​linux/​list.h]].+Mai multe funcții și macrodefiniții de lucru cu liste kernel sunt prezentate și explicate în headerul [[http://elixir.bootlin.com/linux/v4.9/​source/​include/​linux/​list.h?​ | include/​linux/​list.h]].
 ==== Locking ==== ==== Locking ====
  
 ==== Spinlock-uri ==== ==== Spinlock-uri ====
  
-''​spinlock_t''​ (definit in [[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?​v=3.13 ​| linux/​spinlock.h]]) este tipul de bază ce implementează conceptul de spinlock în Linux. El descrie un spinlock, iar operațiile asociate cu un spinlock sunt ''​spin_lock_init'',​ ''​spin_lock'',​ ''​spin_unlock''​. Un exemplu de utilizare este prezentat mai jos:+''​spinlock_t''​ (definit in [[http://elixir.bootlin.com/linux/v4.9/​source/​include/​linux/​spinlock.h?​ | linux/​spinlock.h]]) este tipul de bază ce implementează conceptul de spinlock în Linux. El descrie un spinlock, iar operațiile asociate cu un spinlock sunt ''​spin_lock_init'',​ ''​spin_lock'',​ ''​spin_unlock''​. Un exemplu de utilizare este prezentat mai jos:
  
 <code C> <code C>
Line 269: Line 285:
 </​code>​ </​code>​
  
-==== Semafoare ​====+==== Mutex ====
  
-Un semafor ​este reprezentat de o variabila ​de tipul ''​struct ​semaphore''​ (definit în [[ http://lxr.free-electrons.com/​source/​include/​linux/​semaphore.h?v=3.13 ​| linux/semaphore.h]]). Funcțiile și macro-urile pentru lucrul cu semafoare ​sunt prezentate în continuare:+Un mutex este reprezentat de o variabilă ​de tipul ''​struct ​mutex''​ (definit în [[http://elixir.bootlin.com/linux/v4.9/​source/​include/​linux/​mutex.h?#L22 | linux/mutex.h]]). Funcțiile și macro-urile pentru lucrul cu mutex sunt prezentate în continuare:
  
 <code C> <code C>
-#include <linux/semaphore.h>+#include <linux/mutex.h>
  
-/* functii pentru initializarea ​semaforului ​*/ +/* functii pentru initializarea ​mutexului ​*/ 
-void sema_init(struct ​semaphore ​*sem, int val); +void mutex_init(struct ​mutex *mutex); 
-DECLARE_MUTEX(name); +DEFINE_MUTEX(name);
-void init_MUTEX(struct semaphore *sem); +
-void init_MUTEX_LOCKED(struct semaphore *sem);+
  
-/* functii pentru achiziționarea ​semaforului ​*/ +/* functii pentru achiziționarea ​mutexului ​*/ 
-void down(struct ​semaphore ​*sem); +void mutex_lock(struct ​mutex *mutex);
-int down_interruptible(struct semaphore *sem); +
-int down_trylock(struct semaphore *sem);+
  
 /* functie pentru eliberarea semaforului */ /* functie pentru eliberarea semaforului */
-void up(struct ​semaphore ​*sem);+void mutex_unlock(struct ​mutex *mutex);
 </​code>​ </​code>​
  
-Funcția ''​down''​ decrementează valoarea semaforului și se blochează până când aceasta devine iar nenegativă. Funcția ''​down_interruptible''​ face același lucru numai că operația poate fi întreruptă. Se recomandă testarea de fiecare dată a valorii întoarse de această funcție deoarece o valoare diferită de 0 înseamnă că operația a fost întreruptă și apelantul nu a obținut semaforul. Funcția ''​down_trylock''​ este varianta neblocantă pentru achiziționarea unui semafor, dacă nu se poate lua semaforul se întoarce o valoare diferita de 0. 
  
-Trebuie reținut faptul că nu este permisă o operație down blocantă în context de întrerupere ​sau într-o regiune în care se deține un spinlock (context atomic). +Operațiile sunt similare cu operațiile clasice ale mutexului din userspace ​sau cu operațiile spinlock-ului: mutex-ul ​se achiziționează înainte ​de intrarea în zona critică și se eliberează la ieșirea din zona critică. Spre deosebire de spin-lock-uri,​ aceste operații se pot folosi doar în context proces.
- +
-După apelul funcției ''​up'',​ apelantul nu mai deține semaforul.+
  
 ==== Variabile atomice ==== ==== Variabile atomice ====
  
-De multe ori, este nevoie doar de sincronizarea accesului la o variabilă simplă, de exemplu un contor. Pentru ​ aceasta se poate folosi o variabilă de tip ''​atomic_t''​ (definit în [[http://lxr.free-electrons.com/​source/​include/​linux/​atomic.h?​v=3.13 ​| include/​linux/​atomic.h]]) care ține o valoare întreagă. Mai jos sunt prezentate unele operații care pot fi efectuate asupra unei variabile ''​atomic_t'':​+De multe ori, este nevoie doar de sincronizarea accesului la o variabilă simplă, de exemplu un contor. Pentru ​ aceasta se poate folosi o variabilă de tip ''​atomic_t''​ (definit în [[http://elixir.bootlin.com/linux/v4.9/​source/​include/​linux/​atomic.h?​ | include/​linux/​atomic.h]]) care ține o valoare întreagă. Mai jos sunt prezentate unele operații care pot fi efectuate asupra unei variabile ''​atomic_t'':​
  
 <code C> <code C>
Line 312: Line 321:
 int atomic_inc_and_test(atomic_t *v); int atomic_inc_and_test(atomic_t *v);
 int atomic_dec_and_test(atomic_t *v); int atomic_dec_and_test(atomic_t *v);
 +int atomic_cmpxchg(atomic_t *v, int old, int new);
 +</​code>​
 +
 +=== Utilizarea variabilelor atomice ===
 +
 +Un mod frecvent de utilizare a variabilelor atomice este pentru a menține starea unei acțiuni (de exemplu un flag). Putem folosi astfel o variabilă atomică pentru a marca acțiuni exclusive. De exemplu, considerăm că o variabilă atomică poate avea valorile ''​LOCKED''​ și ''​UNLOCKED''​ și, dacă ''​LOCKED''​ atunci o funcție anume să se întoarcă cu un mesaj ''​-EBUSY''​. Modul de folosire este indicat schematic în codul de mai jos:
 +
 +<code C>
 +#define LOCKED 0
 +#define UNLOCKED 1
 +
 +static atomic_t flag;
 +
 +static int my_acquire(void)
 +{
 + int initial_flag;​
 +
 + /*
 + * Check if flag is UNLOCKED; if not, lock it and do it atomically.
 + *
 + * This is the atomic equivalent of
 + * if (flag == UNLOCKED)
 + * flag = LOCKED;
 + * else
 + * return -EBUSY;
 + */
 + initial_flag = atomic_cmpxchg(&​flag,​ UNLOCKED, LOCKED);
 + if (initial_flag == LOCKED) {
 + printk(KERN_ALERT "​Already locked.\n"​);​
 + return -EBUSY;
 + }
 +
 + /* Do your thing after getting the lock. */
 + [...]
 +}
 +
 +static void my_release(void)
 +{
 + /* Release flag; mark it as unlocked. */
 + atomic_set(&​flag,​ UNLOCKED);
 +}
 +
 +void my_init(void)
 +{
 + [...]
 + /* Atomic variable is initially unlocked. */
 + atomic_set(&​flag,​ UNLOCKED);
 +
 + [...]
 +}
 +</​code>​
 +
 +Codul de mai sus este echivalentul folosirii unei operații de tipul ''​trylock''​ (precum [[http://​linux.die.net/​man/​3/​pthread_mutex_lock|pthread_mutex_trylock]]).
 +
 +----
 +
 +Putem, de asemenea, folosi o variabilă pentru a reține dimensiunea unui buffer și pentru actualizări atomice ale acesteia. De exemplu codul de mai jos:
 +
 +<code C>
 +static unsigned char buffer[MAX_SIZE];​
 +static atomic_t size;
 +
 +static void add_to_buffer(unsigned char value)
 +{
 + buffer[atomic_read(&​size)] = value;
 + atomic_inc(&​size);​
 +}
 +
 +static unsigned char remove_from_buffer(void)
 +{
 + unsigned char value;
 +
 + value = buffer[atomic_read(&​size)];​
 + atomic_dec(&​size);​
 +
 + return value
 +}
 +
 +static void reset_buffer(void)
 +{
 + atomic_set(&​size,​ 0);
 +}
 +
 +void my_init(void)
 +{
 + [...]
 + /* Initilized buffer and size. */
 + atomic_set(&​size,​ 0);
 + memset(buffer,​ 0, sizeof(buffer));​
 +
 + [...]
 +}
 </​code>​ </​code>​
  
 ==== Operatii atomice pe biți ==== ==== Operatii atomice pe biți ====
  
-Kernelul pune la dispoziție un set de funcții (în [[http://lxr.free-electrons.com/​source/​include/​asm-generic/​bitops/​atomic.h?​v=3.13#L65 | asm/​bitops.h]]) care modifică sau testează biți în mod atomic.+Kernelul pune la dispoziție un set de funcții (în [[http://elixir.bootlin.com/linux/v4.9/​source/​include/​asm-generic/​bitops/​atomic.h?#​L50 | asm/​bitops.h]]) care modifică sau testează biți în mod atomic.
  
 <code C> <code C>
Line 329: Line 430:
 </​code>​ </​code>​
  
-''​addr''​ reprezintă adresa zonei de memorie ai carei biți se modifică sau testează, iar ''​nr''​ reprezintă bitul asupra căruia se efectuează operația. +''​addr''​ reprezintă adresa zonei de memorie ai cărei ​biți se modifică sau testează, iar ''​nr''​ reprezintă bitul asupra căruia se efectuează operația.
  
 ===== Resurse utile ===== ===== Resurse utile =====
Line 338: Line 438:
     * [[http://​lwn.net/​images/​pdf/​LDD3/​ch08.pdf | Chapter 8.  Allocating Memory]]     * [[http://​lwn.net/​images/​pdf/​LDD3/​ch08.pdf | Chapter 8.  Allocating Memory]]
     * [[http://​lwn.net/​images/​pdf/​LDD3/​ch11.pdf | Chapter 11. Data Types in the Kernel]]     * [[http://​lwn.net/​images/​pdf/​LDD3/​ch11.pdf | Chapter 11. Data Types in the Kernel]]
-  - [[http://www.gnugeneration.com/books/linux/​2.6.20/​kernel-api/​ | The Linux Kernel API]]+  - [[https://www.kernel.org/doc/htmldocs/​kernel-api/​ | The Linux Kernel API]]
   - [[http://​isis.poly.edu/​kulesh/​stuff/​src/​klist/​ | Linux Kernel Linked-List Explained]]   - [[http://​isis.poly.edu/​kulesh/​stuff/​src/​klist/​ | Linux Kernel Linked-List Explained]]
   - [[http://​kernelbook.sourceforge.net/​kernel-locking.html/​ | Unreliable Guide To Locking]]   - [[http://​kernelbook.sourceforge.net/​kernel-locking.html/​ | Unreliable Guide To Locking]]
   - [[http://​excess.org/​article/​2008/​07/​oclug-june-kernel-walkthrough/​ | Linux Kernel Walkthrough Screencast]]   - [[http://​excess.org/​article/​2008/​07/​oclug-june-kernel-walkthrough/​ | Linux Kernel Walkthrough Screencast]]
-  - [[http://linuxwell.com/​2012/​11/​10/​magical-container_of-macro/​| Magical container_of() Macro]]+  - [[http://radek.io/​2012/​11/​10/​magical-container_of-macro/​| Magical container_of() Macro]]
  
so2/laboratoare/lab03.1456938562.txt.gz · Last modified: 2016/03/02 19:09 by madalina.hristache
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