Sommario Ringraziamenti...................................................................................................................3 Introduzione.......................................................................................................................4 1 Sistemi operativi Realtime..............................................................................................6 1.1 Concetto di Realtime...............................................................................................6 1.2 RealTime OS – Hard vs Soft Realtime...................................................................8 1.3 Panoramica sui Sistemi operativi RealTime...........................................................8 1.4 Content Switch, Stack, Task Control Block, Preemption e Timeslices................10 1.5 Concorrenza..........................................................................................................14 1.6 Interrupt.................................................................................................................15 1.7 Scheduler...............................................................................................................16 2 Caratteristiche di eCos..................................................................................................17 2.1 Dove è cominciato tutto – Cygnus Solutions........................................................17 2.2 L'origine di eCos...................................................................................................18 2.2.1 Configurabilità e configurazione...................................................................18 2.2.2 I componenti chiave di eCos.........................................................................20 2.2.3 Supporto a processori e piattaforme di valutazione......................................21 2.2.4 Supporto tecnico ad eCos..............................................................................22 2.3 Visione d'insieme del sistema eCos......................................................................23 2.4 Terminologia di eCos............................................................................................23 2.4.1 Il component framework...............................................................................24 2.4.2 Component Repository..................................................................................25 2.4.3 Opzioni di Configurazione............................................................................27 2.4.4 Pacchetti e componenti..................................................................................28 2.4.5 Target............................................................................................................29 2.4.6 Template........................................................................................................29 3 Caratteristiche di eCos..................................................................................................31 3.1 Portabilità..............................................................................................................31 3.2 Caratteristiche di base di HAL..............................................................................31 3.2.1 Funzionalità di HAL.....................................................................................33 3.2.2 Configurazione di HAL.................................................................................34 3.2.2.1 Componenti di configurazione comuni.................................................34 3.2.2.2 Componenti di configurazione specifici dell'architettura......................35 3.2.3 Inizializzazione di HAL................................................................................36 3.3 Il kernel di eCos....................................................................................................39 3.4 Meccanismi di gestione del tempo in eCos...........................................................40 3.5 Scheduler nativi in eCos.......................................................................................40 3.5.1 Bitmap Scheduling........................................................................................40 3.5.2 MLQ scheduler..............................................................................................42 3.6 Sincronizzazione dei thread con eCos..................................................................43 4 Esempio di applicazione usando eCos..........................................................................44 4.1 Processo di build di eCos......................................................................................44 4.2 Un'occhiata più da vicino......................................................................................44 4.3 Setup dell'hardware di sviluppo............................................................................48 4.4 Tool di configurazione di eCos.............................................................................49 4.5 Redboot.................................................................................................................49 4.5.1 Installazione di RedBoot...............................................................................51 4.5.2 Boot di RedBoot............................................................................................51 1 4.6 Processo di build di eCos......................................................................................52 4.7 Applicazioni..........................................................................................................53 Considerazioni finali e Conclusioni................................................................................55 Riferimenti Bibliografici.................................................................................................59 2 Ringraziamenti Ringrazio la Prof. Leonardi e il Prof. Valenti per la pazienza e la disponibilità che hanno avuto nei miei confronti durante il periodo di stesura dell'elaborato. Ringrazio immensamente Alfonso e Patrizia, i miei genitori, per la fiducia dimostrata in questi anni e per la sopportazione delle mie frequenti crisi universitarie; senza di loro non sarei mai arrivato fino a questo punto e quindi a loro va tutto il mio rispetto, la mia riconoscenza e la mia stima. Ringrazio Alex, mio fratello, anche lui ha mi sopportato parecchio, soprattutto nelle mie richieste di aiuto notturne pre-esame e Cristiana, sua moglie, che più di una volta si è data da fare per darmi aiuto con gli esami. Ringrazio poi mia nonna, i miei zii e i miei cugini che si sono sempre interessanti di come stesse procedendo l'università. Ringrazio tutti i miei amici, in particolar modo Mattia, Lancia, Della, Menga e Bobby con cui ho passato diverse ore a studiare; si sono dimostrati veri amici e credo potrò sempre contare su di loro. Non posso poi non menzionare Kenzo e Ruset per le goliardiche serate passate assieme. Un ulteriore grazie a tutti i compagni di attività sportive, specialmente quelli della pallanuoto con cui sono rimasto in contatto nonostante l'abbia dovuta abbandonare. Devo un grazie anche alle mie amiche Elisa e Alice che mi hanno sempre ascoltato e hanno sempre avuto parole di conforto quando ne avevo bisogno. Infine un grazie a tutti coloro che in qualche modo hanno creduto in me e mi hanno sostenuto. 3 Introduzione Un sistema operativo (SO, o in inglese OS) è il componente di infrastruttura software di un sistema informatico (computer) responsabile della gestione, del coordinamento delle attività e la condivisione delle risorse hardware del computer. Il sistema operativo mette a disposizione dell'utente una interfaccia software (grafica o testuale) per accedere alle risorse hardware (dischi, memoria, I/O in generale) del sistema. Il compito principale del sistema operativo è quello di permettere all'utente, umano o non, di interagire direttamente con la macchina. Quando invece parliamo di sistema operativo realtime, intendiamo un sistema operativo multitasking (che permette l'esecuzione contemporanea di più processi) programmato per applicazioni realtime cioè genericamente con vincoli temporali. Queste applicazioni includono sistemi embedded (progettati per uno specifico scopo), robot industriali, macchine a controllo numerico, equipaggiamento scientifico, etc. Come si vedrà nel capitolo uno del presente elaborato, un sistema operativo realtime rende possibile la creazione di un sistema realtime, ma non garantisce che il risultato finale sia realtime; questo richiede infatti il corretto sviluppo del software. Un sistema operativo realtime non deve necessariamente avere grandi capacità elaborative quanto piuttosto garantire di riuscire a fare il proprio lavoro entro i tempi prefissati. I SO realtime sono valutati in modo migliore più velocemente e/o deterministicamente rispondono a particolari eventi piuttosto che alla quantità di lavoro che riescono a svolgere in un determinato periodo. Nel capitolo uno inoltre vedremo poi come un sistema realtime riesce attraverso algoritmi di scheduling (organizzazione del lavoro) a fornire gli strumenti che gli permettono di essere predicibile nel tempo. La diffusione di dispositivi embedded che utilizzano sistemi operativi realtime è sicuramente sottostimata dalla popolazione media; questo perché sono generalmente “nascosti”. Solitamente hanno risorse limitate e sono studiati per avere una bassa interazione con l'uomo, oppure quando permettono l'interazione, questa avviene in maniera diversa rispetto a quella classica mouse-tastiera-monitor. L'interazione, nei casi di sistemi operativi realtime per dispositivi embedded, avviene solitamente con attuatori (pulsanti) e/o sensori. Un esempio di dispositivi commerciali che fanno uso di questo tipo di sistemi operativi sono: lettori mp3, console portatili da gioco, stampanti, fotocamere, dispositivi GPS, robot, telefoni cellulari etc. 4 Vista la diffusione di questi sistemi operativi, nel capitolo due andremo ad introdurre eCos, un sistema operativo realtime, open source, royalty-free per sistemi embedded, che viene utilizzato per il tipo di applicazioni poc'anzi descritte. Vedremo come e perché è nato, come è strutturato, quali sono i suoi componenti chiave, come si configura e con quali piattaforme hardware può essere utilizzato. Il capitolo tre è dedicato all'approfondimento delle caratteristiche chiave di eCos: il kernel, HAL, lo scheduler e i meccanismi di sincronizzazione dei thread. Nel quarto capitolo poi andremo a vedere un esempio di processo di build di eCos, cioè come si riesce attraverso l'ambiente fornito da eCos a realizzare una propria applicazione che girerà poi su una piattaforma hardware reale. Infine l'elaborato si conclude con alcune considerazioni e commenti relativi a quanto appreso. 5 1 Sistemi operativi Realtime 1.1 Concetto di Realtime Nel panorama informatico vi sono molteplici definizioni di realtime, ma il più di esse sono contraddittorie. Il tema è piuttosto discusso poiché non si trova un accordo sulla terminologia. La definizione canonica di un sistema realtime, data da Donald Gillies (matematico e informatico), è la seguente: “Un sistema è realtime quando la correttezza della computazione dipende non solo dalla correttezza logica della computazione, ma anche dal tempo richiesto per produrre il risultato di questa. Se la restrizione temporale del sistema non sono rispettate, questo può portare alla crisi del sistema”. Questa definizione, ha portato altri ad aggiungere: “è essenziale che che le restrizioni temporali del sistema abbiano una garanzia che vengano rispettate. Questa garanzia richiede che il sistema sia di tipo prevedibile. Sarebbe inoltre gradito che il sistema mantenga un altro grado di utilizzazione mentre soddisfa le restrizioni temporali”. Il classico esempio di software che deve essere realtime è quello del sistema frenante ABS: se non viene azionato in tempo il sistema ABS, questo può causare un incidente. Quindi è estremamente importante che i progettisti di sistemi realtime conoscano tutte le possibili casistiche di funzionamento del sistema in modo da poterne prevedere il comportamento. Per questo motivo i progettisti spendono gran parte del loro tempo a studiare quello che è definito il “caso peggiore” in termini prestazionali. L'istituto degli ingegneri elettrici ed elettronici (IEEE) definisce invece il concetto di realtime attraverso lo standard POSIX 1003.1: “Real time in un sistema operativo: l'abilita del sistema operativo di soddisfare un determinato carico di lavoro in un determinato tempo di risposta”. Anche il dipartimento di ingegneria aerospaziale del politecnico di Milano dà la sua definizione di sistema realtime: “un sistema capace di garantire requisiti temporali del processo sotto il suo controllo”. Il sistema deve quindi essere veloce e predicibile. Veloce significa con basse latenze, per esempio nel rispondere ad eventi esterni asincroni in tempi ridotti. Più è bassa la latenza, meglio il sistema risponde ad eventi che richiedono attenzione immediata. Predicibile significa che deve essere possibile determinare il periodo di completamento del processo in un tempo certo. 6 Un'altra interessante definizione di sistema operativo realtime (o RTOS) è quella data dal prof. Claudio Melchiorri del DEIS (Dipartimento di Elettronica, Informatica e Sistemistica dell'università di Bologna): “un RTOS è un sistema in cui per valutare la correttezza delle operazioni si considera anche la variabile tempo”. Devono essere garantite quindi due tipologie di correttezza: la correttezza logica e quella temporale. La prima implica che i risultati e le risposte forniti dal sistema siano quelli previsti (giusta computazione dei dati), mentre la seconda deve fare in modo che i dati siano prodotti entro certi limiti temporali fissati (deadlines). Nel marzo 2003, presso la nostra Università, si è tenuto un seminario sulla progettazione dei sistemi embedded e realtime, durante il quale il Prof. Valenti ha illustrato il suo punto di vista sulle caratterizzazioni realtime: “Le specifiche tecniche dei sistemi embedded contengono spesso vincoli sui tempi di risposta agli eventi esterni tali, da soli, a giustificare l'avvio di una progettazione hardware dedicata.” Tali vincoli temporali, indicati dal Prof. Valenti, possono essere di vario tipo: • Deadline: è la richiesta temporale più restrittiva; consiste nella pretesa di assicurare un tempo massimo di risposta prevedibile e sempre inferiore ad un certo limite fra il presentarsi di un evento e la generazione dell'evento effetto. Si consideri, come esempio, un sistema di controllo dei freni di un veicolo che, in alcun caso, può ammettere un ritardo imprevedibile fra il comando di frenatura e l'attuazione dello stesso. • Average: è una richiesta piuttosto generica che consiste nello specificare un ritardo tipico fra evento stimolo ed evento effetto. Questo si traduce nel dimensionamento del tempo medio di risposta in una situazione standard senza considerare il caso peggiore che potrebbe anche essere notevolmente più lento. • Burst: è una richiesta che non influisce sul tempo di risposta ma, piuttosto, sulla capacita del sistema di non perdere eventi, eventualmente utilizzando strumenti di accodamento hardware (fifo o similari). Si consideri, ad esempio, un frame grabber che deve acquisire una immagine senza, ovviamente, perdere alcun pixel, pur potendo impiegare un tempo variabile nella successiva elaborazione dell'informazione. I sistemi che hanno almeno un vincolo del tipo deadline sono identificati come sistemi hard realtime mentre quelli che hanno almeno un vincolo "average" o "burst" sono detti sistemi soft realtime. Nel caso in cui le specifiche funzionali non contengano alcun vincolo temporale, invece, si ha un sistema senza caratterizzazione realtime. 7 1.2 RealTime OS – Hard vs Soft Realtime Una volta introdotto il concetto di realtime, possiamo approfondire l'argomento andando ad analizzare le due modalità appena annunciate: la modalità hard-realtime e softrealtime. La modalità hard-realtime considera inutile il completamento dell'operazione dopo la sua deadline temporale, poiché questo può portare (nell'ambito di utilizzo) a un guasto completo del sistema. I sistemi hard-realtime sono usati quando è imperativo che il completamento dell'elaborazione rientri nei tempi prestabiliti. Questi sistemi sono rivolti ad ambiti in cui il non rispetto della deadline può causare danni fisici al sistema e all'ambiente che lo circonda: per esempio un pacemaker o il sistema di frenata di un auto. Nei sistemi soft-realtime invece possiamo pensare che le deadline vengano mediamente rispettate, ma non vi sono garanzie che questo accada, né si possono fare previsioni deterministiche sui tempi di esecuzione delle operazioni successive. I sistemi softrealtime, sono tipicamente usati dove vi è un accesso di tipo concorrenziale dei dati con più sistemi interconnessi tra di loro che ne fanno uso. Un esempio tipico è quello del sistema che sincronizza i voli aerei di linea: i piani aerei devono rimanere costantemente aggiornati, ma si può operare senza danni anche con latenze di qualche secondo. Generalmente anche i sistemi audio-video sono di questo tipo: se la garanzia temporale non viene rispettata, si avrà un degrado della qualità del segnale, ma il sistema può continuare a funzionare. 1.3 Panoramica sui Sistemi operativi RealTime Innanzitutto dobbiamo definire cosa rende realtime un sistema operativo (RTOS): • un RTOS deve essere multi-threaded e preemptible. Deve cioè permettere l'esecuzione di più thread e deve essere munito di un sistema che permetta l'interruzione temporanea di un processo che è in esecuzione senza la sua cooperazione. Tale processo verrà poi ripristinato quando il processo a priorità più alta che è stato messo in esecuzione al suo posto, ha concluso le sue operazioni. 8 • Deve essere presente un meccanismo di priorità. Nella progettazione del sistema, si sa a priori che alcuni thread hanno più priorità di esecuzione rispetto ad altri; il sistema operativo deve prevedere un meccanismo di gestione di queste priorità. • L'OS deve supportare un meccanismo di sincronizzazione dei thread di tipo prevedibile (per esempio mutex, semafori, flags, monitor, etc..). • Il comportamento deve essere noto. Ciò implica che i produttori di RTOS rilascino determinate specifiche: • massima latenza nel servire una richiesta di interrupt, determinato verificando sia analiticamente che sperimentalmente il numero di cicli macchina (clock) che intercorre nel caso peggiore fra l'IRQ (interrupt request) e l'esecuzione della prima istruzione della ISR (interrupt service routine): questo deve essere prevedibile, ripetibile e compatibile con i requisisti dell'applicazione. Questo valore, detto interrupt latency, non dipende dal numero di interrupt simultanei in attesa essendo per definizione quello del caso peggiore (worst case). • il massimo tempo che richiede ogni chiamata di sistema (system call). Questo valore può essere eventualmente dato in forma tabulare per determinate configurazioni, ad esempio il numero totale di task o oggetti di sistema. Il progettista ne dovrà tenere conto. • il tempo massimo di mascheramento degli interrupt da parte di OS e drivers. Lo sviluppatore ha inoltre la completa responsabilità di caratterizzare opportunamente dal punto di vista temporale ogni elemento che possa introdurre ritardi nel processamento degli eventi da trattare in realtime – ad esempio dovrà essere posta molta attenzione ai canali DMA ed all'eventuale adozione di cache per dispositivi di memoria intrinsecamente lenti (DRAM). L'offerta commerciale di sistemi operativi Real Time è piuttosto ampia. Senza entrare troppo nel dettaglio, andremo ad elencare quelli più conosciuti: • eCos (Red Hat) • LynxOS (LynuxWorks) • Neutrino (QNX Software System) • Nucleus (Mentor Graphics) • pSOSystem (Wind River) 9 • QNX (QNX Software System) • Quadros (RTXC) • Linux/RT (TimeSys) • VxWorks (Wind River) • RTAI (Politecnico di Milano) 1.4 Content Switch, Stack, Task Control Block, Preemption e Timeslices Ogni sistema operativo per permettere l'esecuzione contemporanea di thread e processi (sistema operativo multitasking), ha bisogno di un sistema che salvi lo stato della CPU nei momenti in cui si deve eseguire un cambio di processo attivo. Questo stato, cioè il contenuto dei registri della CPU e del program counter, è chiamato contesto e l'operazione di cambio contesto è detto context switch. Un registro è una piccola quantità di memoria molto veloce che risiede all'interno della CPU che è usata per velocizzare l'esecuzione di programmi, fornendo un accesso veloce ai dati più usati, solitamente durante l'elaborazione. Il program counter invece è un registro specifico che memorizza l'indirizzo di memoria della prossima istruzione che deve essere eseguita. Si noti che su alcune architetture, il program counter memorizza l'indirizzo dell'istruzione che si sta eseguendo. Il contesto normalmente si trova su una porzione dello stack di ogni processo e concorre a formare il task control block (TCB) insieme a informazioni di carattere più generale come il nome del task, la priorità o dati statistici di esecuzione utili per il debug (figura 1.1). 10 Figura 1.1: Contesto e Task Control Block Lo stack è infatti una struttura dati con politica di accesso di tipo LIFO (Last Input First Output) cioè i dati vengono letti in modo inverso rispetto a cui sono stati scritti. Oltre al contesto, lo stack può contenere: • variabili automatiche • indirizzo di ritorno di chiamate (call) • argomenti relativi ai registri Il meccanismo del context switch è una caratteristica fondamentale dei sistemi operativi multitasking poiché anche in caso di CPU singola, si ha l'illusione di avere l'esecuzione simultanea di due o più processi: la rapida esecuzione di questo meccanismo (centinaia o migliaia di volte al secondo) dà appunto questa illusione (figura 1.2). Figura 1.2: Meccanismo del context switch Il cambio di contesto può avvenire come risultato di un processo che ha volontariamente rinunciato al suo tempo disponibile di CPU, come decisione presa dallo scheduler in conseguenza del fatto che il processo ha terminato il timeslice di CPU oppure in seguito ad una preemption causata dall'arrivo di un thread a priorità più alta. Il timeslice è un periodo di tempo predefinito in fase di progettazione entro il quale ogni processo ha il 11 permesso di funzionare; al termine di questo tempo ovviamente il lavoro di quest'ultimo non viene perduto, ma viene congelato per poi essere ripreso al momento opportuno. Lo scheduler è quella parte del sistema operativo che si occupa di scegliere quale processo mandare in esecuzione (riesumandolo o facendolo partire ex novo). Nelle figure 1.3 e 1.4 viene illustrato per una particolare architettura (H8S), la situazione della CPU idle (libera) con il task 1 pronto e poi con il task 1 in esecuzione. È importante notare che il context switch è generalmente abbastanza avido di risorse, richiedendo un tempo di CPU considerevole (qualche microsecondo) per ogni cambio di contesto; se da una parte infatti il diminuire del tempo di timeslice potrebbe darci maggiore illusione di esecuzione contemporanea di processi, dall'altra ci porta ad un degrado prestazionale causato dalle richieste di sistema effettuate durante il cambio di contesto. Vediamo ora i punti essenziali del context switch partendo dal caso di avere due task, task 1 e task 2, di cui task 1 è in esecuzione (figura 1.4) e subisce preemption a causa dell'arrivo del task 2 più prioritario. 1. Sospensione del processo corrente attraverso il salvataggio del contesto 2. Recupero del contesto del prossimo processo da mettere in esecuzione 3. Inserimento del contesto appena recuperato nei registri della CPU (figura 1.5) 4. Ritorno alla locazione di memoria puntata dal program counter (ritorno alla linea di codice che rimette in esecuzione il processo che era stato interrotto) e quindi dopo al context switch il processo in esecuzione è il task 2 (figura 1.6). Figura 1.3: Stato della CPU con il task1 pronto 12 Figura 1.4: Figura – il task 1 si mette in esecuzione Figura 1.5: il task 2 a priorità più alta di task 1 effettua un Context Switch 13 Figura 1.6: Il Task 2 è in esecuzione 1.5 Concorrenza La gestione del multitasking in un sistema operativo è tanto importante quanto delicata. Per capire meglio come si gestisce il multitasking è necessario mettere in evidenza la differenza che c'è tra thread e processi. Mentre i processi sono solitamente indipendenti tra di loro, utilizzando diverse aree di memoria, i thread condividono risorse di sistema, memoria e informazioni di stato. La progettazione di applicativi concorrenti in un ambiente orientato ai processi richiede dei servizi di sistema (system call) dedicate alla comunicazione come le pipe o i socket di unix. La memoria viene infatti solitamente gestita attraverso MMU (Memory Managment Unit). In questo caso non sono richiesti particolari accorgimenti nel codice applicativo per assicurare la consistenza dei dati scambiati in quanto è il kernel ad occuparsi di questo (atomicità della read o della write, accessi concorrenti). La progettazione degli stessi applicativi in un ambiente orientato ai thread invece viene da un lato agevolata dalla condivisione della memoria; ogni thread infatti può accedere alla medesima locazione di memoria per leggere o modificarne il contenuto senza richiedere alcun servizio al SO. Il software applicativo deve però disporre di alcuni 14 strumenti per assicurare la consistenza dei dati condivisi. Ad esempio: due thread accedono ad una variabile di tipo “char” che normalmente (dipendente comunque dall'architettura), viene acceduta con una istruzione non interrompibile. In questo caso non esistono particolari problemi in quanto in un accesso concorrente in scrittura si perde il precedente valore ma si mantiene l'ultimo scritto. Ipotizziamo comunque che questa variabile di tipo char (detto comunemente tipo atomico) debba essere incrementata da due thread concorrenti. Il thread A legge il valore corrente della variabile (ad esempio 0) poi si accinge a scrivervi il nuovo valore (1). Nel mentre il thread B opera una preemption di A e decide di fare lo stesso, quindi legge ancora 0 (A non ha ancora scritto il suo nuovo valore) e scrive il suo nuovo valore 1. A questo punto il thread A viene ripreso ed esegue la scrittura di 1 mentre il doppio incremento avrebbe richiesto la scrittura del valore 2. I thread A e B devono essere sincronizzati per evitare questo tipo di comportamento che facilmente degenera se il tipo di dati acceduti non è atomico o se le operazioni su questi sono più complesse del semplice incremento. Il SO deve mettere a disposizione dei meccanismi di sincronizzazione (in questo caso dei mutex) e il software applicativo deve utilizzarli; è quindi responsabilità del programmatore/sistemista evitare il crearsi di situazioni quali race condition (corse critiche) e deadlock (stallo). 1.6 Interrupt Lo stato di interruzione rappresenta un contesto aggiuntivo a quello dei processi o dei thread. Si definisce interrupt un tipo particolare di interruzione della CPU che provoca l'interruzione di un processo qualora si verifichino determinate condizioni oppure il processo in esecuzione debba effettuare una richiesta al sistema operativo. È come un segnale o messaggio, generalmente di natura asincrona, che arriva alla CPU per avvisarla del verificarsi di un certo evento. Ci sono due tipi di interrupt: • Interrupt hardware generati da dispositivi esterni alla CPU, che hanno il compito di comunicare il verificarsi di eventi esterni, di solito dispositivi di Input/Output. • Interrupt software che sono delle istruzioni assembly, tipo INT xx o SYSCALL, che possono essere assimilate alle chiamate di sottoprogrammi, ma che sfruttano il meccanismo delle interruzioni per passare il controllo dal programma 15 chiamante a quello chiamato, e viceversa; vengono utilizzati per accedere direttamente alle risorse del sistema operativo. L'uso di interrupt è una tecnica ampiamente utilizzata in sistemi multitasking e in particolare in sistemi di tipo realtime. Quando si presenta un interrupt il SO ha poca libertà di azione in quanto normalmente è bloccata la rilevazione di altri eventi asincroni. Come è stato detto, nei sistemi realtime il tempo massimo di blocco di una interruzione, deve essere dichiarato. Per un sistema operativo general purpose come UNIX o Linux, il trattamento dell'interruzione viene eseguito dal kernel stesso, eventualmente delegando parte della elaborazione ai device drivers. In un sistema operativo realtime invece, il trattamento viene solitamente delegato all'applicativo che è stato scritto in modo che sappia come deve operare. Il kernel mette solo a disposizione i mezzi per sincronizzare i thread con l'evento (segnalazione di semafori, accodamento dei dati, schedulazione di thread). 1.7 Scheduler Sebbene sia già stata messa in evidenza l'importanza dello scheduler, cerchiamo di fare un po' di chiarezza sul comportamento nei sistemi operativi convenzionali, nei sistemi operativi soft-realtime e nei sistemi operativi hard-realtime. Per i primi possiamo dire che il meccanismo di schedulazione ha l'obiettivo di ottimizzare le prestazioni globali del sistema, per esempio cercando il compromesso tra la minimizzazione dell'overhead (cioè richieste aggiuntive a quelle strettamente necessarie) e la distribuzione delle risorse di processore ai vari task. Invece nei sistemi soft-realtime si cerca di ottimizzare i tempi di risposta, mentre nei sistemi hard-realtime lo scheduler ha il compito di garantire il rispetto delle deadline. In questi ultimi sistemi, interruzioni e processi hanno tutti una priorità definita in quanto è imperativo che vi sia sempre la corrispondenza tra tempo fisico e tempo di clock. Riassumendo possiamo dire che lo scheduler provvede a scegliere quale thread mettere in esecuzione ogni qualvolta un evento interno od esterno modifichi lo stato globale del sistema. 16 2 Caratteristiche di eCos 2.1 Dove è cominciato tutto – Cygnus Solutions Cygnus Solutions, fu fondata nel 1989 da John Gilmore, Michael Tiemann e David Henkel-Wallace con lo scopo di fornire supporto commerciale al free software. Il suo motto era: “Making free software affordable”, cioè “Rendiamo il free software conveniente”. Cygnus è un acronimo di "Cygnus, Your GNU Support", "Cygnus, il Tuo Supporto a GNU”. L'idea che ebbero questi sviluppatori fu quella di concentrare il loro lavoro sulla creazione di un piccolo set di strumenti open source che sarebbe poi stato venduto. Iniziarono quindi vendendo il compilatore GNU (GCC) e il debugger (GDB) come un piccolo pacchetto software. Questo fu possibile grazie all'enorme talento di Tiemmann, il quale contribuì a numerosi port del compilatore GNU e scrisse il primo compilatore nativo in C++. L'altro importante contributo venne da Henkel-Wallace e Gilmore che svilupparono rispettivamente le binutils (utility binarie) e il debugger GDB. Questo lavoro ebbe talmente grandi proporzioni che Gilmore divenne il nuovo mantainer di GDB; quando la comunità internet lo venne a sapere, vi fu una inondazione di differenti versioni del debugger; questo portò a integrare in GDB le nuove caratteristiche proposte dalla comunità. Tutto questo duro lavoro è poi stato ripagato in quello che oggi chiamiamo GNUPro Developers Kit, il quale include: • GCC – Gnu Compiler Collection, un compilatore con supporto a numerosi linguaggi di programmazione, fra cui il C in particolare; • G++ - Compilatore C++ che fa parte di GCC; • GDB - GNU DeBugger; • GAS – GNU Assembler; • LD – GNU Linker; • Cygwin – Un set di strumenti per migrare applicazioni da UNIX/Linux alla piattaforma Microsoft Windows; • Insight – Interfaccia grafica per fare debug; 17 • Source-Navigator - Un strumento per la comprensione e l'analisi del codice 2.2 L'origine di eCos Alla fine degli anni '90 il mercato dei sistemi operativi realtime era piuttosto frammentato. Cygnus, grazie all'esperienza maturata e al rapporto con i produttori di semiconduttori, volle creare una soluzione software per dispositivi embedded che fosse di alta qualità e concorrenziale dal punto di vista dei costi. Questa soluzione si andava ad affiancare al set GNUPro, espandendo l'offerta di Cygnus. Quello che questo team andò a sviluppare fu un sistema operativo realtime che permetteva l'astrazione dell'hardware e che era altamente configurabile. Nacque cosi eCos (embedded Configurable operative system), un RTOS che si adattava a numerosi sistemi embedded e che grazie alla sua flessibilità permetteva una forte riduzione dei tempi per lo sviluppo di questi sistemi: la riduzione dei tempi e quindi dei costi è sempre un aspetto fondamentale nei sistemi embedded. Utilizzando il modello di sviluppo open source, eCos è a costo zero dato che può essere scaricato e provato gratuitamente. Inoltre un altro aspetto estremamente importante è l'essere royalty-free, cioè non vi sono neppure costi a posteriori. Gli sviluppatori hanno accesso completo a tutto il codice sorgente, strumenti inclusi, che possono essere modificati a piacere. Non ci sono costi iniziali associati a licenze di utilizzo del codice e neppure ai tools relativi; tutto ciò che serve per inizializzare lo sviluppo di un sistema embedded può essere fatto senza alcuna spesa. Gli sviluppatori non devono neanche dare nessun contributo relativo al codice sviluppato per componenti aggiuntivi o applicazioni sviluppate, ma devono fornire le eventuali modifiche fatte al codice di eCos. Questo permette alla comunità open source di sviluppare un prodotto sempre migliore. 2.2.1 Configurabilità e configurazione Per capire meglio l'architettura di eCos, è importante analizzare le peculiarità del framework che compongono il sistema eCos. Il framework dei componenti è specificamente orientato ai sistemi embedded e si adatta perfettamente al modello dei sistemi embedded. 18 Usando questo framework, un enorme quantità di funzioni per una applicazione può essere costruita da componenti software già utilizzati o da blocchi di esso. Il framework dei componenti di eCos è stato progettato per minimizzare l'uso di memoria dei componenti del sistema, permettere agli utenti di tenere sotto controllo le tempistiche per soddisfare le caratteristiche di realtime, ed utilizzare i più diffusi linguaggi di programmazione (C, C++ e assembly). La maggior parte dei sistemi embedded oggi sul mercato supporta più funzionalità di quelle che servono per una specifica applicazione. Spesso viene incluso software aggiuntivo che dà un supporto generico a caratteristiche a cui gli sviluppatori di sistemi embedded non sono interessati o di cui non hanno bisogno. Questo software aggiuntivo concorre a rendere il sistema più complesso ed inoltre più il codice è lungo e più ci sono probabilità che qualcosa non vada. Un esempio estremo è quello del programma “Hello world”: usando i tradizionali RTOS, che hanno supporto a mutex, task switching ed altre caratteristiche integrate, tutte queste funzionalità sarebbero presenti nonostante non ce ne sia il bisogno. In questo senso eCos dà la maggiore possibilità di controllo dei componenti di run-time, dando la possibilità agli sviluppatori di rimuovere ciò che non è necessario. Il sistema eCos è quindi un sistema altamente scalabile: si possono creare sistemi con dimensioni dell'ordine delle centinaia di bytes fino a migliaia di kilobytes quando caratteristiche come lo stack di rete e componenti di terze parti come un web server vengono utilizzate. Gli sviluppatori possono scegliere i componenti che soddisfano le richieste base delle loro applicazioni e configurare quel particolare componente per una specifica implementazione richiesta dall'applicazione. Questo significa potere abilitare o meno una specifica caratteristica di un componente, o selezionare una particolare implementazione di quel componente. Un esempio pratico è quello della configurazione dello scheduler del kernel: eCos dà la possibilità di selezionare il numero di livelli di priorità e se utilizzare i time slicing. Ogni linea di codice non necessaria per l'applicazione viene eliminata dall'immagine finale dell'applicazione. La configurabilità permette alle ditte di costruire una base di componenti riutilizzabili, con l'accesso al codice sorgente del componente. Questo può ridurre il tempo di sviluppo e il tempo di immissione sul mercato poiché i componenti sono altamente portabili e possono essere utilizzati in un ampio range di applicazioni. Il framework di eCos incoraggia lo sviluppo di codice di terze parti in modo tale da estendere caratteristiche e funzionalità dei componenti chiave di eCos. Se gli 19 sviluppatori lavorano per estendere funzioni sul prodotto e contribuiscono a renderle disponibili in eCos, la crescita delle funzionalità di eCos sono senza limiti. Inoltre, se la funzionalità non è al momento disponibile, il codice sorgente è disponibile e permette di risolvere il problema da sé. La filosofia del controllo dei componenti nell'implementazione di eCos è quella di ridurre le dimensioni del codice, sempre e il più possibile. In questo modo anche sistemi minimali non soffrono dell'inserimento di codice aggiuntivo necessario al supporto di caratteristiche avanzate che vengono usate solo in sistemi più complessi. Nello sviluppo di applicazioni, i metodi di controllo dei componenti software possono essere di 3 tipi: a tempo di esecuzione, a tempo di link e tempo di compilazione. Il metodo utilizzato da eCos è quest'ultimo, in combinazione con il linking selettivo fornito dal GNU Linker: in questo modo si hanno i migliori risultati in termini di dimensione del codice e ciò lo rende adatto allo sviluppo di sistemi embedded. L'uso del metodo di controllo a tempo di compilazione, anche detto metodo di configurazione a livello sorgente, avviene grazie al preprocessore C e ciò offre numerosissime opzioni specifiche che possono essere applicate al codice. Oltre alla generazione di codice più corto, la configurazione a livello sorgente offre molti altri importanti vantaggi nello sviluppo di software per sistemi embedded: • le applicazioni sono più veloci perché le variabili non devono essere controllate durante l esecuzione per determinare quale azione intraprendere; • il codice è più reattivo e le latenze sono ridotte, il che permette di avere sistemi più deterministici che è molto importante in dispositivi realtime; • il codice generato è più semplice, rendendo verifiche e test più facili da eseguire • il codice è generato su misura dell'applicazione e si crea quindi una specifica applicazione per RTOS; • i costi possono essere ridotti perché l'utilizzo di risorse è ottimizzato e il numero di cicli di CPU sono sfruttati in modo efficiente, permettendo l uso di hardware più economico. 2.2.2 I componenti chiave di eCos Le funzionalità standard che si aspettano di avere in un sistema operativo realtime per dispositivi embedded, sono tutte incluse assieme al kernel realtime come componenti 20 chiave e sono: • Hardware Abstract Layer (HAL) – fornisce uno strato software che da accesso generale all'hardware. • Kernel – include interruzioni, supporto alle eccezioni, thread e meccanismi di sincronizzazione, scelta del tipo di scheduler, timers, contatori e allarmi. • Librerie matematiche ISO C – compatibilità standard con le chiamate di queste funzioni. • Driver dispositivi – include la porta seriale, ethernet, ROM flash e altri. • Supporto al debugger GNU (GDB) – fornisce un software diretto per la comunicazione con l'host GDB abilitando il debug dell'applicazione. Entrambi, eCos e l'applicazione, vengono eseguite in modalità supervisor cioè non c'è distinzione tra user mode e kernel mode. Una infrastruttura minimale è poi inclusa in eCos. I test vengono configurati allo stesso modo delle applicazioni e ciò assicura che l'esatta configurazione selezionata, venga sottoposta ai test. Il tool di configurazione dà gli strumenti per distribuire i test. L'espansione dell'infrastruttura di test è pianificata per i prossimi rilasci di eCos. 2.2.3 Supporto a processori e piattaforme di valutazione Il supporto di architetture e processori di eCos è molto ampio. Questo lo rende una buona scelta per le aziende che sviluppano su diverse architetture hardware e differenti linee di produzione. Una volta che viene effettuato il porting dell'HAL di eCos per una nuova architettura, l' applicazione precedentemente sviluppata può essere tranquillamente montata sulla nuova piattaforma. Il supporto software ad eCos è disponibile per le piattaforme di valutazione commerciali che sono sul mercato oggi. Le principali architetture di CPU supportate includono: • ARM • Fujitsu Fr-V • Hitachi H8/300 • Intel x86 • Matshushita AM3x • MIPS 21 • NEC V8xx • PowerPc • Samsung CalmRISC16/32 • SPARC • SPARClite • SuperH Grazie al modello di sviluppo open source pero questa lista è in continuo aggiornamento ed è consultabile sul sito web di eCos all'indirizzo http://sources/redhat.com/ecos/hardware.html 2.2.4 Supporto tecnico ad eCos La valutazione della qualità del supporto tecnico è un aspetto molto importante da tenere in conto per determinare se un prodotto può essere usato per i propri scopi. Ci sono più modalità possibili per avere supporto con eCos. Il percorso che si fa per ottenere questo supporto dipende largamente dalla quantità di assistenza necessaria. Sei differenti mailing list sono disponibili per eCos: • Lista di discussione – dà supporto e assistenza tecnica su vari argomenti relativi ad eCos attraverso gli sviluppatori. • Lista delle patch – usata per proporre patch, al codice di eCos, al mantainer prima che vengano affidate al repository ufficiale del codice sorgente. Qui troviamo anche le discussioni relative alle patch proposte. • Lista di sviluppo – Vi sono le discussioni relative ai miglioramenti da sviluppare, come ad esempio nuovi ports e nuove caratteristiche. • Lista Annunci – una lista a basso traffico che tratta delle novità importanti di eCos come nuove versioni di eCos e/o importanti miglioramenti. • Lista delle pagine web CVS – contiene le notifiche dei cambiamenti delle pagine web che vengono mantenute nel sistema CVS (Concurrent Version System). Questa è una lista a sola lettura. • Lista CVS – è una lista a sola lettura che notifica i cambiamenti fatte al codice sorgente di eCos. Come buona norma, è bene cercare tra la mailing list e provare a risolvere il problema 22 da sé, prima di fare una richiesta di aiuto. Generalmente le richieste di aiuto vengono processate in due-tre giorni. Bisogna tenere in considerazione che non a tutte le domande viene data risposta. Fare domande specifiche e dettagliate, può aiutare il mantainer e gli sviluppatori a dare una mano. Le risposte che vengono date possono aiutare altre persone a risolvere problemi analoghi e questo è uno dei punti di forza del sistema di sviluppo open-source e delle open mailing-list. L'iscrizione alle mailing list appena citate è completamente gratuito e può essere fatto collegandosi al sito web http://source.redhat.com/ecos/intouch.html Il sistema invece che si occupa di indicizzare i bug di eCos sono contenuti in un database Bugzilla. Il database Bugzilla ha un motore ricerca avanzato che permette di fare ricerche attraverso parole chiave, in particolari piattaforme e versioni specifiche di eCos. Questo database si trova all'url http://bugs.ecos.sourceware.org/ Per questioni tecniche private relative allo sviluppo di eCos e alla collaborazione, i mantainers hanno inoltre un indirizzo su cui possono essere contattati: [email protected] 2.3 Visione d'insieme del sistema eCos L'obiettivo con cui è stato progettato eCos è quello di permettere la costruzione di un sistema per dispositivi embedded completo partendo da tanti componenti software che possono poi essere riutilizzati. Lo sviluppatore può quindi scegliere la configurazione ad hoc, rimuovendo componenti non necessari, in modo da ottenere un prodotto che si adatta perfettamente ai requisiti dell'applicazione. Prestazioni e utilizzo della memoria vengono cosí ottimizzati al massimo. La figura 2.1 mostra un esempio di come i vari blocchi base e alcuni componenti aggiuntivi, sono organizzati a strati per rendere possibile l'incorporamento di tutte le funzioni necessarie per l'applicazione. 2.4 Terminologia di eCos Il sistema di configurazione di eCos coinvolge una serie di termini specifici importanti che vengono usati sia in questa tesi che nella documentazione ufficiale, in inglese che verranno quindi spiegati nel seguito. 23 Figura 2.1: Organizzazione a strati di eCos 2.4.1 Il component framework L'insieme degli strumenti che permette agli utenti di configurare eCos e gestire i vari pacchetti presenti nel repository è chiamato component framework. Incluso ad esso vi sono lo strumento di configurazione in modalità linea di comando, quello in versione grafica, lo strumento per la gestione della memoria e quello per l'amministrazione dei pacchetti. Il framework salva le scelte dell'utente in una configurazione, la quale contiene i pacchetti che sono stati scelti con le relative opzioni desiderate che possono essere attivate, disattivate o avere un valore selezionato. Gli strumenti del framework lavorano sulla configurazione insieme alle opzioni di quest'ultimo per determinare i valori di default e i range validi che possono avere. La configurazione è salvata in un file con 24 estensione .ecc. La figura 2.2 mostra una porzione del pacchetto del kernel di eCos proveniente dallo strumento di configurazione. Si noti come i vari blocchi sono incapsulati uno nell'altro a formare un pacchetto completo e indipendente. Possiamo inoltre osservare la gerarchia della configurazione a partire dai pacchetti poi ai componenti, alle opzioni di configurazione e alle sotto-opzioni. I blocchi in costruzione vengono raggruppati in un pacchetto in base alla funzionalità che esso include. Nella figura 2.2 vediamo appunto che il pacchetto del kernel di eCos contiene il componente della gestione delle eccezioni del kernel e il componente che gestisce lo scheduler del kernel; altri componenti non sono visibili in questa figura. Possiamo però notare in quest'ultima, la nidificazione delle opzioni di configurazione, per esempio nello scheduler del timeslice e le sottoopzioni che esso include. Figura 2.2: Esempio di configurazione a blocchi che compongono un pacchetto. 2.4.2 Component Repository Il componente repository è una struttura a directory che contiene tutti i pacchetti provenienti dall'installazione di eCos. Per aggiungere o aggiornare nuovi pacchetti, o per rimuovere quelli vecchi, possiamo utilizzare lo strumento di amministrazione dei pacchetti di cui abbiamo parlato in precedenza. La directory principale, “ecos” che contiene appunto tutti i file della distribuzione di eCos. La sottodirectory che contiene il component repository si chiama “packages”. All'interno di quest'ultima troviamo un file “ecos.db” che contiene i dettagli relativi alla varie versioni dei pacchetti presenti nel component repository. Può capitare che questo file debba essere modificato a mano, per 25 esempio quando si fa il porting su di una nuova architettura, per fare in modo che il nuovo HAL venga riconosciuto dalla configurazione. In generale, gli sviluppatori di applicazioni, possono considerare il component repository come una risorsa a sola lettura che può essere sempre riutilizzata per applicazioni differenti. La figura 2.3 mostra la struttura della directory del component repository. Figura 2.3: Struttura della directory del component repository. Essendo eCos in continua evoluzione, quello che si vede in figura è una fotografia del component repository della versione 2 di eCos che mostra il modello generale di organizzazione. Quando però vi sono nuove aggiunte al progetto eCos, i mantainers decideranno se queste aggiunte vanno messe all'interno di sottodirectory esistenti o se si deve creare una nuova sottodirectory. Una descrizione della struttura della directory del component repository è riassunta nella tabella 2.1. 26 Directory Descrizione compat Contiene i pacchetti per la compatibilita con POSIX (IEE 1003.1) e μITRON 3.0 cygmon Pacchetto per il debug monitor Cygmon devs Contiene i driver specifici di hardware come la seriale, ethernet e PCMCIA error Contiene i codici di stato e di errore comuni fs Contiene il pacchetto del filesystem di RAM e ROM hal Contiene i pacchetti relativi ad HAL infra Contiene i pacchetti dell'infrastruttura di eCos come le macro, opzioni di inizializzazione etc.. io Pacchetti per la gestione dell'I/O Hardware isoinfra Pacchetti per il supporto alle librerie ISO C (come stdlib e stdio) e implementazioni POSIX kernel Pacchetto per le funzionalità chiave del kernel di eCos (scheduler, semafori etc..) language Supporto per le librerie matematiche ISO C net Supporto alle funzionalità di networking (TCP/IP, UDP etc..) redboot Pacchetto per il ROM monitor RedBoot services Pacchetto per l'allocazione dinamica della memoria e supporto alle librerie di compressione /decompressione Tabella 2.1: Descrizione dei componenti del component repository. 2.4.3 Opzioni di Configurazione Le opzioni di configurazione sono la parte fondamentale della configurabilità di eCos: permettono infatti di abilitare, disabilitare o impostare un valore ad una specifica opzione. Ognuna di queste, ha associata una macro che lavora a livello sorgente del file di configurazione e include valori prestabiliti che possono essere usati come riferimento. Una volta completata l'applicazione poi, attraverso le opzioni è possibile ottimizzarla per soddisfare i requisiti di sistema. Il component framework descrive ogni pacchetto usando il cosiddetto Component Definition Language (CDL) e in ogni pacchetto c'è almeno uno script CDL che lo mette in relazione con il component framework. L'annidamento delle opzioni di configurazione viene usato per dare un controllo più fine 27 del sistema. Se una opzione che ha delle sotto-opzioni viene disabilitata, certamente queste sotto-opzioni sono inutili e quindi non possono essere selezionate e/o modificate. può pero accadere che alcune opzioni abbiano delle dipendenze su altre, rendendole quindi dei vincoli; se per esempio si seleziona l'opzione “Bitmap Scheduler” allora l'opzione “Scheduler Timeslicing” deve necessariamente essere disabilitata. In altri casi le opzioni non possono essere modificate: è il caso ad esempio di alcuni processori che supportano solo un metodo di memorizzazione dei dati (endian mode). A seconda dell'hardware selezionato nella configurazione, l'endian mode può essere o meno definito. Altre opzioni di configurazione invece, possono accettare solo determinati valori. Il numero di livelli di priorità deve infatti essere compreso tra 1 e 32. Una volta completata la configurazione, possono verificarsi dei conflitti. Questi vengono segnalati dallo strumento di configurazione in modo che possano essere corretti. Se questi conflitti vengono ignorati è si possono verificare degli errori durante il processo di compilazione o di linking. In questo caso si devono assolutamente risolvere i conflitti per completare l'applicazione: lo strumento di configurazione può proporre una soluzione oppure viene chiesto l'intervento dell'utente per la risoluzione del conflitto. 2.4.4 Pacchetti e componenti Un componente è una opzione di configurazione che incapsula altre opzioni più dettagliate al suo interno. I componenti possono essere abilitati o disabilitati a seconda delle necessità di quella particolare applicazione. La gerarchia delle opzioni dei componenti da un controllo ottimale della configurazione: rimuovendo componenti non necessari, il tempo di compilazione e la dimensione dell'immagine di eCos saranno minori. Un pacchetto è un tipo di componente che è pronto per la distribuzione. All'interno vi sono tutti i files sorgenti necessari, i file header, i file che descrivono la configurazione, la documentazione e altri file importanti. Spesso un pacchetto è racchiuso in unico file, permettendo attraverso il tool appropriato l'installazione o l'aggiornamento nel futuro se vengono fatte variazioni. Avere un pacchetto di distribuzione come unita indipendente, permette agli sviluppatori di terze parti di estendere le funzionalità di eCos. Utilizzando 28 inoltre il tool specifico per caricare un pacchetto, permette di avere il controllo delle versioni dei pacchetti usati nel sistema. 2.4.5 Target Un target è un insieme di componenti hardware nella quale l'applicazione realizzata verrà eseguita; può essere una scheda commerciale, hardware auto-costruito oppure un simulatore. Quando si effettua la configurazione è necessario selezionare il target in modo che il component framework possa caricare i relativi pacchetti che supportano il dispositivo e l'HAL riferito al target. Inoltre vengono settate le opzioni di configurazione ai valori di default in relazione al target selezionato. Questo lavoro è ovviamente più automatizzato per le piattaforme di valutazione supportate da eCos, mentre è richiesto un lavoro maggiore nel caso di proprio hardware in quanto è necessario determinare quali pacchetti e quali valori devono essere caricati nella configurazione. 2.4.6 Template Un template è una configurazione parziale molto utile come punto di inizio: è una combinazione di target e un gruppo di pacchetti. Questo gruppo di pacchetti ha un nome che ne descrive le funzionalità (tabella 2.2). Quando si inizia una nuova configurazione, il template viene usato come punto di inizio in relazione ai requisiti generali dell'applicazione. Le opzioni di configurazione vengono poi successivamente modificate ad hoc per la propria applicazione. Il tool di configurazione mostra poi i pacchetti specifici inclusi nel template. 29 Nome del Template Descrizione All Contiene i pacchetti per un hardware specifico Cygmon Pacchetto per costruire eCos con Cygmon Cygmon kernel Pacchetto per costruire eCos con Cygmon senza il supporto del kernel Default Contiene le infrastrutture, il kernel, librerie matematiche e C piu il supporto a pacchetti necessari Elix Contiene il pacchetto per la compatibilita con EL/IX Kernel Contiene HAL, le infrastrutture e il kernel eCos Minimal Contiene HAL e le infrastrutture net Pacchetti per il supporto allo stack di rete di OpenBSD New_Net Pacchetti per il supporto allo stack di rete di FreeBSD Posix Contiene HAL, le infrastrutture, il kernel eCos e il supporto POSIX RedBoot Supporto per la costruzione dell'immagine del RedBoot monitor Stubs Pacchetto per il supporto a GDB Uitron Pacchetto per la compatibilita con μITRON Tabella 2.2: Template di eCos 30 3 Caratteristiche di eCos 3.1 Portabilità Uno dei componenti chiave del sistema eCos è l'Hardware Abstraction Layer (HAL). Questo componente è quello che permette infatti di far girare in modo diretto la stessa applicazione sulle diverse piattaforme che eCos supporta. È inoltre possibile estendere la compatibilità delle piattaforme realizzando un porting dell'HAL per i nuovi hardware. Per porting si intende l'adattamento o la modifica di un componente (software) in modo da rendere possibile l'esecuzione dell'applicazione in un ambiente diverso da quello di origine: se effettuare il porting è una operazione semplice e quindi economica, il software si dice portabile. È facile intuire che la grande portabilità di eCos è uno dei suoi punti forti, rendendolo uno dei prodotti più flessibili del mercato. 3.2 Caratteristiche di base di HAL L' HAL ha il compito di isolare funzioni specifiche di una architettura e di convertirle in una forma generica per permettere la portabilità dei componenti dell'infrastruttura. Fondamentalmente l' HAL è uno strato software con API (Application Programming Interface) generalizzate, che contengono le specifiche operazioni hardware necessarie per completare la funzione desiderata. Un esempio di come l'HAL astrae specifiche implementazioni dell'hardware per la stessa chiamata dell'API si vede nel listato 3.1 per l'architettura ARM e nel listato 3.2 per l'architettura PowerPC. 31 1 #define HAL_ENABLE_INTERRUPTS() 2 asm volatile ( 3 "mrs r3,cpsr;" 4 "bic r3,r3,#0xC0;" 5 "msr cpsr,r3" 6: 7: 8 : "r3" 9 ); \ \ \ \ \ \ \ \ \ Listato 3.1: Implementazione della macro HAL_ENABLE_INTERRUPTS() su architettura ARM 1 #define HAL_ENABLE_INTERRUPTS() 2 CYG_MACRO_START 3 cyg_uint32 tmp1, tmp2; 4 asm volatile ( 5 "mfmsr %0;" 6 "ori %1,%1,0x8000;" 7 "rlwimi %0,%1,0,16,16;" 8 "mtmsr %0;" 9 : "=r" (tmp1), "=r" (tmp2)); 10 CYG_MACRO_END \ \ \ \ \ \ \ \ \ \ Listato 3.2: Implementazione della macro HAL_ENABLE_INTERRUPTS() su architettura PowerPC La chiamata che vediamo nei 2 listati è la HAL_ENABLE_INTRERRUPTS(), ed è la stessa per entrambi indipendentemente dall'architettura. Come si nota però, il processo che l'abilitazione di un interruzione varia da una architettura all'altra (come mostrano le righe da X a Y nel listato X e da X a Y nel listato X). L HAL permette allo strato di applicazione di accedere direttamente all'hardware e ad ogni funzione di architettura. La struttura di HAL segue alcuni vincoli. In primo luogo è interamente implementata in linguaggio C e assembly. Secondariamente gli interfacciamenti ad HAL sono implementate con delle macro in linguaggio C. Questo permette la massima efficienza di implementazione che non intacca l'interfaccia. Le interfacce possono essere scritte in assembly o C come funzioni inline, oppure come chiamate a funzioni esterne. Usando il metodo inline, i costi operativi di esecuzione associati alla funzione sono assenti ma la dimensione del codice può aumentare. L'HAL è formato da tre moduli separati: • architettura 32 • varianti • piattaforma Il primo modulo si occupa di gestire le famiglie di processori supportati da eCos. Ogni sotto-modulo dell'architettura contiene il codice per l'inizializzazione della CPU, gestione degli interrupt, context switching e altre funzionalità specifiche al set di istruzioni dell'architettura associata alla famiglia dei processori. Una variante è invece associata ad uno specifico processore all'interno di una famiglia di processori descritta dall'architettura. Per esempio in una stessa famiglia di processori si può avere il supporto della MMU (Memory Managment Unit) di alcuni processori, mentre di altri no. Una piattaforma invece è uno specifico hardware che include l'architettura del processore scelto o una sua variante. Generalmente si include il codice per l'inizializzazione della piattaforma, la configurazione dei chip select e dei timer. Nel mantenere la configurabilità di eCos un obiettivo prioritario, solo i componenti che sono realmente necessari per una specifica piattaforma o applicazione, vengono integrati nel kernel. La selezione dei componenti viene semplificata attraverso l'uso dello strumento di configurazione di eCos. L'interfaccia grafica mostra i dettagli di ogni componente e permette all'utente una selezione mirata. 3.2.1 Funzionalità di HAL Oltre a rendere eCos un prodotto portabile, HAL fornisce anche alcune funzionalità realtime specifiche per alcune piattaforme includendo un gestore delle eccezioni (exception handler), un gestore delle interruzioni (interrupts handler) e virtual vector. Il gestore delle interruzioni permette a un sistema embedded di ripristinarsi in seguito ad una eccezione hardware come gli overflow, un accesso errato alla memoria o una divisione per zero. Questi eventi non possono permettere il crollo del sistema, quindi devono essere opportunamente gestiti dall'HAL, dal kernel o dall'applicazione stessa. Le routine di risposta agli interrupts sono spesso usati nei sistemi di tipo realtime, ma per essere davvero efficienti, devono essere scritti in modo da sfruttare le caratteristiche del processore in uso. Sono supportati due tipi di routine: ISR (Interrupt Service Routine), utilizzati per semplici task che possono essere velocemente portati a termine e DSR (Deferred Service Routine) che sono usati per task complessi che possono essere attuati il prima possibile, ma con la riabilitazione di interrupt mantenuta a livelli di bassa 33 latenza per le ISR. Avere entrambi i tipi di routine disponibili, permette allo sviluppatore di gestire una priorità di questi, garantendo l'esigenza di bassi tempi di risposta. Virtual vectors è il nome della tabella che ha indirizzamento statico nello spazio di memoria del dispositivo: essa contiene 64 vettori che puntano a funzioni di sistema o a dati. I vettori hanno sempre lo stesso indirizzamento e ciò permette alla RAM e alla ROM di accedere a queste funzioni di sistema. Per esempio, si ha quindi la possibilità di fare debug sull'applicazione usando una comunicazione di tipo seriale o ethernet. 3.2.2 Configurazione di HAL Come è stato detto nel capitolo 2, eCos utilizza un metodo di configurazione a livello sorgente, il quale determina quali componenti includere nell'immagine finale della nostra applicazione. La configurazione a livello sorgente imposta i valori per specifiche macro in funzione delle opzioni scelte, poi l'HAL viene generato tenendo conto delle specifiche impostate nella configurazione. Le opzioni di configurazione possono essere divise in due parti: quelle specifiche di una architettura e quelle comuni a tutte le architetture. I componenti di configurazione comune contengono le opzioni generali per la gran parte o tutti i pacchetti dell'HAL del sistema eCos. I componenti specifici dell'architettura invece possono essere ulteriormente suddivisi in opzioni generali della piattaforma o specifiche di quest'ultima che si riferiscono ad un particolare hardware. 3.2.2.1 Componenti di configurazione comuni I componenti di configurazione comuni sono standard per tutti i pacchetti HAL del sistema e sono sei. Ogni componente contiene le opzioni di configurazione per impostare l'HAL in modo da soddisfare le specifiche dell'applicazione. La tabella 3.1 dà una descrizione dei sei componenti fornendo anche il nome in formato CDL. 34 Nome Componente Nome CDL Descrizione Opzioni di HAL indipendenti dalla piattaforma CYGPKG_HAL_COMMON Controlla gli interfacciamenti generali al kernel e altre opzioni quali il supporto alle eccezioni di HAL, installazione delle tabelle MMU e supporto alla diagnostica Nome Componente Nome CDL Descrizione Gestione delle interruzioni di HAL CYGPKG_HAL_COMMON_INTERRUPTS controlla la configurazione della struttura degli interrupt e la configurazione della dimensione dello stack degli interrupt Nome Componente Nome CDL Descrizione Supporto al Context Switch CYGPKG_HAL_COMMON_CONTEXT Permette al codice del context switch di sfruttare le convenzioni di specifiche architetture per ridurre la quantità di informazioni di stato da salvare durante il context switch Nome Componente Nome CDL Descrizione Comportamento di inizializzazione della cache CYGPKG_HAL_CACHE_CONTROL Permette l'abilitazione della cache per dati e istruzioni durante il processo di startup. Queste opzioni devono essere disabilitate se opzioni specifiche per l'hardware devono essere utilizzate. Nome Componente Nome CDL Descrizione Supporto al debug a livello sorgente CYGPKG_HAL_DEBUG Definisce il livello di debug da includere nel supporto ad HAL. Nome Componente Nome CDL Descrizione Supporto al ROM monitor CYGPKG_HAL_ROM_MONITOR Definisce l'interazione tra l'applicazione e il ROM monitor. L'applicazione può essere generata per lavorare con un ROM monitor oppure comportarsi come un ROM monitor. Tabella 3.1: Componenti comuni di configurazione di HAL 3.2.2.2 Componenti di configurazione specifici dell'architettura Queste opzioni possono cambiare enormemente tra una piattaforma e l'altra poiché dipendono dal template hardware che è stato scelto. Per esempio usando lo strumento grafico di configurazione e selezionando “Motorola MBX860/821 board”, il template hardware abilita i pacchetti seguenti nella configurazione (i nomi nel formato CDL sono tra parentesi): • PowerPC Architecture (CYGPKG_HAL_POWERPC) • PowerPC MPC8xx Variant HAL (CYGPKG_HAL_POWERPC_MPC8xx) • Motorola MBX PowerPC Evaluation Board (CYGPKG_HAL_POWERPC_MBX) • Motorola MBX PowerQUICC Support (CYGPKG_HAL_QUICC) Altre sotto-opzioni per questo template includono la selezione della velocità di clock della scheda hardware e il dispositivo di boot della ROM da usare. In un altro caso invece, selezionando ARM PID Development Board come template, si 35 abilitano questi pacchetti: • ARM Architecture (CYGPKG_HAL_ARM) • ARM PID Evaluation Board (CYGPKG_HAL_ARM_PID) Le opzioni di configurazione in questo caso sono: la selezione dell'endian mode e l'uso di porte di controllo per la diagnostica. Una sotto-opzione che hanno tutti i componenti specifici dell'architettura è lo Startup Type (CYG_HAL_STARTUP): questo impone dei vincoli sul pacchetto del supporto al ROM monitor. Lo Startup Type può essere ROM o RAM e nelle piattaforme ROMRAM, il codice contenuto nella ROM viene copiato nella RAM durante il processo di inizializzazione. 3.2.3 Inizializzazione di HAL Per capire meglio le funzionalità offerte da HAL, analizziamo il processo di startup del software mentre inizializza l'hardware. I sotto-moduli di HAL hanno il compito di inizializzare i processi, come il coordinamento con un ROM monitor, l'invocazione di costrutti statici C++ e il salto all'inizio del codice dell'applicazione. La figura 3.1 mostra il diagramma di flusso della routine di inizializzazione di HAL per la scheda di valutazione basata su PowerPc Motorola MBX860. Si tenga in considerazione che la procedura di startup può differire in funzione dell'architettura e della piattaforma usata. La routine descritta nella figura 3.1 può essere implementata in linguaggio C o assembly. 36 Figura 3.1: Procedura di inizializzazione di HAL Descriviamo ora i vari passi numerati della figura 3.1: 1. Il punto di partenza del sistema avviene dopo il ciclo di inizializzazione chiamato Hardware Powerup. Questo viene anche usato per un soft-reset. 2. Dopo un soft-reset (o un hard-reset) il processore punta ai suoi vettori di reset (reset_vector nel diagramma). Il vettore di reset si trova nel file vectors.S nella sottodirectory dell'architettura. Questo file contiene il punto di inizio per tutti i pacchetti dell'HAL. Il vettore di reset effettua la configurazione minimale dei registri del processore per permettere all'applicazione di inizializzarsi. 3. Il vettore di reset punta a _start che si trova anch'esso in vectors.S ed è il punto principale di inizio per l'avvio di HAL. 4. Viene chiamata la routine hal_cpu_init contenuta in variant.inc o in arch.inc a seconda dell'architettura. Questa funzione configura i registri specifici del processore. 5. Si invoca hal_hardware_init che è una funzione specifica della piattaforma e quindi si trova nel file assembly di questa. In questa routine vi è anche il setup 37 della cache, dei registri dell'interrupt a valori di default, dei registri di clock e dei registri specifici della piattaforma hardware in uso. 6. La configurazione dell'area di stack dell'interrupt consente di riservare un'area di memoria per salvare le informazioni di stato della CPU quando avviene una interruzione. La quantità di spazio da riservare si può settare nella configurazione dei componenti comuni. 7. Il codice relativo a hal_mon_init che si trova in variant.inc o platform.inc dipende dalla configurazione: quando eseguita come ROM monitor o applicazione ROM, lo scopo principale di questa routine è assicurare che il gestore delle eccezioni supportino tutte le condizioni di eccezione della CPU. 8. Pulizia della sezione BBS: contiene tutte le variabili globali e locali non inizializzate con una classe statica di memorizzazione. 9. Lo stack è inizializzato e permette di fare chiamate di funzioni C all'interno del codice assembly di vectors.S. 10. Viene chiamata hal_platform_init che si trova nel file hal_aux.c della piattaforma in uso. Questa a sua volta chiama hal_if_init che si trova in hal_if.c nella sottodirectory di HAL chiamata “common”: questa funzione inizializza la tabella dei virtual vector in funzione delle opzioni selezionate. 11. Inizializzazione della MMU: gestisce la traduzione di indirizzi logici ad indirizzi fisici provvedendo inoltre a meccanismi di protezione e caching attraverso la routine hal_MMU_init. 12. Abilitazione di cache per dati e istruzioni attraverso la funzione hal_enable_caches. 13. Esecuzione della routine hal_IRQ_init: avviene il setup del CPM (Communication Processor Module) che accetta e prioritarizza interrupts interni ed esterni. In questo caso è una opzione specifica dei processori PowerPC. 14. Chiamate dei costruttori C++ da parte cyg_hal_invoke_constructors. Il linker gestisce la generazione della lista dei costruttori globali. 15. Se la configurazione include un ambiente di debug e non vi è supporto ad un ROM monitor, viene chiamata initialize_stub il quale installa il gestore delle trap e inizializza l'hardware per il debug. 16. L'ultimo processo di inizializzazione di HAL è poi quello di passare il controllo al kernel per la sua configurazione: questo viene fatto con la routine cyg_start. 38 3.3 Il kernel di eCos Il kernel di eCos è stato progettato per soddisfare quattro obiettivi principali: 1) Bassa latenza agli interrupt; il tempo necessario per rispondere a un interrupt e cominciare l'esecuzione di una ISR. Minore è questo tempo e migliori sono le performance di sistema. 2) Bassa latenza di commutazione dei task; il tempo da quando un thread diventa disponibile al momento in cui inizia l'esecuzione effettiva. Un tempo basso, rende migliore un RTOS. 3) Uso di poca memoria; risorse di memoria sia per programma e dati sono ridotti al minimo. Si consente la configurazione di tutti i componenti di memoria a seconda delle necessità. 4) Comportamento deterministico; in tutti gli aspetti di esecuzione, le prestazioni del kernel devono essere prevedibili, per soddisfare i vincoli temporali. ECos offre un interessante caratteristica per migliorare ulteriormente le prestazioni delle applicazioni: la possibilità di realizzare una applicazione, con o senza un vero e proprio kernel. In applicazioni semplici che non necessitano di programmazione o multitasking, funzioni di eCos per inizializzare e gestire l'hardware possono essere create senza il kernel, portando al miglioramento della velocità di esecuzione e riducendo l'ingombro di memoria. In molte applicazioni per DSP questo metodo di procedere è comune. Spostandosi nella direzione opposta, eCos può essere invece un OS a tutti gli effetti ,con un set completo di kernel e componenti fondamentali, tra cui: lo scheduler e la gestione delle sincronizzazioni, interrupt e gestione delle eccezioni, contatori, allarmi, clock, timer, compatibilità POSIX e μITRON (una serie di specifiche di kernel realtime per sistemi embedded su piccola scala), ROM monitor , RAM e ROM file system, il supporto PCI, il supporto al protocollo di rete TCP/IP e la possibilità di essere continuamente migliorato grazie alle contribuzioni di parte di terzi. Come già detto in precedenza, il tool di configurazione eCos consente una facile configurazione e build del kernel. 39 3.4 Meccanismi di gestione del tempo in eCos ECos utilizza un meccanismo di timer hardware per gestire le sue funzionalità temporali, basato su alcuni componenti: • Counter: si tratta di un'astrazione che mantiene il conteggio di una sorgente di tick. Il tick può essere generato da un componente hardware oppure da un thread e non è necessariamente periodico • Clock: è un contatore con associata una risoluzione il quale è guidato da una sorgente periodica di tick che rappresenta il periodo. Il kernel di eCos implementa di default un clock di sistema detto RTC (Real Time Clock) • Allarme: e connesso con ad un contatore e provvede a generare eventi in base al valore del contatore associato. L'evento può essere configurato in modalità periodica o one shot. Quando un allarme viene configurato viene fornito anche un “handler” associato che processa l'evento. • Timer: è un allarme collegato al clock. 3.5 Scheduler nativi in eCos Gli scheduler realtime nativi supportati da eCos sono: lo scheduler MLQ (Multi Level Queue) e quello bitmap. Entrambi supportano il sistema di preemption ed entrambi utilizzano un sistema di priorità a 32 livelli (da 0 a 31, dove 0 è il livello più alto). Lo scheduler MLQ supporta inoltre il multiprocessore simmetrico (SMP), cioè un'architettura dotata di più CPU che possono accedere equamente alla memoria di sistema. 3.5.1 Bitmap Scheduling Il bitmap scheduling consiste in una coda contenente i thread che sono caricati in memoria. Ogni thread nella coda ha una priorità associata. Come è stato detto vi sono 32 livelli di priorità disponibili (figura 3.1) e ogni livello di priorità può essere associato con solo un thread, limitando quindi il numero di thread presenti nella coda a 31 (un thread è usato dal thread detto di idle). 40 Figura 3.2: Bitmap Scheduler : un livello univoco di priorita per ogni thread Lo scheduler permette sempre l'esecuzione del thread corrente con priorità più alta. Inoltre è possibili abilitare o meno il supporto alla preemption. Se questa è abilitate e un thread a priorità maggiore di quello in esecuzione entra nella coda, lo scheduler sospende quello corrente e lancia quello con priorità più alta (figura 3.2). Figura 3.3: Linea temporale dello scheduler Bitmap Grazie al design molto semplicistico di questo scheduler, trovare quale thread ha priorità più alta e metterlo in esecuzione rende il sistema veloce e reattivo. Si riesce inoltre a predire facilmente il comportamento del sistema, rendendolo di tipo deterministico. Questi vantaggi sono pero limitati ad un sistema con massimo 31 threads. 41 3.5.2 MLQ scheduler Nello scheduler MLQ invece di una coda per tutti i thread e tutte le priorità, si usa un set di code dove ognuna di esse contiene un numero di thread che hanno tutti la stessa priorità (figura 3.3). Ogni coda ha il suo algoritmo di scheduling che solitamente è un algoritmo a timeslice che condivide il tempo di CPU tra i vari thread in modo eguale. I livelli a diversa priorità possono avere scheduler di tipo diverso. Figura 3.4: Coda Multilivello (MLQ) Queste code sono gestite in modo che quelle con priorità più alta vengono messe in esecuzione per prime. (figura 3.4). I thread sono suddivisi in differenti livelli di priorità in relazione alle loro proprietà. Nel caso di supporto a processori SMP, ogni processore ha il suo scheduler. Figura 3.5: Grafico temporale dello scheduler MLQ Questo scheduler come si vede è più complicato di quello bitmap e questo comporta una latenza leggermente più alta. Lo scheduler MLQ permette pero la realizzazione di sistemi più complessi, permettendo allo sviluppatore di creare una separazione distinta tra task in background e foreground (i quali hanno richieste temporali differenti) e di 42 non avere il limite del numero dei thread come nel caso del bitmap scheduler. La complessità però rende più difficile la generazione di un sistema di tipo deterministico. 3.6 Sincronizzazione dei thread con eCos ECos supporta numerosi meccanismi di sincronizzazione per permettere la comunicazione tra threads e per gestire l'uso condiviso delle risorse. Questi sono: mutex, semafori, flags, spinlock, variabili di stato e mailbox. Mutex o oggetti a mutua esclusione permettono ai threads di condividere le risorse in modo seriale. Ogni thread a turno blocca la risorsa, la usa, e poi la sblocca per permetterne l uso al thread successivo. I semafori usano un contatore associato ad ogni risorsa per tenere traccia della sua disponibilità. Se la risorsa non è in uso, il primo thread che ne richiederà l utilizzo, avrà il permesso di usarla. Se invece molti thread stanno aspettando di poter usare la stessa risorsa, verrà dato per primo l accesso al thread con priorità più alta. I flags sono parole a 32 bit, dove ogni bit della parola può rappresentare un requisito della risorsa. I thread possono essere eseguiti quando un singolo requisito è soddisfatto o quando una specifica combinazione di requisiti sono rappresentati dal flag. ECos supporta SMP con spinlock. Ogni volta che il thread ha bisogno della risorsa, controlla il suo flag di stato: se la risorsa è in uso il thread continua ciclicamente a testare il flag finché non trova la risorsa libera. Le variabili di stato permettono a un thread che sta usando la risorsa, di segnalare ad altri thread quando la risorsa diventa disponibile. Queste variabili possono essere usate quando il thread in esecuzione ha processato i dati e vuole segnalare ad un secondo thread che i dati sono pronti per essere letti. Le mailbox servono infine per passare messaggi tra threads. 43 4 Esempio di applicazione usando eCos 4.1 Processo di build di eCos L'obiettivo del processo di build di eCos è la generazione di una libreria di eCos chiamata libtarget.a. Vengono poi generati anche altri file per il nostro dispositivo, come lo script linker e in alcuni casi anche altre librerie. A questo punto è necessario introdurre RedBoot: una applicazione open source che usa l'HAL di eCos per fornire l'inizializzazione, il caricamento e volendo il debug della nostra applicazione (bootstrap). RedBoot supporta il caricamento e l'esecuzione di applicazioni attraverso l'interfaccia seriale o ethernet. Il processo di build di RedBoot è leggermente differente da quello di eCos perché si genera un file binario che viene poi installato sul nostro dispositivo hardware: il RedBoot monitor viene utilizzato per fare debug sulla nostra applicazione utilizzando GDB sul nostro sistema di sviluppo. Il processo di build di eCos utilizza i makefiles e le GNU make utility per la generazione della libreria libtarget.a e segue tre diramazioni: “sorgenti”, “build” e “installazione”. La diramazione dei “sorgenti” è il repository del codice sorgente che si trova sotto la directory packages. La diramazione “build” è generata dagli strumenti di configurazione e contiene files intermedi come i makefiles e i file oggetto. Solitamente ogni pacchetto nella configurazione globale di eCos ha la sua sottodirectory che viene utilizzata per salvare i relativi makefiles e files oggetto, ma questa struttura può variare tra i vari processi di build. L'ultima diramazione, quella di “installazione”, si trova nella sottodirectory “lib” per la libreria principale di eCos e nella sottodirectory “include” per i file headers esportati. Solitamente le diramazioni “build” e “installazione” si trovano sotto la stessa directory di lavoro. 4.2 Un'occhiata più da vicino Vediamo ora da vicino come si genera la libreria di eCos per una piattaforma PC intel i386. 44 Il processo di build inizia con una configurazione: in base alle necessità, utilizziamo il tool di configurazione per includere i pacchetti e settare i valori delle opzioni in modo opportuno. Fatto ciò salviamo la configurazione nel file ecos.ecc. Questo genererà le sottodiramazioni “build” e “installazione” nella nostra directory di lavoro (figura 4.1). Figura 4.1: Struttura generata dalla configurazione di RedBoot Una volta salvata la configurazione, il tool provvederà anche a generare i file necessari per il processo di build: i makefiles e i files headers. Concentriamoci per un momento sul pacchetto HAL i386 e come i settaggi delle opzioni di configurazione vengono usate per generare i file relativi al processo di build. La figura 4.2 mostra una porzione della finestra del tool di configurazione, assieme ai file generati in funzione delle opzioni selezionate. 45 Figura 4.2: Diagramma di generazione del tool di configurazione Nell'angolo in alto a sinistra della figura 4.2, si vede il pacchetto dell'architettura i386 (CYGPKG_HAL_I386). L'opzione che abilita il supporto alla FPU (Float Point Unit) è abilitata (CYGPKG_HAL_I386_FPU), mentre le altre due opzioni sono disabilitate. La seguente descrizione corrisponde ai numeri nei cerchietti della figura 4.2 e i puntini di sospensione (...) nel codice sono stati usati per eliminare alcune parti in modo da evidenziare specifiche parti dei file sorgenti: 1. Il pacchetto dell'architettura i386 è rappresentato con il suo script CDL hal_i386.cdl che fa parte dei sorgenti: una volta caricato lo script, il tool di configurazione genererà i files necessari per il processo di build. Il comando cdl_package per CYGPKG_HAL_I386 che si vede in linea 1, contiene i file necessari alla compilazione della linea 3: questa verrà poi usata dal tool di configurazione a tempo di build per determinare quali files devono essere compilati per includere le caratteristiche del pacchetto di architettura i386. Dalla linea 5 alla 9 ci sono le proprietà del “make”, usate dal tool per generare il giusto makefile usato per compilare il pacchetto. 2. Qui si vede una porzione del makefile generato dopo aver salvato la nostra configurazione. Questo makefile si trova sotto ecos_build/hal/i386/arch/current. 46 Nella figura 4.2 si osserva la traduzione dallo script CDL relativo alle proprietà del make, alla generazione del makefile (dalla linea 2 alla linea 4). 3. In funzione dell'abilitazione al supporto FPU dell'architettura i386, quando salviamo il file ecos.ecc, lo script CDL viene generato come si vede dalla linea 2 alla linea 6. 4. Infine il tool di configurazione genera il file header hal_i386.h che include le opzioni di configurazione CYGPKG_HAL_I386_FPU. Questo file si trova nella directory ecos_install/include/pkgconf. Siccome l'opzione è abilitata, nella linea 2 vediamo che CYGPKG_HAL_I386_FPU è posta a 1. Una volta generati i due files, il processo di build può continuare: il tool di configurazione infatti determina quali files devono essere compilati dallo script CDL, come mostrato in figura 4.2 nel file hal_i386.cdl. Flag globali di compilazione (CYGBLD_GLOBAL_FLAGS) vengono utilizzati durante lo stadio di compilazione del processo di build, ma alcuni pacchetti hanno le proprie opzioni di compilazione: queste opzioni si possono settare come tutte le altre opzioni di configurazione. Durante il processo di build, i file oggetto vengono messi nella directory di “build”, poi il tool di configurazione, attraverso un processo di link, collegherà i file assieme. Flag globali di linker (CYGBLD_GLOBAL_LDFLAGS) possono essere settati allo stesso modo dei flag di compilazione. Entrambe queste tipologie di flag possono essere visualizzate attraverso una sottoschermata del tool di configurazione (come si vede in figura 4.3) selezionando Build -> Option e poi scorrendo il menu a tendina. Il pannello a sinistra mostra i pacchetti usati nella configurazione, mentre quello a destra mostra i flag specifici del pacchetto. Selezionando l'intera configurazione come nell'immagine, vengono mostrati tutti i flag. Come passo finale nel processo di build, il tool di configurazione invoca le utility di archivio per creare la libreria che verrà poi messa nella directory ecos_install/lib. Nel caso di build di RedBoot, il prodotto finale sarà un file binario che verrà posizionato nella directory ecos_install/bin. 47 Figura 4.3: Opzioni nel processo di build del tool di configurazione 4.3 Setup dell'hardware di sviluppo Per l'esempio che andremo ad analizzare verranno utilizzati (figura 4.4): • Sistema di sviluppo host: contiene l'intero ambiente di sviluppo eCos e il debugger GDB. • Sistema target PC i386: computer basato su tecnologia Pentium usato per lanciare RedBoot e la nostra applicazione. È munito di floppy drive. Entrambi i PC sono muniti di scheda ethernet e porta seriale. Possono comunicare tra di loro con entrambi i metodi: per poter comunicare con la porta seriale, è necessario un cavo seriale di tipo null-modem, mentre per la comunicazione via ethernet è necessario un hub di rete. Per il sistema target è necessario che il controller ethernet sia l'INTEL 82559 poiché è quello supportato dai driver di eCos. In questo esempio inoltre, per semplicità, gli indirizzi IP sono configurati in modo statico: il sistema host ha indirizzo 192.168.0.2 mentre il sistema target avrà indirizzo 192.168.0.10. Entrambi hanno subnet mask 255.255.255.0. É bene ricordare che per sistemi destinati al rilascio commerciale, non è consigliato utilizzare gli indirizzi IP statici, quanto piuttosto utilizzare un sistema di indirizzamento IP dinamico come DHCP o BOOTP. 48 Figura 4.4: Ambiente di sviluppo dell'esempio 4.4 Tool di configurazione di eCos Nel nostro esempio andremo ad utilizzare il tool grafico di configurazione “configtool”, il quale si trova sotto ecos-2.0/tools/bin. La directory di lavoro in cui andremo a salvare le configurazioni e i file immagine invece è sotto ecos-2.0/test. I sorgenti dei programmi di esempio che compileremo si trovano infine sotto ecos-2.0/examples. 4.5 Redboot Come primo passo dobbiamo fare il build, caricare su floppy e fare il boot di Redboot sul nostro target. Nel nostro esempio andremo a generare una immagine che verrà poi messa su floppy e che verrà eseguita sul nostro PC target. Vediamo ora i passi che si devono compiere per il nostro scopo: 1. Dopo aver lanciato il tool di configurazione, selezioniamo Build -> Templates. Nella finestra di dialogo che appare impostiamo poi come hardware “i386 PC Target”, “redboot” come package e diamo conferma con “ok”. Nel caso ci fossero dei conflitti causati dalle impostazioni, il tool di configurazione ci viene in aiuto proponendo una finestra con le soluzioni possibili. Clicchiamo ora su “Continue” e lasciamo che il tool carichi i pacchetti e le relative configurazioni per il nostro target PC i386. 49 2. Ora vogliamo impostare i valori delle opzioni di configurazione di RedBoot. Ciò è possibile selezionando manualmente le opzioni, oppure importando un file preconfigurato (.ecm) che imposta automaticamente i valori. Per importare una configurazione minimale clicchiamo su File->Import e selezioniamo il file redboot_FLOPPY.ecm che si trova nella sottodirectory ecos- 2.0/packages/hal/i386/pc/v2_0/misc. Premiamo il tasto “Open”, risolviamo i conflitti nel caso ce ne siano e clicchiamo su “Continue”. 3. Andiamo adesso a configurare le opzioni di rete della nostra build, impostando il valore 192,168,0,10 in Default IP Address (CYGDAT_REDBOOT_DEFAULT_IP_ADDR) che si trova sotto RedBoot ROM Monitor (CYGPKG_REDBOOT) nel pacchetto RedBoot Networking (CYGPKG_REDBOOT_NETWORKING). Mettiamo inoltre la spunta su Do Not Try To Use BOOTP (CYGSEM_REDBOOT_DEFAULT_NO_BOOTP). È importante notare che di default eCos utilizza come canale di comunicazione la prima porta COM da cui riceve input. Nel nostro esempio inoltre è abilitata l'opzione Output to PC Screen (CYGSEM_HAL_I386_ PC_DIAG_SCREEN) che ci permetterà di utilizzare come canali di comunicazione con il nostro target, il monitor e la tastiera. 4. Salviamo ora la nostra configurazione con File->Save As. Dobbiamo poi impostare un nome per il nostro file (.ecc); nel nostro caso usiamo “redboot” e lo salviamo sotto la directory ecos-2.0/test. Il tool di configurazione andrà ora a generare le directory di cui abbiamo parlato nei paragrafi precedenti, aggiungendo i suffissi _build e _install al nome del file della configurazione salvata (figura 4.1). La sottodirectory redboot_build viene usata per memorizzare i file necessari durante il processo di build, la sottodirectory redboot_install contiene l'immagine binaria e i file header del processo di build e redboot_mlt contiene i file di layout della memoria usati dal tool di configurazione. 5. A questo punto possiamo creare l'immagine di RedBoot. Se nell'angolo in basso a destra del tool vediamo la dicitura “No Conflict” possiamo procedere con il build vero e proprio; in caso contrario con la combinazione di tasti “ALT+F5” possiamo vedere i conflitti che vi sono. Per lanciare il processo di build clicchiamo su Build->Library: durante il processo vedremo una serie di messaggi che mostrano lo stato di compilazione. Quando la compilazione è 50 terminata la finestra mostrerà “build finished”. In caso di errori durante la compilazione questi saranno visualizzati nella finestra di stato e fintanto che non vengono corretti il processo non può continuare. Il file che viene creato a processo completo è redboot.ini che si trova nella sottodirectory redboot_install/ bin/. 4.5.1 Installazione di RedBoot A questo punto è necessario creare il floppy di boot con l'immagine appena creata. Con l'utility dd copiamo l'immagine dal nostro sistema al floppy con il comando dd conv=sync if=redboot.bin of=/dev/fd0, dopo esserci spostati nella directory redboot_install/bin/. 4.5.2 Boot di RedBoot Possiamo ora inserire il nostro floppy nel PC i386 target impostando come dispositivo di boot il floppy drive. Sul monitor del target si vedrà una cosa del tipo (figura 4.5): 1 2 3 4 5 6 7 8 Ethernet eth0: MAC address 00:d0:bd:43:9d:d2 IP: 192.168.0.10, Default server: 0.0.0.0, DNS server IP: 0.0.0.0 RedBoot(tm) bootstrap and debug environment [FLOPPY] Non-certified release, version UNKNOWN - built 12:22:06, Jan 20 2009 Platform: PC (I386) Copyright (C) 2000, 2001, 2002, Red Hat, Inc. RAM: 0x00000000-0x000a0000, 0x0008ac30-0x000a0000 available RedBoot> Figura 4.5: Messaggi di inizializzazione di RedBoot Vengono mostrati: la configurazione di rete ethernet, la versione dell'immagine, la data di creazione, il tipo di piattaforma, la memoria utilizzata, quella disponibile e il prompt di RedBoot che indica che è pronto a ricevere un input. Successivamente ci possiamo collegare al PC target con HyperTerminal usando le seguenti impostazioni: baud rate di 38400, 8 bit di dati, 1 bit di stop e controllo del 51 flusso disabilitato (flow control). Oppure è possibile collegarsi con il programma telnet usando come host di destinazione l'IP 192.168.0.10 e come porta la 9000, come impostato in TCP Port To Listen For Incoming Connections (CYGNUM_REDBOOT_NETWORKING_TCP_PORT). Una volta connessi al PC target, sul computer host verrà visualizzato il prompt di RedBoot che ci permetterà in un secondo tempo di lanciare le nostre applicazioni. 4.6 Processo di build di eCos Continuando il nostro esempio, dobbiamo ora configurare e compilare una immagine di eCos che sarà poi linkata con la nostra applicazione. L'immagine che andremo a creare utilizza la configurazione di inizializzazione RAM, il che permetterà di caricare la nostra applicazione in questa memoria. Potremo poi lanciare tale applicazione ed eventualmente fare debug di essa. Lo scopo principale di questo punto è quello di generare le libreria eCos secondo quanto impostato nella configurazione. In modo analogo a quanto fatto per la generazione dell'immagine di RedBoot, apriamo il tool di configurazione e scegliamo Build->Template. In template metteremo “i386 PC Target” e in package useremo “default”. Diamo poi “ok”, risolviamo gli eventuali conflitti e premiamo su “Continue” per procedere al passaggio successivo. Ora che abbiamo una configurazione di base, possiamo adattarla alle nostre esigenze: verifichiamo che Startup Type (CYG_HAL_STARTUP) sia impostato su “RAM”. Questa opzione si trova sotto il pacchetto HAL di eCos, sotto il pacchetto i386 Architecture (CYGPKG_HAL_I386), sotto il pacchetto della piattaforma i386 PC Target (CYGPKG_HAL_I386_PC). Inoltre visto che utilizziamo il RedBoot monitor, abilitiamo il supporto ad esso con l'opzione Work With a ROM Monitor (CYGSEM_HAL_USE_ROM_MONITOR). A questo punto possiamo salvare la nostra configurazione nella sottodirectory ecos2.0/test con File->SaveAs utilizzando il nome ecos.ecc. Il tool di configurazione, come per RedBoot andrà a creare le directory ecos_build, ecos_install e ecos_mlt. Siamo ora pronti per generare la libreria eCos: risolviamo gli eventuali conflitti, verificando la dicitura in basso a destra della finestra, poi selezioniamo Build->Library e attendiamo la compilazione, che si conclude con “build finished”. É stata ora generata 52 la libreria libtarget.a che si trova sotto ecos_install/lib. Come per il caso di RedBoot se si presentano degli errori di compilazione, è necessario risolverli per poter proseguire. 4.7 Applicazioni A questo punto possiamo effettuare il processo di build delle nostre applicazioni incorporando la libreria eCos appena creata. Figura 4.6: Diagramma di flusso della procedura di creazione dell'applicazione di eCos. Le applicazioni che andremo a generare sono quelle che si trovano sotto la directory ecos-2.0/examples. Senza entrare nel dettaglio, utilizzeremo il Makefile già presente nella directrory degli esempi. Il Makefile è quel file che descrive le modalità di compilazione della nostra applicazione, tra cui i flag globali di linker e di compilazione. Spostiamoci quindi nella directory degli esempi e diamo il comando make INSTALL_DIR=/ecos-2.0/test/ecos_install. In questo modo il Makefile potrà reperire le informazioni di compilazione specifiche dell'applicazione che stiamo andando a creare, poiché le legge dai file della directory precedentemente generata dal tool di configurazione. Con questo comando si andrà anche a generare i files eseguibili delle 53 applicazioni che potranno poi essere inviate a RedBoot e poi eseguite. Prima di fare ciò, però è necessario convertire i file eseguibili nel formato S-record, con il comando: i386elf-objcopy -O srec hello hello.srec (hello è uno degli applicativi di esempio che sono stati generati). Si può notare in figura il diagramma di flusso della procedura appena descritta (figura 4.6). Il file .srec potrà poi essere trasferito al PC target mediante uno dei metodi supportati dal RedBoot precedentemente installato, ovvero streaming su seriale RS232 oppure TFTP (Trivial File Transfer Protocol) su ethernet. Una volta caricato il file .srec con il metodo che si ritiene più comodo, siamo pronti per lanciare il nostro applicativo di esempio “hello” dando il comando “go” al prompt di RedBoot. Sul monitor del PC Target si dovrebbe visualizzare il testo “Hello eCos World!!!” che era ciò che ci si aspettava. Per una spiegazione più dettagliata del processo di build di applicazioni con eCos si può fare riferimento alla documentazione online all'url http://ecos.sourceware.org/docslatest/user-guide/ecos-user-guide.html. 54 Considerazioni finali e Conclusioni Una delle attività principali comprese nel processo di progettazione di un sistema realtime è quella della selezione del sistema operativo. Tale scelta viene eseguita valutando da un lato i sistemi operativi sviluppati da terze parti e dall'altro la possibilità di sviluppare un sistema operativo in proprio. Contrariamente a quanto avviene infatti per i sistemi "general purpose", per i quali siamo abituati ad avere un CD/DVD di installazione pronto all'uso, difficilmente il progettista potrà contare su un sistema operativo adattato al proprio specifico "target", magari per il semplice fatto che l'hardware è stato progettato ex-novo. I sistemi sviluppati da "terze parti", fra cui troviamo eCos, normalmente rendono disponibile all'utente finale: • o i sorgenti completi del sistema operativo, magari previa sottoscrizione di un opportuno accordo di non divulgazione per quelli "non open source"; • oppure forniscono il sistema operativo in due parti, una libreria binaria contenente le primitive del kernel ed una parte in forma sorgente modificabile dall'utente, che costituisce il "board/platform support package" (BSP/PSP). Una delle caratteristiche più evidenti che contraddistingue tutti i package open source, fra cui eCos, è senz'altro quella dell'assenza di costi diretti per l'acquisto di licenza d'uso. In realtà il progettista deve valutare il sistema operativo nel suo complesso, tenendo anche conto di: • caratteristiche tecniche (prestazioni, benchmark, algoritmi usati, ecc..) costo per l'adattamento al proprio target (spesso i S.O. commerciali comprendono nel costo di licenza la fornitura di uno o più BSP/PSP) disponibilità del codice sorgente per eventuali certificazioni (safety critical, ad esempio); • disponibilità di strumenti di analisi e debug (per eCos, RedBoot con stub GDB). Gli RTOS commerciali possono essere rilasciati in licenza per singolo progetto o per una famiglia di progetti e tali licenze possono valere per l'intero ciclo di vita dell'OS oppure deve essere rinnovato ogni anno: questo può comportare investimenti e quindi costi non indifferenti nello sviluppo. Riassumendo possiamo dire che, oltre ad avere un Real Time Kernel, gli aspetti che si 55 devono tenere conto per sviluppare applicazioni su un determinato RTOS sono: • qualità della documentazione • flessibilità di utilizzo su diverse piattaforme • qualità degli strumenti di sviluppo • qualità del supporto tecnico • costi di licenza OS realtime? si no si Uso del kernel opzionale? no no no Il kernel è configurabile e scalabile? si si si I componenti dell'OS possono no si no essere modificati? È presente uno scheduler? si si si Supporta i thread? si si si Supporta l'allocazione della si si si memoria? È fornito il codice sorgente? no si no Gli strumenti di sviluppo sono free? no si no Gli strumenti di sviluppo girano su si si si Solaris? Gli strumenti di sviluppo girano su si si si Windows? Gli strumenti di sviluppo girano su no si si Linux? L'esecuzione è royalty-free? no si no Permette l'accesso diretto si no no all'hardware? Tabella 5: Comparazione tra sistemi operativi per dispositivi embedded eCos Linux RT Linux Caratteristica VxWorks Facciamo ora un confronto sintetico tra alcune tra le più popolari soluzioni per dispositivi embedded (tabella 5): si si si si si si si si si si si si si si 56 Come si nota anche dalla tabella, eCos offre alcuni chiari vantaggi agli sviluppatori di applicazioni embedded. È un sistema open source con una alta flessibilità di configurazione che permette quindi di creare una applicazione su misura atta a soddisfare i requisiti dell'applicazione con le risorse hardware disponibili. Non sono presenti licenze di utilizzo e gli strumenti free lo rendono molto attraente a sviluppatori con budget ridotto. Eccellenti performance realtime, basse latenze e comportamento deterministico rendono eCos un'interessante soluzione per i propri applicativi. Alcuni possibili svantaggi nell'uso di eCos devono però essere valutati attentamente prima di decidere il suo utilizzo in una applicazione embedded. La toolchain (l'insieme degli applicativi usati per lo sviluppo) utilizzata in modo preferenziale nel build di eCos è quella derivata dal progetto GNU della Free Software Foundation. Gcc, Gdb, Insight e le Binutils sono senza ombra di dubbio gli strumenti preferiti dagli sviluppatori embedded, principalmente per la qualità e l'affidabilità del codice prodotto e per l'intrinseca portabilità verso svariate piattaforme hardware. Esistono di questi strumenti anche versioni rilasciate con licenze commerciali, magari abbinate a front end grafici integrati. Si tratta comunque di strumenti "difficili" da usare, almeno per chi non è abituato all'uso delle command line di Unix. La configurazione si ottiene attraverso innumerevoli opzioni di invocazione, che devono essere redatte dall'utente in opportuni Makefile. Gli strumenti di sviluppo commerciali (alcuni dei quali, fra l'altro, utilizzano proprio gcc come compilatore interno, ad esempio la suite Rowley per ARM) dispongono normalmente di un IDE nel quale la selezione delle opzioni di compilazione avviene in modo "user friendly". Come sviluppatori di eCos, bisogna inoltre prendersi carico della responsabilità della ricerca delle risorse per il proprio progetto all'interno della comunità degli sviluppatori e che eventualmente alcuni componenti che non esistono, vanno creati con le proprie capacità. Molti RTOS commerciali offrono supporto tecnico aggiuntivo, alcuni anche con garanzie sulle tempistiche di risposta per la risoluzione dei problemi. Lo sviluppatore di eCos può però decidere se lavorare con un consulente specializzato in eCos o utilizzare come supporto solo la comunità: come è stato detto si possono infatti utilizzare le mailing list presenti sul sito. Questo tipo di supporto potrebbe però essere un rischio non accettabile per applicativi a breve ciclo di sviluppo. 57 Ponderati i lati positivi e negativi dell'uso di eCos, pare evidente che questo sistema possa essere una valida soluzione per molti applicativi embedded: eCos sta infatti guadagnando nel tempo fette di mercato. A supporto di questo si può vedere all'indirizzo internet http://www.ecoscentric.com/ecos/examples.shtml un lungo elenco di dispositivi commerciali che ne fanno uso: dal controller di rete WIFI della playstation 3, ai televisori top di gamma Samsung, ad alcuni router Netgear, ai dispositivi di accesso biometrico nella concept car Volvo, ad alcune stampanti Brother, a dispositivi di posizionamento aerospaziale dell'ESA (European Space Agency) etc. Sicuramente ha centrato l'obiettivo degli sviluppatori che l'hanno creato, di produrre un sistema operativo leggero, altamente configurabile, realtime, con buone prestazioni ed esente da licenze: quindi concludendo vale sicuramente la pena di considerarlo nello sviluppo di applicazioni embedded. 58 Riferimenti Bibliografici [Arpinen] Tero Arpinen “Introduction to eCos Real-Time Operating System”, Tempere University Of Technology. [IEEE] IEEE Computer Society “Standardized Application Enviroment Profile (AEP) – POSIX Realtime and Embedded Application Support”. [Kalinsky] David Kalinsky “Basic concept of real-time operating system”, Linuxdevices website [Newsgroup] Comp.realtime “Frequently Asked Questions”, Newsgroup online [Massa] Anthony J. Massa “ Embedded software development with eCos”, Prentice Hall. [Melchiorri] Claudio Melchiorri “Introduzione a RTAI-LINUX” , DEIS – Università di Bologna. [OsDevWiki] url: http://wiki.osdev.org per l'argomento “Context Switcing”. [Salenby] Gusatv Salenby, Daniel Lundgren “Comparison if scheduling in FreeRTOS and eCos”. [Sgandurra] Robert Sgandurra “An Introduction to eCos”, Pentek, Inc. [Thomas] Michael Thomas “Real Time Operating System”, Renesas Technology America, Inc. [UdsCagliari] Università degli studi di Cagliari “Real-timing in ambito Linux: teoria e applicazioni reali. [Valenti] Alessandro Valenti “I sistemi embedded: considerazioni sulla progettazione dei sistemi dedicati con e senza sistema operativo”, Università degli studi di Modena e Reggio Emilia. [Wikipedia] url: http://wikipedia.org/ per gli argomenti: Preemption, Context switch, Thread, Context, Sistemi Operativi Realtime, Real- time computing, eCos, Embedded system, Cygnus Solutions, RedBoot. 59