# TinyOS

TinyOS è un sistema operativo per i mote, i nodi delle reti wireless di sensori (wireless sensor network). È sotto sviluppo da parte di un consorzio guidato dall'Università della California - Berkeley, in collaborazione con Intel Research e nasce per far parte della tecnologia Smart Dust. TinyOS è scritto nel linguaggio di programmazione NesC con il quale si evolve in simbiosi.

## Modello di concorrenza

TinyOS è un sistema operativo totalmente non bloccante e l'intero ciclo di vita del software in esecuzione è di tipo event-driven. Ogni evento viene associato ad un interrupt che a sua volta è legato ad una sezione di codice di tipo Asynchronous Code -AC-. Sezioni di tipo AC possono essere invocata solo dagli interrupt e di conseguenza queste routine (riconoscibili poiché decorate tramite la keyword event) entrano a far parte del core del sistema stesso (possono essere pensate come le routine che vengono richiamate tramite il vettore degli interrupt in un sistema operativo standard).
L'assenza di primitive bloccanti è un grosso limite poiché può portare a situazioni di race condition. Tuttavia, nesC "risolve" tramite un workaround il problema permettendo l'uso della keyword "Atomic" all'interno del codice AC: la keyword può essere usata per decorare blocchi di codice da eseguire senza interruzioni. L'implementazione di questo costrutto richiede la disattivazione di tutti gli interrupt, l'esecuzione del codice (monopolizzando l'intero sistema) e la successiva riattivazione degli interrupt: pertanto, viene consigliato di rendere le sezioni atomiche quanto più piccole possibile per evitare di perdere eventi che nel frattempo potrebbero verificarsi.

## Modello Split-phase

Tutte le operazioni non delegate a servire gli interrupt (richieste di I/O verso una periferica, richieste di attivazione di porte o comunque routine personali) vengono eseguite un modo asincrono: è prevista una fase di registrazione della richiesta presso lo scheduler, una fase di esecuzione della richiesta (di tipo SC, eseguita in modo sincrono monopolizzando l'intero sistema) ed una fase di segnalazione tramite callback di operazione completata; il termine callback non deve trarre in inganno poiché sono eventi e dunque vanno trattate allo stesso modo.
Il modello split-phase (che in inglese significa "a fase spaccata") è esplicitamente progettato per gestire operazioni che richiedono tempi nell'ordine dei microsecondi per essere completate. Queste operazioni vanno identificate tramite la keyword Task in modo da poterle distinguere dalle operazioni di tipo Event (hanno livelli di priorità diversi)

## Lo scheduler

Il nucleo (kernel) ha uno scheduling a due livelli gerarchici: uno per gli eventi e uno per i task. Gli eventi hanno diritto di prelazione reciproco e sui task. Un evento potrebbe interrompere un altro evento pertanto per evitare situazioni di race condition è necessario l'utilizzo della keyword Atomic.
L'esecuzione dei task segue una politica First-Come First-Served tradizionale, senza diritto di prelazione (nonpreemptive).
Questo semplice modello di concorrenza funziona bene per applicazioni con prevalenza di operazioni di I/O (applicazioni I/O bound) ma può generare delle difficoltà per applicazioni con prevalenza di elaborazioni (applicazioni CPU bound). Sono state avanzate delle proposte per l'incorporazione del multi-threading al livello d'utente (a livello di sistema esistono dei thread di controllo) nel sistema, proprio per far fronte ad applicazioni CPU bound, ma è stato dimostrato che ciò avrebbe un impatto negativo sulle prestazioni del sistema stesso. Ad ogni modo, per motivazioni legate al risparmio energetico, i mote delle reti wireless di sensori non dovrebbero svolgere elaborazioni intensive. Uno dei primi suggerimenti di TinyOS programming, il manuale di riferimento per i programmatori, recita: "Keep tasks short", "fai in modo che le sezioni di codice dei task siano brevi".
Gli scopi dichiarati dei progettisti di TinyOS sono:
* ridurre i consumi di energia;
* ridurre il sovraccarico computazionale (overhead);
* minimizzare le dimensioni del sistema operativo;
* supportare le richieste di operazioni frequenti e concorrenti con un alto livello di robustezza ed efficienza.
Per raggiungere questi scopi sono state fatte delle scelte che escludono l'utilizzo dei meccanismi delle interruzioni software e dei cambi di contesto (context switch); tali scelte massimizzano il rendimento della CPU, risorsa costosa in termini energetici. All'atto della compilazione di un programma NesC, il codice dell'utente e quello delle componenti di sistema utilizzate vengono collegati staticamente e tradotti dal compilatore NesC in un unico file sorgente C; questo viene quindi processato dal compilatore C nativo che genera un unico file binario di dimensioni ridotte. Il sistema ha un unico stack riservato alle variabili locali e uno spazio di memoria riservato alle variabili globali (data). La memoria destinata al codice (text) del file binario eseguibile è assegnata in modo contiguo.