1 .. SPDX-License-Identifier: GPL-2.0 2 3 .. include:: ../disclaimer-ita.rst 4 5 Validatore di sincronizzazione durante l'esecu 6 ============================================== 7 8 Classi di blocchi 9 ----------------- 10 11 L'oggetto su cui il validatore lavora è una " 12 13 Una classe di blocchi è un gruppo di blocchi 14 sincronizzazione, anche quando i blocchi potre 15 decine di migliaia). Per esempio un blocco nel 16 mentre ogni inode sarà un'istanza di questa c 17 18 Il validatore traccia lo "stato d'uso" di una 19 dipendenze con altre classi. L'uso di un blocc 20 usato rispetto al suo contesto d'interruzione, 21 possono essere interpretate come il loro ordin 22 che un processo cerca di acquisire L2 mentre g 23 vista di lockdep, i due blocchi (L1 ed L2) non 24 dipendenza indica solamente l'ordine in cui so 25 verifica permanentemente la correttezza dell'u 26 dipendenze, altrimenti ritornerà un errore. 27 28 Il comportamento di una classe di blocchi vien 29 istanze. Una classe di blocco viene registrata 30 istanza, mentre tutte le successive istanze ve 31 uso e le loro dipendenze contribuiranno a cost 32 classe di blocco non sparisce quando sparisce 33 rimossa quando il suo spazio in memoria viene 34 succede quando si rimuove un modulo, o quando 35 36 Stato 37 ----- 38 39 Il validatore traccia l'uso cronologico delle 40 l'uso in categorie (4 USI * n STATI + 1). 41 42 I quattro USI possono essere: 43 44 - 'sempre trattenuto nel contesto <STATO>' 45 - 'sempre trattenuto come blocco di lettura ne 46 - 'sempre trattenuto con <STATO> abilitato' 47 - 'sempre trattenuto come blocco di lettura co 48 49 gli `n` STATI sono codificati in kernel/lockin 50 includono: 51 52 - hardirq 53 - softirq 54 55 infine l'ultima categoria è: 56 57 - 'sempre trattenuto' 58 59 Quando vengono violate le regole di sincronizz 60 vengono presentati nei messaggi di errore di s 61 graffe, per un totale di `2 * n` (`n`: bit STA 62 63 modprobe/2287 is trying to acquire lock: 64 (&sio_locks[i].lock){-.-.}, at: [<c02867fd 65 66 but task is already holding lock: 67 (&sio_locks[i].lock){-.-.}, at: [<c02867fd 68 69 Per un dato blocco, da sinistra verso destra, 70 del blocco e di un eventuale blocco di lettura 71 precedentemente. Il carattere mostrato per ogn 72 73 === ====================================== 74 '.' acquisito con interruzioni disabilitat 75 '-' acquisito in contesto d'interruzione 76 '+' acquisito con interruzioni abilitate 77 '?' acquisito in contesto d'interruzione c 78 === ====================================== 79 80 Il seguente esempio mostra i bit:: 81 82 (&sio_locks[i].lock){-.-.}, at: [<c02867fd 83 |||| 84 ||| \-> softirq disab 85 || \--> acquisito in 86 | \---> hardirq disab 87 \----> acquisito in 88 89 Per un dato STATO, che il blocco sia mai stato 90 STATO, o che lo STATO sia abilitato, ci lascia 91 mostrati nella seguente tabella. Il carattere 92 esattezza in quale scenario ci si trova al mom 93 94 +---------------+---------------+----------- 95 | | irq abilitati | irq disabi 96 +---------------+---------------+----------- 97 | sempre in irq | '?' | '-' 98 +---------------+---------------+----------- 99 | mai in irq | '+' | '.' 100 +---------------+---------------+----------- 101 102 Il carattere '-' suggerisce che le interruzion 103 altrimenti verrebbe mostrato il carattere '?'. 104 fatta anche per '+' 105 106 I blocchi inutilizzati (ad esempio i mutex) no 107 un errore. 108 109 Regole dello stato per un blocco singolo 110 ---------------------------------------- 111 112 Avere un blocco sicuro in interruzioni (*irq-s 113 usato in un contesto d'interruzione, mentre un 114 (*irq-unsafe*) significa che è sempre stato a 115 abilitate. 116 117 Una classe softirq insicura è automaticamente 118 seguenti stati sono mutualmente esclusivi: sol 119 usata una classe di blocco:: 120 121 <hardirq-safe> o <hardirq-unsafe> 122 <softirq-safe> o <softirq-unsafe> 123 124 Questo perché se un blocco può essere usato 125 (sicuro in interruzioni), allora non può mai 126 interruzioni abilitate (insicuro in interruzio 127 verificarsi uno stallo. Per esempio, questo bl 128 essere rilasciato il contesto d'esecuzione vie 129 si tenterà di acquisirlo nuovamente. Questo p 130 particolare uno stallo ricorsivo. 131 132 Il validatore rileva e riporta gli usi di bloc 133 blocchi singoli. 134 135 Regole per le dipendenze di blocchi multipli 136 -------------------------------------------- 137 138 La stessa classe di blocco non deve essere acq 139 potrebbe portare ad uno blocco ricorsivo e dun 140 141 Inoltre, due blocchi non possono essere tratte 142 143 <L1> -> <L2> 144 <L2> -> <L1> 145 146 perché porterebbe ad uno stallo - chiamato st 147 cerca di trattenere i due blocchi in un ciclo 148 aspettano per sempre che l'altro termini. Il v 149 queste dipendenze cicliche di qualsiasi comple 150 potrebbero essere altre sequenze di blocchi. I 151 blocchi possono essere acquisiti circolarmente 152 153 In aggiunta, le seguenti sequenze di blocco ne 154 permesse, indipendentemente da quale che sia l 155 156 <hardirq-safe> -> <hardirq-unsafe> 157 <softirq-safe> -> <softirq-unsafe> 158 159 La prima regola deriva dal fatto che un blocco 160 trattenuto in un contesto d'interruzione che, 161 di interrompere un blocco insicuro in interruz 162 stallo da blocco inverso. La seconda, analogam 163 in interruzioni software potrebbe essere tratt 164 interruzione software, dunque potrebbe interro 165 interruzioni software. 166 167 Le suddette regole vengono applicate per quals 168 si acquisiscono nuovi blocchi, il validatore v 169 delle regole fra il nuovo blocco e quelli già 170 171 Quando una classe di blocco cambia stato, appl 172 173 - se viene trovato un nuovo blocco sicuro in i 174 abbia mai trattenuto dei blocchi insicuri in 175 176 - se viene trovato un nuovo blocco sicuro in i 177 verificheremo se abbia trattenuto dei blocch 178 software. 179 180 - se viene trovato un nuovo blocco insicuro in 181 abbia trattenuto dei blocchi sicuri in inter 182 183 - se viene trovato un nuovo blocco insicuro in 184 verificheremo se abbia trattenuto dei blocch 185 software. 186 187 (Di nuovo, questi controlli vengono fatti perc 188 potrebbe interrompere l'esecuzione di qualsias 189 stallo; questo anche se lo stallo non si verif 190 191 Eccezione: dipendenze annidate sui dati portan 192 ---------------------------------------------- 193 194 Ci sono alcuni casi in cui il kernel Linux acq 195 istanza di una classe di blocco. Solitamente, 196 gerarchia fra oggetti dello stesso tipo. In qu 197 implicitamente l'ordine fra i due oggetti (def 198 gerarchia), ed il kernel tratterrà i blocchi 199 ognuno degli oggetti. 200 201 Un esempio di questa gerarchia di oggetti che 202 i *block-dev* che rappresentano l'intero disco 203 sua partizione; la partizione è una parte del 204 blocchi sarà corretto fintantoche uno acquisi 205 poi quello della partizione. Il validatore non 206 ordine implicito, perché queste regole di sin 207 208 Per istruire il validatore riguardo a questo u 209 introdotte nuove primitive per specificare i " 210 esempio, per i blocchi a mutua esclusione dei 211 chiamata simile a:: 212 213 enum bdev_bd_mutex_lock_class 214 { 215 BD_MUTEX_NORMAL, 216 BD_MUTEX_WHOLE, 217 BD_MUTEX_PARTITION 218 }; 219 220 mutex_lock_nested(&bdev->bd_contains->bd_mut 221 222 In questo caso la sincronizzazione viene fatta 223 tratta di una partizione. 224 225 Ai fini della validazione, il validatore lo co 226 di blocco separata. 227 228 Nota: Prestate estrema attenzione che la vostr 229 vogliono usare le primitive _nested(); altrime 230 positivi che falsi negativi. 231 232 Annotazioni 233 ----------- 234 235 Si possono utilizzare due costrutti per verifi 236 devono essere trattenuti: lockdep_assert_held* 237 lockdep_*pin_lock(&lock). 238 239 Come suggerito dal nome, la famiglia di macro 240 che un dato blocco in un dato momento deve ess 241 generato un WARN()). Queste vengono usate abbo 242 esempio in kernel/sched/core.c:: 243 244 void update_rq_clock(struct rq *rq) 245 { 246 s64 delta; 247 248 lockdep_assert_held(&rq->lock); 249 [...] 250 } 251 252 dove aver trattenuto rq->lock è necessario pe 253 rq. 254 255 L'altra famiglia di macro è lockdep_*pin_lock 256 solo per rq->lock ATM. Se per caso un blocco n 257 genereranno un WARN(). Questo si rivela partic 258 verificare la correttezza di codice con *callb 259 potrebbero assumere che un blocco rimanga trat 260 potrebbero invece pensare che il blocco possa 261 riacquisito (involontariamente si apre una sez 262 restituisce 'struct pin_cookie' che viene usat 263 verificare che nessuno abbia manomesso il bloc 264 kernel/sched/sched.h abbiamo:: 265 266 static inline void rq_pin_lock(struct rq *rq 267 { 268 rf->cookie = lockdep_pin_lock(&rq->loc 269 [...] 270 } 271 272 static inline void rq_unpin_lock(struct rq * 273 { 274 [...] 275 lockdep_unpin_lock(&rq->lock, rf->cook 276 } 277 278 I commenti riguardo alla sincronizzazione poss 279 tuttavia sono le verifiche in esecuzione effet 280 vitali per scovare problemi di sincronizzazion 281 livello di informazioni quando si ispeziona il 282 queste annotazioni! 283 284 Dimostrazione di correttezza al 100% 285 ------------------------------------ 286 287 Il validatore verifica la proprietà di chiusu 288 ogni sequenza di sincronizzazione di un singol 289 una volta nel kernel, il validatore dimostrerà 290 nessuna combinazione e tempistica di queste se 291 una qualsiasi classe di blocco. [1]_ 292 293 In pratica, per dimostrare l'esistenza di uno 294 scenari di sincronizzazione multi-processore e 295 dimostrare la correttezza basandosi sulla sola 296 apparsa almeno una volta (in qualunque momento 297 contesto). Uno scenario complesso che avrebbe 298 sfortunata presenza di processi, interruzioni, 299 riprodotto su un sistema a singolo processore. 300 301 Questo riduce drasticamente la complessità de 302 sincronizzazione nel kernel: quello che deve e 303 kernel quante più possibili "semplici" sequen 304 volta, allo scopo di dimostrarne la correttezz 305 una verifica per ogni possibile combinazione d 306 e differenti scenari con hardirq e softirq e a 307 impossibile da fare) 308 309 .. [1] 310 311 assumendo che il validatore sia corretto al 312 del sistema possa corromperne lo stato. Ass 313 MNI/SMM [potrebbero interrompere anche perc 314 disabilitate] sono corretti e non interferi 315 assumiamo che un hash a 64-bit sia unico pe 316 sincronizzazione nel sistema. Infine, la ri 317 essere maggiore di 20. 318 319 Prestazione 320 ----------- 321 322 Le regole sopracitate hanno bisogno di una qua 323 durante l'esecuzione. Il sistema sarebbe diven 324 per la sua lentezza se le avessimo fatte davve 325 per ogni abilitazione delle interruzioni. La c 326 O(N^2), quindi avremmo dovuto fare decine di m 327 evento, il tutto per poche centinaia di classi 328 329 Il problema è stato risolto facendo una singo 330 sincronizzazione' (una sequenza unica di blocc 331 Per farlo, viene mantenuta una pila dei blocch 332 hash a 64-bit unico per ogni sequenza. Quando 333 la prima volta, l'hash viene inserito in una t 334 essere verificata senza bisogno di blocchi. Se 335 tabella ci dirà che non è necessario verific 336 337 Risoluzione dei problemi 338 ------------------------ 339 340 Il massimo numero di classi di blocco che il v 341 MAX_LOCKDEP_KEYS. Oltrepassare questo limite i 342 seguente avviso:: 343 344 (DEBUG_LOCKS_WARN_ON(id >= MAX_LOCKDEP 345 346 Di base questo valore è 8191, e un classico s 347 classi, dunque questo avviso è solitamente la 348 perdita delle classi di blocco o d'inizializza 349 descrizione dei due problemi: 350 351 1. caricare e rimuovere continuamente i moduli 352 esecuzione porterà ad una perdita di class 353 caricamento crea un nuovo insieme di classi 354 quel modulo. Tuttavia, la rimozione del mod 355 (vedi dopo perché non le riusiamo). Dunque 356 rimozione di un modulo non fa altro che aum 357 a raggiungere, eventualmente, il limite. 358 359 2. Usare array con un gran numero di blocchi c 360 inizializzati. Per esempio, una tabella has 361 il proprio spinlock_t consumerà 8192 class 362 esplicitamente inizializzati in esecuzione 363 dell'inizializzazione durante la compilazio 364 Sbagliare questa inizializzazione garantisc 365 blocco. Viceversa, un ciclo che invoca spin 366 mapperebbe tutti alla stessa classe di bloc 367 368 La morale della favola è che dovete sempre 369 vostri blocchi. 370 371 Qualcuno potrebbe argomentare che il validator 372 classi di blocco. Tuttavia, se siete tentati d 373 il codice e pensate alla modifiche necessarie, 374 di blocco da rimuovere probabilmente sono lega 375 facile a dirsi che a farsi. 376 377 Ovviamente, se non esaurite le classi di blocc 378 quella di trovare le classi non funzionanti. P 379 ritorna il numero di classi attualmente in uso 380 381 grep "lock-classes" /proc/lockdep_stat 382 383 Questo comando produce il seguente messaggio:: 384 385 lock-classes: 386 387 Se il numero di assegnazioni (748 qui sopra) a 388 allora c'è probabilmente un problema da qualc 389 essere utilizzato per identificare le classi d 390 391 grep "BD" /proc/lockdep 392 393 Eseguite il comando e salvatene l'output, quin 394 un'esecuzione successiva per identificare even 395 output può anche aiutarti a trovare situazion 396 blocco è stata omessa. 397 398 Lettura ricorsiva dei blocchi 399 ----------------------------- 400 401 Il resto di questo documento vuole dimostrare 402 possibilità di stallo. 403 404 Ci sono tre tipi di bloccatori: gli scrittori 405 spin_lock() o write_lock()), lettori non ricor 406 down_read()), e lettori ricorsivi (bloccatori 407 rcu_read_lock()). D'ora in poi, per questi tip 408 seguente notazione: 409 410 W o E: per gli scrittori (bloccatori esclu 411 *Writer*, ed E per *Exclusive*). 412 413 r: per i lettori non ricorsivi (r dall'ing 414 415 R: per i lettori ricorsivi (R dall'inglese 416 417 S: per qualsiasi lettore (non ricorsivi + 418 sono bloccatori condivisi (S dall'ingle 419 420 N: per gli scrittori ed i lettori non rico 421 non ricorsivi. 422 423 Ovviamente, N equivale a "r o W" ed S a "r o R 424 425 Come suggerisce il nome, i lettori ricorsivi s 426 permesso di acquisire la stessa istanza di blo 427 sezione critica di un altro lettore. In altre 428 stessa istanza di blocco nelle sezioni critich 429 430 Dall'altro canto, lo stesso comportamento indu 431 auto infliggersi uno stallo. 432 433 La differenza fra questi due tipi di lettori e 434 vengono bloccati solo dal trattenimento di un 435 non ricorsivi possono essere bloccati dall'att 436 Consideriamo il seguente esempio:: 437 438 TASK A: TASK B: 439 440 read_lock(X); 441 write_lock(X); 442 read_lock_2(X); 443 444 L'attività A acquisisce il blocco di lettura 445 o meno) usando read_lock(). Quando l'attività 446 X, si fermerà e rimarrà in attesa che venga 447 un tipo lettore ricorsivo, l'attività A conti 448 attesa non possono bloccare lettori ricorsivi, 449 Tuttavia, se read_lock_2() è un lettore non r 450 dall'attività B e si causerà uno stallo. 451 452 Condizioni bloccanti per lettori/scrittori su 453 ---------------------------------------------- 454 Essenzialmente ci sono quattro condizioni bloc 455 456 1. Uno scrittore blocca un altro scrittore. 457 2. Un lettore blocca uno scrittore. 458 3. Uno scrittore blocca sia i lettori ricorsiv 459 4. Un lettore (ricorsivo o meno) non blocca al 460 bloccare quelli non ricorsivi (perché potr 461 attesa). 462 463 Di seguito le tabella delle condizioni bloccan 464 tipo in riga blocca quello in colonna, mentre 465 466 +---+---+---+---+ 467 | | W | r | R | 468 +---+---+---+---+ 469 | W | Y | Y | Y | 470 +---+---+---+---+ 471 | r | Y | Y | N | 472 +---+---+---+---+ 473 | R | Y | Y | N | 474 +---+---+---+---+ 475 476 (W: scrittori, r: lettori non ricorsivi, R 477 478 Al contrario dei blocchi per lettori non ricor 479 trattenuti da chi trattiene il blocco di scrit 480 attende il rilascio. Per esempio:: 481 482 TASK A: TASK B: 483 484 read_lock(X); 485 486 write_lock(X); 487 488 read_lock(X); 489 490 non produce uno stallo per i lettori ricorsivi 491 in attesta del blocco X, mentre il secondo rea 492 aspettare perché si tratta di un lettore rico 493 fosse un lettore non ricorsivo, questo codice 494 495 Da notare che in funzione dell'operazione di b 496 particolare il valore del parametro 'read' in 497 essere di scrittura (blocco esclusivo), di let 498 condiviso e non ricorsivo), o di lettura ricor 499 ricorsivo). In altre parole, per un'istanza di 500 acquisizione che dipendono dalla funzione di a 501 lettura non ricorsiva, e di lettura ricorsiva. 502 503 In breve, chiamiamo "non ricorsivi" blocchi di 504 ricorsiva, mentre "ricorsivi" i blocchi di let 505 506 I blocchi ricorsivi non si bloccano a vicenda, 507 (anche in lettura). Un blocco di lettura non r 508 ricorsivo, e viceversa. 509 510 Il seguente esempio mostra uno stallo con bloc 511 512 TASK A: TASK B: 513 514 read_lock(X); 515 read_lock(Y); 516 write_lock(Y); 517 write_lock(X); 518 519 Il processo A attende che il processo B esegua 520 processo B attende che A esegua read_unlock() 521 522 Tipi di dipendenze e percorsi forti 523 ----------------------------------- 524 Le dipendenze fra blocchi tracciano l'ordine c 525 acquisita, e perché vi sono 3 tipi di bloccat 526 dipendenze. Tuttavia, vi mostreremo che 4 sono 527 stalli. 528 529 Per ogni dipendenza fra blocchi avremo:: 530 531 L1 -> L2 532 533 Questo significa che lockdep ha visto acquisir 534 contesto di esecuzione. Per quanto riguarda l' 535 interessa sapere se possiamo rimanere bloccati 536 In altre parole, vogliamo sapere se esiste un 537 da L1 e un L2 che viene bloccato da L3. Dunque 538 che L1 blocca e (2) quello che blocca L2. Di c 539 lettori ricorsivi e non per L1 (perché blocca 540 combinare scrittori e lettori non ricorsivi pe 541 dagli stessi tipi). 542 543 Con questa semplificazione, possiamo dedurre c 544 grafo delle dipendenze di lockdep: 545 546 1) -(ER)->: 547 dipendenza da scrittore esclusivo 548 significa X -> Y, dove X è uno sc 549 550 2) -(EN)->: 551 dipendenza da scrittore esclusivo 552 "X -(EN)->" significa X-> Y, dove 553 o uno scrittore o un lettore non r 554 555 3) -(SR)->: 556 dipendenza da lettore condiviso a 557 significa X -> Y, dove X è un let 558 lettore ricorsivo. 559 560 4) -(SN)->: 561 dipendenza da lettore condiviso a 562 "X -(SN)-> Y" significa X -> Y , d 563 o meno) e Y può essere o uno scri 564 565 Da notare che presi due blocchi, questi potreb 566 loro. Per esempio:: 567 568 TASK A: 569 570 read_lock(X); 571 write_lock(Y); 572 ... 573 574 TASK B: 575 576 write_lock(X); 577 write_lock(Y); 578 579 Nel grafo delle dipendenze avremo sia X -(SN)- 580 581 Usiamo -(xN)-> per rappresentare i rami sia pe 582 modo -(Ex)->, -(xR)-> e -(Sx)-> 583 584 Un "percorso" in un grafo è una serie di nodi 585 Definiamo un percorso "forte", come il percors 586 tipo -(xR)-> e -(Sx)->. In altre parole, un pe 587 blocco ad un altro attraverso le varie dipende 588 -> Y -> Z (dove X, Y, e Z sono blocchi), e da 589 o -(ER)->, allora fra Y e Z non deve esserci u 590 591 Nella prossima sezione vedremo perché definia 592 593 Identificazione di stalli da lettura ricorsiva 594 ---------------------------------------------- 595 Ora vogliamo dimostrare altre due cose: 596 597 Lemma 1: 598 599 Se esiste un percorso chiuso forte (ciclo fort 600 combinazione di sequenze di blocchi che causa 601 l'esistenza di un ciclo forte è sufficiente a 602 603 Lemma 2: 604 605 Se non esiste un percorso chiuso forte (ciclo 606 combinazione di sequenze di blocchi che causin 607 cicli forti sono necessari alla rilevazione de 608 609 Con questi due lemmi possiamo facilmente affer 610 è sia sufficiente che necessario per avere gl 611 alla possibilità di imbattersi concretamente 612 forte significa che può causare stalli, per q 613 sono anche cicli di dipendenze che non causera 614 615 Dimostrazione di sufficienza (lemma 1): 616 617 Immaginiamo d'avere un ciclo forte:: 618 619 L1 -> L2 ... -> Ln -> L1 620 621 Questo significa che abbiamo le seguenti dipen 622 623 L1 -> L2 624 L2 -> L3 625 ... 626 Ln-1 -> Ln 627 Ln -> L1 628 629 Ora possiamo costruire una combinazione di seq 630 stallo. 631 632 Per prima cosa facciamo sì che un processo/pr 633 un altro prende L2 in L2 -> L3, e così via. A 634 saranno trattenuti da processi/processori dive 635 636 Poi visto che abbiamo L1 -> L2, chi trattiene 637 ma prima dovrà attendere che venga rilasciato 638 L2 è già trattenuto da un altro processo/pro 639 L3 non sono -(xR)-> né -(Sx)-> (la definizion 640 in L1 -> L2 non è un bloccatore non ricorsivo 641 L2 -> L3 non è uno scrittore (che blocca chiu 642 643 In aggiunta, possiamo trarre una simile conclu 644 deve aspettare che L3 venga rilasciato, e cosà 645 chi trattiene Lx deve aspettare che Lx+1 venga 646 L1, dunque si è creato un ciclo dal quale non 647 stallo. 648 649 Dimostrazione della necessità (lemma 2): 650 651 Questo lemma equivale a dire che: se siamo in 652 deve esiste un ciclo forte nel grafo delle dip 653 654 Secondo Wikipedia[1], se c'è uno stallo, allo 655 ovvero ci sono N processi/processori dove P1 a 656 e P2 ne aspetta uno trattenuto da P3, ... e Pn 657 rilasciato. Chiamiamo Lx il blocco che attende 658 trattiene Ln. Quindi avremo Ln -> L1 nel grafo 659 nel grafo delle dipendenze avremo L1 -> L2, L2 660 significa che abbiamo un ciclo:: 661 662 Ln -> L1 -> L2 -> ... -> Ln 663 664 , ed ora dimostriamo d'avere un ciclo forte. 665 666 Per un blocco Lx, il processo Px contribuisce 667 contribuisce a quella Lx -> Lx+1. Visto che Px 668 impossibile che Lx in Px+1 sia un lettore e ch 669 ricorsivo. Questo perché i lettori (ricorsivi 670 ricorsivi. Dunque, Lx-1 -> Lx e Lx -> Lx+1 non 671 -(xR)-> -(Sx)->. Questo è vero per ogni ciclo 672 673 Riferimenti 674 ----------- 675 676 [1]: https://it.wikipedia.org/wiki/Stallo_(inf 677 678 [2]: Shibu, K. (2009). Intro To Embedded Syste
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.