Cursul trecut a arătat cum planifică un RTOS task-urile în timp; acesta arată ce se întâmplă când acele task-uri trebuie să coopereze - să partajeze date fără să se strice reciproc. Pornim de la condiția de cursă și mecanismele clasice de sincronizare, trecem prin capcana inversiunii de prioritate și cele două protocoale care o rezolvă (Priority Inheritance și Priority Ceiling), apoi urcăm un nivel: cum se organizează kernelul însuși (monolitic vs. microkernel), cum izolează memoria task-urile unele de altele, ce înseamnă multicore în timp real, și cum se alege, practic, un RTOS pentru un proiect concret.
1Obiectul și structura cursului6 min
Un task planificat corect, dar care corupe datele altui task, nu e un sistem funcțional - e un sistem care eșuează imprevizibil. Sincronizarea corectă a accesului la resurse partajate e la fel de esențială ca planificarea temporală studiată în Cursul 07, iar cele două probleme sunt legate mai strâns decât pare la prima vedere: un mecanism de sincronizare prost ales poate distruge garanțiile de schedulabilitate demonstrate anterior.
- Un task trece prin stările Running, Ready, Blocked, Suspended
- RMS oferă priorități fixe, cu testul suficient Liu-Layland; EDF oferă priorități dinamice, cu testul U ≤ 1
- Timpul de blocare al unui task de prioritate mare, produs de altele cu prioritate mai mică, trebuie inclus în analiza temporală
Rezultate ale învățării
- Să identificați o condiție de cursă și să explicați de ce apare la o operație aparent simplă
- Să alegeți între mutex, semafor și grup de evenimente pentru un scenariu dat
- Să explicați inversiunea de prioritate și să aplicați Priority Inheritance sau Priority Ceiling
- Să comparați arhitectura monolitică cu microkernel din perspectiva izolării erorilor
- Să descrieți rolul MPU/MMU și diferența dintre AMP și SMP pe platforme multicore
- Să enumerați criteriile practice pentru alegerea unui RTOS într-un proiect real
2Condiții de cursă și secțiuni critice8 min
Chiar și o operație care arată atomică în cod poate fi, la nivel de procesor, o secvență de trei pași separați:
uint32_t event_count = 0;
void TaskA() { ++event_count; }
void TaskB() { ++event_count; }
event_count++ nu e neapărat o singură instrucțiune: se descompune în citirea
valorii din memorie, incrementarea ei și scrierea rezultatului înapoi. Dacă TaskA și
TaskB execută această secvență aproape simultan, se poate întâmpla:
τA citește 10 → τB citește 10 → τA scrie 11 → τB scrie 11
Deși ambele task-uri au incrementat variabila, valoarea finală e 11, nu 12 - a doua scriere suprascrie rezultatul primei, fără să știe că prima a avut loc. Actualizarea lui τA a dispărut complet, silențios, fără nicio eroare semnalată.
N_task-uri_în_secțiunea_critică(R) ≤ 1O secțiune critică trebuie să fie cât mai scurtă: fără operații blocante, fără întârzieri voluntare, fără calcule care pot fi mutate în afara ei.
Varianta A ține un mutex în timpul filtrării, scrierii pe Flash și trimiterii pe rețea. Varianta B copiază datele sub protecția mutex-ului, apoi procesează în afara lui. Care e mai bună și de ce?
Vezi rezolvarea
// Varianta A - secțiune critică excesiv de lungă
lock(data_mutex);
read_shared_data();
perform_complex_filtering();
write_external_flash();
send_network_packet();
unlock(data_mutex);
// Varianta B - copiere rapidă, procesare în afara mutex-ului
SensorData local_copy{};
lock(data_mutex);
local_copy = shared_sensor_data;
unlock(data_mutex);
perform_complex_filtering(local_copy);
write_external_flash(local_copy);
send_network_packet(local_copy);
Varianta B e corectă: doar copierea propriu-zisă necesită protecție, restul operațiilor pot
rula fără mutex-ul blocat. Durata maximă a secțiunii critice mărginește direct timpul de blocare
al altor task-uri care cer aceeași resursă: B_i ≥ C_critical,max - o secțiune critică
lungă în Varianta A ar propaga întârzieri mari către orice task blocat pe acel mutex.
Dezactivarea temporară a întreruperilor poate proteja secvențe foarte scurte între un task și
un ISR (L_IRQ,max ≥ T_întreruperi_dezactivate,max), dar nu funcționează pe
multicore - mascarea pe un nucleu nu împiedică alt nucleu să acceseze aceeași memorie.
Calificatorul volatile influențează doar optimizările compilatorului; el nu
oferă excludere mutuală și nu transformă o secvență citire-modificare-scriere într-o
operație atomică.
3Mutex-uri și semafoare10 min
Trei primitive de sincronizare acoperă majoritatea nevoilor unui sistem embedded: mutex-ul, semaforul și grupul de evenimente.
| Primitivă | Model | Utilizare tipică |
|---|---|---|
| Mutex | are proprietar; doar cine îl obține îl poate elibera | protejarea unei resurse partajate (excludere mutuală) |
| Semafor | contor 0 ≤ S ≤ S_max, fără proprietar | semnalizare între task-uri sau între ISR și task |
| Grup de evenimente | set de biți, fiecare marcând o condiție | așteptarea combinată a mai multor condiții |
// Mutex - protejarea unei resurse partajate, cu timeout
if (lock_mutex(configuration_mutex, mutex_timeout)) {
shared_configuration = new_configuration;
unlock_mutex(configuration_mutex);
} else {
handle_lock_timeout();
}
Un ADC termină o conversie și generează o întrerupere; task-ul de procesare așteaptă acel eveniment fără să consume procesor în așteptare activă.
// în ISR
void ADC_IRQHandler() {
read_conversion_result();
give_semaphore_from_isr(adc_ready_semaphore);
}
// în task
void ProcessingTask() {
while (true) {
take_semaphore(adc_ready_semaphore, portMAX_DELAY);
process_adc_sample();
}
}
Semaforul binar (S ∈ {0,1}) e potrivit aici: ISR-ul „dă" semaforul, iar task-ul „ia" - blocându-se eficient, fără polling, până la sosirea evenimentului.
Ca și în Cursul 07: aproape orice așteptare pe un mutex sau semafor ar trebui să aibă un timeout - resursa așteptată poate să nu mai sosească (mesaj pierdut, periferic defect), iar un task blocat nelimitat pe o resursă care nu mai vine e echivalent, din perspectiva sistemului, cu un task mort.
4Grupuri de evenimente și cozi de mesaje6 min
Grupurile de evenimente permit unui task să aștepte o combinație logică de condiții, nu doar una singură:
E = e₀ ∨ e₁ ∨ ... ∨ eₘ₋₁ - fiecare bit e setat independent de o
sursă diferită (task sau ISR), iar un task poate aștepta orice combinație AND/OR a acestor
biți.Cozile de mesaje transportă date, nu doar semnalizare - un task „pune" un element, altul „scoate", cu blocare (și timeout) la coadă plină sau goală. Un aspect ușor de neglijat: proprietatea datelor transmise printr-o coadă trebuie definită explicit - odată ce un buffer a fost transmis, task-ul emițător nu ar trebui să-l mai modifice, altfel apare, din nou, o condiție de cursă, doar mutată de la o variabilă globală la un obiect transmis prin canal.
- „Am folosit o coadă de mesaje, deci sunt protejat de condiții de cursă." Doar dacă și proprietatea datelor e respectată strict - dacă emițătorul continuă să scrie în bufferul transmis după ce l-a pus în coadă, problema revine sub altă formă. Transferați proprietatea complet: după trimitere, emițătorul nu mai atinge acel buffer.
5Inversiunea de prioritate8 min
Cele trei primitive de mai sus rezolvă corectitudinea accesului la date - dar introduc o problemă nouă, pur temporală, când task-uri cu priorități diferite partajează aceeași resursă.
Trei task-uri: τH (prioritate mare), τM (prioritate medie), τL (prioritate mică). τL deține un mutex M. Scenariul problematic:
- τL obține mutex-ul M și intră în secțiunea critică.
- τH devine Ready, preemptează pe τL, dar apoi solicită și el mutex-ul M → τH se blochează, așteptând ca τL să-l elibereze.
- τM devine Ready. Cum τL are prioritate mai mică decât τM, τM preemptează pe τL.
- τM rulează cât are nevoie, fără să folosească deloc mutex-ul M - dar, cât timp τM rulează, τL nu poate finaliza secțiunea critică, deci nu poate elibera M, deci τH rămâne blocat.
Soluția nu e „evitați mutex-urile" - e să limitați explicit cât poate dura blocarea, prin protocoale dedicate de control al priorității, subiectul următoarelor două secțiuni.
6Priority Inheritance Protocol8 min
Priority Inheritance Protocol (PIP) reduce inversarea de prioritate prin ridicarea temporară a priorității task-ului care deține resursa.
p_efectiv_L = max(p_bază_L, p_H). După
eliberarea resursei: p_efectiv_L → p_bază_L.Aplicat scenariului anterior:
- τL deține mutex-ul.
- τH solicită mutex-ul și se blochează.
- τL moștenește prioritatea lui τH.
- τM nu mai poate preempta pe τL, pentru că prioritatea efectivă a lui τL e acum mai mare decât a lui τM.
- τL termină rapid secțiunea critică și eliberează mutex-ul.
- τH e deblocat și rulează.
- τL revine la prioritatea sa de bază.
Blocarea lui τH e acum mărginită de durata secțiunii critice a lui τL, nu de interferența unui număr nedeterminat de task-uri intermediare.
| Avantaj | Limitare |
|---|---|
| Elimină interferența task-urilor cu prioritate intermediară | Nu previne automat deadlock-ul |
| Integrat în numeroase mutex-uri RTOS, fără modificarea modelului de programare | Poate permite blocări în lanț |
| Păstrează modelul obișnuit de obținere/eliberare a resursei | Funcționează doar dacă se folosește mutex-ul corespunzător, nu un semafor binar |
Task-ul care deține mutex-ul nu trebuie suspendat, întârziat voluntar sau blocat pe o operație lentă în interiorul secțiunii critice - dacă τL, chiar cu prioritate moștenită, execută o operație lentă și necontrolată în secțiunea critică (de exemplu o scriere Flash blocantă), blocarea rămâne lungă indiferent de protocol. PIP mărginește cine poate întârzia τH, nu cât durează secțiunea critică în sine - acea disciplină rămâne responsabilitatea proiectantului.
De asemenea, în multe RTOS-uri, Priority Inheritance e implementat pentru mutex-uri, nu pentru semafoare binare - înlocuirea unui mutex cu un semafor, într-un cod care pare echivalent, poate elimina complet protecția împotriva inversiunii de prioritate, fără niciun avertisment.
7Priority Ceiling și reguli de proiectare10 min
Protocoalele de tip Priority Ceiling atribuie fiecărei resurse o valoare numită plafon de prioritate (priority ceiling).
Π(Rₖ) = max(pᵢ), peste toate task-urile τᵢ
care pot utiliza Rₖ - adică cea mai mare prioritate dintre task-urile care pot cere resursa.Există mai multe variante. În Immediate Ceiling Priority Protocol (varianta cea mai folosită practic), task-ul care obține o resursă primește imediat prioritatea plafonului acelei resurse:
p_efectiv_i = max(p_bază_i, Π(Rₖ))Resursa R e folosită de τH, τM și τL; plafonul ei e Π(R) = pH (cea mai mare prioritate dintre
utilizatori). Când τL obține R: p_efectiv_L = max(pL, Π(R)) = pH - τL rulează
imediat la prioritatea maximă posibilă pentru acea resursă, chiar înainte ca vreun task cu
prioritate mai mare s-o solicite efectiv. τM nu poate preempta secțiunea critică, iar analiza
temporală devine mai simplă decât la PIP, pentru că blocarea nu mai depinde de când apare
cererea concurentă.
Bᵢ ≤ max(Cⱼ,critic), peste task-urile τⱼ cu prioritate mai mică ce ating o resursă
relevantă pentru τᵢ.| Protocol | Avantaj principal | Limitare principală |
|---|---|---|
| Fără protocol | Implementare minimală | Inversare de prioritate potențial nemărginită |
| Priority Inheritance | Elimină interferența priorităților intermediare | Nu previne automat deadlock-ul și blocarea în lanț |
| Priority Ceiling (original) | Previne deadlock-ul și limitează blocarea | Necesită administrarea plafonului sistemului |
| Immediate Ceiling | Comportament predictibil, implementare eficientă | Necesită stabilirea corectă a plafonului fiecărei resurse |
Alegerea între Priority Inheritance și Priority Ceiling depinde de serviciile oferite de RTOS, posibilitatea cunoașterii anticipate a tuturor utilizatorilor unei resurse, cerințele de analiză formală și nivelul de criticitate al sistemului - Priority Ceiling e mai potrivit pentru sisteme statice, bine analizate dinainte; poate fi dificil de aplicat în arhitecturi foarte dinamice, unde nu se cunosc dinainte toți utilizatorii posibili ai unei resurse.
8Arhitecturi de kernel: monolitic vs. microkernel10 min
Arhitectura kernelului stabilește distribuția responsabilităților între codul privilegiat și componentele aplicației - o decizie care influențează latența serviciilor, costul comunicației între componente, izolarea erorilor, suprafața de cod cu privilegii, și posibilitatea certificării.
Arhitectura cu spațiu unic (monolitică)
Kernelul, driverele și aplicația accesează aceeași memorie, legate frecvent într-o singură
imagine executabilă: Aplicație + servicii + kernel + drivere → imagine firmware. Un
apel de serviciu e, de regulă, direct: C_serviciu ≈ C_apel + C_kernel, fără schimbare
de spațiu de adrese și fără copiere de mesaje.
Avantaje: latență redusă, overhead mic, utilizare eficientă a memoriei, integrare simplă pe microcontrolere fără MMU. Limitare principală: propagarea erorilor - un pointer invalid dintr-un driver poate modifica memoria kernelului sau stiva altui task, pentru că nu există o graniță hardware între ele.
Microkernel și servicii separate
Un microkernel păstrează în modul privilegiat numai mecanismele esențiale: planificarea,
gestionarea firelor, comunicația inter-proces (IPC), spațiile de adrese, întreruperile și
controlul accesului la resurse. Driverele, sistemele de fișiere și stivele de rețea rulează în
componente separate, accesate prin schimb de mesaje: client → IPC → server, cu un
cost suplimentar față de apelul direct:
C_microkernel = C_apel + C_IPC + C_schedule + C_serviciu + C_retur
- mai mare decât apelul direct monolitic, dar cu un beneficiu structural important.Beneficiul: dacă un driver neprivilegiat eșuează, kernelul poate împiedica accesul lui la memoria altor componente, iar sistemul poate opri driverul, reporni serviciul sau trece într-un mod degradat - fără să compromită restul sistemului.
Proprietățile depind de corectitudinea kernelului, configurarea drepturilor, serviciile
executate deasupra lui, protocolul IPC și gestionarea erorilor. Principiul relevant e cel al
privilegiului minim: Permisiuni(Cᵢ) = minimum necesar - un microkernel
configurat greșit poate acorda unui serviciu acces excesiv la memorie sau periferice, anulând
practic avantajul arhitectural.
| Criteriu | Monolitic / spațiu unic | Microkernel |
|---|---|---|
| Latență a serviciilor | mică | mai mare (cost IPC) |
| Izolarea erorilor | slabă, fără hardware dedicat | bună, prin granițe IPC |
| Potrivire | microcontrolere mici, cerințe stricte de latență | sisteme cu criticitate mixtă, izolare importantă |
| Complexitate | redusă | mai mare (design + comunicare) |
9Protecția memoriei: MPU și MMU8 min
Izolarea software descrisă mai sus necesită suport hardware. Două componente centrale sunt MPU (Memory Protection Unit) și MMU (Memory Management Unit).
P(Rₖ) = (rₖ, wₖ, xₖ, qₖ), unde rₖ permite
citirea, wₖ scrierea, xₖ execuția, iar qₖ e nivelul de privilegiu necesar.MMU-ul adaugă, suplimentar, traducerea adreselor virtuale în adrese fizice:
A_fizic = M(A_virtual) - permite spații de adrese separate și paginare, cu un cost
hardware și software mai mare decât un simplu MPU.
Într-un sistem cu MPU, un task neprivilegiat poate avea acces la propria stivă, datele proprii, un buffer partajat explicit și anumite periferice - dar nu la memoria altui task sau a kernelului. O încercare de acces în afara regiunilor permise generează o excepție, iar handlerul de eroare poate identifica task-ul vinovat, adresa accesată și tipul operației.
10Multicore și sisteme cu criticitate mixtă10 min
Procesoarele embedded moderne conțin, tot mai frecvent, mai multe nuclee. Două modele principale de organizare: AMP (Asymmetric Multiprocessing) și SMP (Symmetric Multiprocessing).
| Model | Principiu | Exemplu de organizare |
|---|---|---|
| AMP | Core0 → RTOS0, Core1 → RTOS1 - fiecare nucleu rulează o instanță separată de software | un nucleu pentru control, altul pentru comunicații, altul pentru funcții necritice |
| SMP | RTOS → {Core0, Core1, ..., Coreₘ₋₁} - o singură instanță de kernel planifică task-urile pe toate nucleele | o singură coadă Ready, un singur scheduler global |
AMP oferă o separare arhitecturală mai clară, cu comunicare explicită între nuclee prin memorie partajată, mailbox-uri hardware sau întreruperi inter-core. SMP permite utilizarea flexibilă a nucleelor, dar introduce complexități noi: execuție paralelă reală, migrarea task-urilor între nuclee, lock-uri între nuclee, interferență asupra cache-ului comun și dificultăți suplimentare în analiza WCET.
U ≤ m -
task-urile pot avea afinitate restrânsă la o submulțime de nuclee
(Aᵢ ⊆ {0,1,...,m-1}), secțiuni seriale care nu se pot paraleliza, resurse comune care
introduc blocare între nuclee, și interferență hardware (cache, magistrale) greu de mărginit
analitic. Analiza de schedulabilitate pentru multicore e semnificativ mai complexă decât cea
pentru un singur procesor studiată în Cursul 07.Sisteme cu criticitate mixtă
Într-un sistem cu criticitate mixtă, funcții cu niveluri diferite de importanță (de exemplu control critic și interfață grafică; funcție de siguranță și conectivitate) folosesc aceeași platformă hardware. Separarea trebuie realizată atât spațial, cât și temporal:
Mᵢ ∩ Mⱼ = ∅ pentru regiunile private ale componentelor i
și j. Temporal: Cᵢ ≤ Qᵢ în fiecare interval Pᵢ, unde Qᵢ e bugetul alocat componentei,
iar Pᵢ perioada de reîncărcare a bugetului.Un sistem cu criticitate mixtă poate trece într-un mod de criticitate ridicată atunci când timpul de execuție al unei funcții critice depășește estimarea normală - în acel mod, activitățile necritice pot fi reduse sau suspendate pentru a păstra funcțiile esențiale, exact ca o formă de degradare controlată la suprasarcină.
11RTOS-uri moderne: o privire comparativă10 min
Ecosistemul de sisteme de operare embedded acoperă un spectru larg: kerneluri compacte pentru microcontrolere, sisteme apropiate de POSIX, platforme Linux cu preempție în timp real și microkerneluri cu izolare formal verificată.
Comparația trebuie făcută la nivelul unei versiuni și configurații concrete - opțiunile de compilare, driverele, BSP-ul și configurația aplicației pot modifica semnificativ latența maximă, memoria ocupată, certificabilitatea și securitatea reale ale unui produs.
| Platformă | Orientare dominantă | Aspect de evaluat |
|---|---|---|
| FreeRTOS | kernel compact, integrare directă pe microcontrolere mici | serviciile suplimentare necesare produsului (rețea, fișiere) nu vin incluse |
| Zephyr | ecosistem integrat și configurabil (Devicetree, Kconfig), pentru dispozitive conectate | complexitatea configurației și a actualizării |
| Apache NuttX | interfețe apropiate de POSIX (fire, socket-uri, sistem de fișiere) | costul serviciilor activate |
| Linux PREEMPT_RT | funcționalitate generală și latență redusă, pe procesoare cu MMU | configurația completă hardware-software trebuie măsurată, nu presupusă |
| Microkernel comercial (VxWorks, QNX) | izolare, suport profesional, pachete de certificare | costul licențierii și dependența de furnizor |
| seL4 | microkernel bazat pe capabilități, verificat formal pentru anumite configurații | construcția serviciilor și integrarea aplicației rămân responsabilitatea echipei |
FreeRTOS oferă task-uri, priorități, planificare preemptivă sau cooperativă, cozi, semafoare,
mutex-uri și grupuri de evenimente - exact primitivele studiate în această lecție - într-o imagine
statică, legată direct cu aplicația. Zephyr separă descrierea hardware-ului (Devicetree) de logica
aplicației, permițând aceleiași aplicații să ruleze pe platforme diferite. NuttX apropie
dezvoltarea embedded de modelul POSIX clasic (pthread_create, descriptori de fișiere,
socket-uri). Linux cu PREEMPT_RT reduce regiunile nepreemptibile din kernel, dar rămâne potrivit
doar pentru platforme cu procesor performant, MMU și memorie suficientă - și, ca peste tot în acest
domeniu, utilizarea PREEMPT_RT nu demonstrează, singură, respectarea unui deadline; platforma
trebuie măsurată în configurația reală a produsului.
Linux ↔ RTOS. Nu trebuie ales un singur „câștigător" pentru întregul sistem - alegerea
se face per componentă, în funcție de cerințele ei temporale.12Criterii de alegere a unui RTOS8 min
Nu există un RTOS universal optim. Selecția trebuie să pornească de la cerințele sistemului, nu de la popularitatea unei tehnologii, și trebuie să răspundă la o singură întrebare centrală:
„Care platformă permite demonstrarea cerințelor produsului, cu un cost și un risc acceptabile pe întreaga durată de viață?" - nu „care platformă e cea mai populară" sau „care are cele mai multe funcționalități".
Criteriile trebuie ponderate diferit în funcție de context: pentru un dispozitiv simplu, memoria și costul pot domina decizia; pentru un sistem critic, izolarea, certificarea și predictibilitatea temporală au, de regulă, prioritate.
| Dimensiune | Ce trebuie evaluat |
|---|---|
| Cerințe funcționale și temporale | task-uri, IPC, rețea, latență maximă a întreruperilor, jitter, politica de planificare - fiecare marcată obligatorie/recomandată/opțională |
| Resurse | M_RAM = M_kernel + ΣM_stack,i + M_heap + M_buffer + M_driver, măsurat cu driverele și protocoalele reale, nu doar kernelul gol |
| Siguranță, securitate, izolare | standard aplicabil, documentație de certificare, calificarea toolchain-ului, modelul de amenințări, secure boot |
| Ecosistem și cost total | C_total = C_licență + C_integrare + C_dezvoltare + C_validare + C_mentenanță + C_migrare |
Un certificat de siguranță e relevant numai dacă versiunea folosită e cea acoperită de el, configurația e permisă, iar procesorul și compilatorul sunt cele suportate - un pachet de certificare pentru o versiune diferită de RTOS nu se transferă automat.
Procesul recomandat parcurge șase etape: definirea cerințelor măsurabile, filtrarea eliminatorie a candidaților care nu satisfac cerințele obligatorii, evaluarea ponderată a celor rămași, construirea unui prototip pe platforma țintă reală, măsurători efective (latență, jitter, RAM/Flash, consum, comportament la suprasarcină) și, în final, decizia documentată - cu candidații eliminați, ipotezele folosite și condițiile care ar impune o reevaluare ulterioară.
13Erori frecvente5 min
- „Am protejat variabila cu volatile, deci e thread-safe." Fals - volatile influențează doar optimizările compilatorului, nu oferă excludere mutuală și nu transformă o secvență citire-modificare-scriere într-o operație atomică. Folosiți un mutex sau o operație atomică dedicată pentru accesul concurent real.
- „Un semafor binar poate înlocui întotdeauna un mutex." Nu - semafoarele nu au conceptul de proprietar, iar Priority Inheritance, în multe RTOS-uri, e implementat doar pentru mutex-uri. Înlocuirea unui mutex cu un semafor poate elimina, tăcut, protecția împotriva inversiunii de prioritate. Folosiți mutex pentru protejarea resurselor, semafor pentru semnalizare.
- „Priority Inheritance rezolvă complet inversiunea de prioritate, indiferent de cât durează secțiunea critică." Nu - PIP mărginește cine poate întârzia task-ul de prioritate mare, dar nu scurtează secțiunea critică în sine; o secțiune critică prost proiectată (operații lente, I/O blocant) rămâne o problemă indiferent de protocol. Păstrați secțiunile critice scurte, indiferent de protocolul de control al priorității folosit.
- „Un microkernel garantează, singur, un sistem mai sigur decât unul monolitic." Nu automat - microkernelul reduce codul privilegiat și izolează componentele, dar siguranța reală depinde de configurarea corectă a permisiunilor și de aplicarea principiului privilegiului minim. Evaluați configurația concretă, nu doar clasificarea arhitecturală generică.
14Rezumat și glosar5 min
Sincronizarea corectă a task-urilor concurente cere excludere mutuală pentru resursele partajate (mutex) și mecanisme dedicate de semnalizare (semafor, grup de evenimente, coadă de mesaje) - alegerea greșită între ele poate reintroduce, tăcut, condiții de cursă sau poate elimina protecții de care aplicația depinde fără să știe. Când task-uri cu priorități diferite partajează o resursă, poate apărea inversiunea de prioritate; Priority Inheritance și Priority Ceiling sunt cele două protocoale clasice care o mărginesc, cu compromisuri diferite între simplitate și predictibilitate. La nivelul kernelului, arhitectura monolitică oferă latență minimă dar izolare slabă, microkernelul oferă izolare mai bună cu un cost de comunicare; MPU/MMU aduc suportul hardware necesar izolării, iar sistemele multicore (AMP/SMP) și cele cu criticitate mixtă complică semnificativ analiza temporală. Selecția unui RTOS concret trebuie ghidată de cerințele verificabile ale produsului, nu de popularitatea unei platforme.
15Întrebări de verificare6 min
- De ce operația
counter++nu e neapărat atomică, la nivel de procesor? - Care e diferența fundamentală dintre un mutex și un semafor?
- Descrieți scenariul minimal de inversiune de prioritate, cu trei task-uri.
- Cum reduce Priority Inheritance timpul de blocare al unui task de prioritate mare?
- Ce garantează în plus Priority Ceiling față de Priority Inheritance?
- Care e diferența principală dintre un kernel monolitic și un microkernel?
- Ce poate, și ce nu poate, detecta protecția memoriei prin MPU?
- Care e diferența dintre AMP și SMP pe un procesor multicore?
16Direcții de aprofundare2 min
Cursul următor trece de la sincronizarea și structura RTOS-ului la o direcție diferită: sistemele reconfigurabile și accelerarea hardware - de ce și când merită mutat un calcul din software în hardware dedicat (FPGA, accelerator), și ce compromisuri aduce această decizie.
Mutex-urile, semafoarele, grupurile de evenimente și protocoalele de control al priorității discutate aici devin cod real, verificabil pe hardware, în Laboratorul 03, alături de conceptele din Cursul 07.