Facoltà di Ingegneria Corso di Studi in Ingegneria Informatica Elaborato finale in Sistemi real-time Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Anno Accademico 2012/2013 Candidato: Alessio Nisini matr. N46000045 Indice Introduzione 5 Capitolo 1. Panoramica generale 6 1.1 1.2 1.3 Caratteristiche chiave Sistema host e target Componenti e profili 6 7 8 Capitolo 2. Processi real-time 2.1 2.2 2.2.1 2.2.2 10 Creazione dei processi real-time Modelli di memoria virtuale Modello flat Modello overlapped 10 12 12 13 Capitolo 3. Task real-time 3.1 3.2 3.3 3.4 14 Creazione dei task Area stack di un task Hook routine Task base 14 15 15 16 Capitolo 4. Multitasking 4.1 4.2 4.2.1 4.2.2 4.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 18 Stati dei task Scheduling Scheduler VxWorks tradizionale Scheduler VxWorks di thread POSIX Intertask ed interprocess communication Memoria condivisa Semafori Code di messaggi Pipe Eventi WxWorks Segnali 18 20 21 23 24 24 25 27 28 29 30 III Capitolo 5. I/O system 5.1 5.2 5.3 31 I/O base I/O bufferizzato I/O asincrono 32 33 33 Capitolo 6. File system 35 Conclusioni Bibliografia 38 39 IV Introduzione In un recente passato, alla domanda: “cos’è un sistema di elaborazione?” la naturale risposta sarebbe stata: “un personal computer”. Questa risposta è ormai superata, infatti, la maggior parte dei sistemi di elaborazione, oggi, è costituita dai sistemi embedded, presenti su un numero elevatissimo di apparecchi, che la maggioranza di noi utilizza senza averne piena coscienza. A sua volta, la maggior parte dei sistemi embedded è di natura real-time. Negli ultimi anni, la complessità di tali sistemi è cresciuta notevolmente, sia in conseguenza delle più disparate esigenze applicative, sia della complessità dei dispositivi hardware che si sono resi disponibili. Questa crescente complessità nega la possibilità agli sviluppatori di affrontare le problematiche applicative direttamente sull’hardware e rende necessario l’uso dei sistemi operativi real-time. Esistono numerosi RTOS, di natura aperta come RT-LINUX, RTAI, RTEMS o proprietari quali ITRON, QNX, OSE, VxWorks; ognuno di questi ha caratteristiche peculiari che lo rendono più o meno adatto a determinate esigenze. In questo elaborato concentreremo la nostra attenzione sul sistema operativo VxWorks partendo dalle caratteristiche che lo hanno reso noto e soffermandoci su interessanti aspetti quali la gestione dei processi e dei task, lo scheduling e l’interprocess comunication fino a giungere alla gestione dell’I/O e dei file system. 5 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Capitolo 1 Panoramica generale VxWorks è un sistema operativo real-time, conforme allo standard POSIX 1003.1b, sviluppato da Wind River System ad Alameda, California, USA, la cui prima release risale al 1987, quella attuale è la 6.9. Trova applicazione soprattutto nell’industria aereospaziale, nel settore della robotica e nel settore dell’automotive, come dimostra la sua presenza sull’ iDrive system della BMW, sul sistema di navigazione Siemens VDO, su alcuni veicoli spaziali quali Spirit and Opportunity, Phoenix, Clementine ed altri. Ai giorni nostri, VxWorks, è utilizzato su più di un miliardo di sistemi real-time e supporta praticamente qualsiasi CPU, non è quindi errato affermare che è il sistema operativo per lo sviluppo di applicazioni real-time più venduto al mondo. 1.1 Caratteristiche chiave Le caratteristiche fondamentali che hanno reso VxWorks celebre sono: • Il supporto al multi-core e al multi-OS: consente a VxWorks di supportare varie tipologie di configurazione quali la gestione dei processori simmetrica (SMP) o la gestione asimmetrica (AMP): la prima consente l’uso di più CPU, con la medesima architettura, che condividono la memoria e collaborano dividendosi il carico di lavoro; la seconda 6 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time consente l’uso di più CPU, non necessariamente uguali, ognuna con un proprio spazio indirizzi ed eventualmente gestita da un sistema operativo ad essa dedicato; quest’ultima configurazione è utilizzata quando risulta essere conveniente distribuire specifiche attività su componenti differenti. • La scalabilità: è offerta una vasta gamma di configurazioni possibili, che variano dalla leggerezza e semplicità necessarie ad un sistema snello, alla ricchezza di funzionalità di un sistema basato su architetture a 64 bit, multi-core, orientato alle più alte esigenze. • La sicurezza: numerose certificazioni garantiscono la conformità agli standard di sicurezza globale, includendo RTCA DO-178B, EUROCAE ED-12B and IEC 61508. Fornisce anche un multilevel secure (MLS), che consente alle applicazioni di processare informazioni con differenti livelli di sicurezza. 1.2 Sistema host e target VxWorks, adoperato principalmente nell’ambito dei sistemi integrati, utilizza un metodo di compilazione cross-compiling che permette allo sviluppatore di generare codice su un qualsiasi macchina host, ad esempio un PC Linux, Windows, Solaris, e compilare direttamente su uno specifico hardware target, sul quale non è conveniente programmare. La programmazione di applicazioni real-time sulla macchina host è supportata da Wind River fornendo una suite basata su Eclipse, chiamata Wind River Workbench, la quale minimizza la necessità di interfacciamento al sistema target. Il sistema target può essere anche simulato tramite Wind River VxWorks Simulator, consentendo allo sviluppatore di testare applicazioni VxWorks sul sistema host ed eliminando la necessità di un target hardware in fase di sviluppo e di test. Sul sistema target invece VxWorks utilizza i cosiddetti board support package (BSP), i quali forniscono un’interfaccia tra il sistema operativo e l’hardware sottostante e garantiscono la portabilità; è infatti possibile utilizzare VxWorks su più hardware differenti senza dover necessariamente modificare il kernel, a patto di una corretta configurazione dei BSP. Un BSP è tipicamente composto da un codice sorgente scritto in C o in assembly, uno o 7 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time più header file, un makefile e un target.ref o target.nr che contengono la documentazione specifica per il BSP. 1.3 Componenti e profili La scalabilità caratteristica di VxWorks, in termini di modularità ed estensibilità, è garantita attraverso l’implementazione di specifici componenti. Un componente VxWorks è l’unità base per fornire quelle funzionalità con le quali VxWorks può essere configurato; alcuni di questi dipendono da altri componenti che devono essere inclusi nelle configurazioni del sistema operativo, altri sono indipendenti. Il Workbench fornisce funzionalità per configurare VxWorks con i componenti selezionati e determinare le relazioni di dipendenza fra essi, identificati convenzionalmente mediante un nome che comincia con la parola INCLUDE (ad esempio INCLUDE_RTP per il supporto ai processi real-time). Congiuntamente ai componenti vengono spesso utilizzati i cosiddetti profili, utili per garantire un gruppo base di funzionalità alternative a quelle presenti nella configurazione di default, all’atto dell’installazione di VxWorks. Tra i vari profili esistenti citiamo i tre che consentono di diminuire la grandezza di default del sistema operativo; questi profili non supportano i servizi di networking: • PROFILE_MINIMAL_KERNEL: fornisce solamente i servizi indispensabili al funzionamento del sistema; è composto da un microkernel e da un supporto base alla CPU e al BSP, consente il mutitasking e la gestione delle interruzioni, ma non supporta l’allocazione dinamica della memoria. • PROFILE_BASIC_KERNEL: aggiunge al profilo minimale supporto alle code di messaggi, allocazione dinamica della memoria e un sistema I/O base. • PROFILE_BASIC_OS: completa il sistema I/O e aggiunge la gestione di variabili di ambiente, segnali, pipe e di un coprocessore; questo profilo è molto simile alla configurazione VxWorks 5.5, ma senza lo stack network e senza tool di debugging. 8 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time La seguente figura illustra le relazioni fra i tre profili e le funzionalità che forniscono. Figura 1: Profili in VxWorks Il più piccolo tra questi può generare un’immagine del sistema di 100KB o meno. I profili non sono monolitici e ognuno di essi può essere la base per ulteriori funzioni addizionali che possono essere aggiunte tramite componenti VxWorks o altri profili. 9 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Capitolo 2 Processi real-time I Processi real-time VxWorks (RTP) sono per molti aspetti simili ai processi di altri sistemi operativi, quali UNIX e Linux. Il modo in cui sono creati, messi in esecuzione e terminati sarà di facile comprensione per chi è conscio del modello di processo UNIX. Essi sono stati introdotti a partire dalla versione 6.0, in precedenza il funzionamento del sistema si basava esclusivamente su task. In tutto il ciclo di vita del processo è sempre garantito il determinismo, indispensabile per i sistemi real-time. 2.1 Creazione dei processi real-time La creazione dei processi real-time è divisa in due parti e separa la creazione di un'istanza del processo dal caricamento e dall'esecuzione della relativa applicazione. La prima fase consiste nella chiamata rtpSpawn() nel contesto del task padre, questa genera l'oggetto del processo nel sistema, alloca memoria virtuale e fisica, e crea il task iniziale del processo. La seconda fase è a carico del task iniziale del processo appena creato, il quale carica l'eseguibile per intero e avvia la main routine. 10 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Questo approccio garantice il determinismo in due modi: • La fase di creazione del processo (rtpSpawn() task) e la fase di caricamento dell'applicazione (initial task) sono indipendenti: un processo sarà creato in maniera discreta e deterministica senza tener conto dell’applicazione ad esso relativa. Tale meccanismo di indipendenza offre allo sviluppatore la possibilità di assegnare una priorità appropriata al task a seconda dall'applicazione. • L'eseguibile dell'applicazione è caricato per intero quando il processo è creato, ciò garantisce determinismo evitando imprevisti tempi di caricamento durante l'esecuzione. Il nome del task iniziale del processo è basato sul nome del file eseguibile con le seguenti modifiche: • La lettera "i" è prefissa • La prima lettera del nome del file è maiuscola • L'estensione del file è rimossa Ad esempio se l'applicazione "app.vxe" è in esecuzione, il nome del task iniziale del processo sarà "iApp". Un processo VxWorks può essere avviato nei seguenti modi: • interattivamente dalla shell • automaticamente nella fase di boot • in modo programmato da applicazioni o dal kernel VxWorks gestisce una gerarchia di processi caratterizzati da relazioni padre-figlio. Ogni processo creato dal kernel è figlio del kernel. 11 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Un processo VxWorks eredita alcuni attributi dal padre: descrittori dei file, che indicano la possibilità di accedere al medesimo file, e maschere di segnali. Se il processo è figlio del kernel eredita solo i tre descrittori dei file standard (stdin, stdout, stderr) e tutti i segnali saranno sbloccati poiché la maschera dei segnali ereditata sarà 0. Invece le variabili di ambiente non sono ereditate, ma il padre potrà passare al processo figlio il suo ambiente o una parte di esso attraverso diverse procedure. Differenza sostanziale nella creazione dei processi tra VxWorks e lo standard POSIX è che VxWorks non consente la creazione di processi tramite le primitive fork() ed exec(). 2.2 Modelli di memoria virtuale Ogni processo possiede il proprio spazio di indirizzamento che contiene il programma eseguibile, i dati del programma, l'area stack per ogni task, l'area heap e le risorse necessarie per la gestione del processo stesso. I processi VxWorks possono operare con due differenti modelli di memoria virtuale mutuamente esclusivi: modello flat (fisso), modello overlapped (sovrapposto). 2.2.1 Modello flat È il modello di memoria virtuale di default e non richiede configurazioni specifiche. Con questo modello ogni processo VxWorks possiede la propria regione di memoria virtuale, quindi i processi non sono sovrapposti nella memoria. L'utilizzo del modello flat comporta i seguenti vantaggi: • velocità nei cambi di contesto • semplicità nel debugging, poiché gli indirizzi per ogni processo sono univoci • flessibilità nel metodo di programmazione, consentendo alle applicazioni VxWorks di funzionare sia con che senza il supporto di una MMU. Quest'ultima caratteristica facilita il determinismo per i processi hard real-time rendendo possibile la disabilitazione della MMU. 12 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Per contro, l'utilizzo del modello di memoria virtuale fisso costringe alla rilocazione dei file eseguibili e ciò comporta un tempo di caricamento maggiore rispetto a quello che avremmo utilizzando il modello sovrapposto; inoltre il modello flat non permette allo sviluppatore di scegliere una specifica area di memoria virtuale dove il codice delle applicazioni risiederà, questa sarà scelta dall’algoritmo automatico best-fit. 2.2.2 Modello overlapped È opzionale ed un suo utilizzo implica necessariamente l'uso della MMU. Con questo modello tutte le applicazioni VxWorks condividono un range comune di indirizzi di memoria virtuale nel quale risiede l’insieme delle informazioni relative alle applicazioni stesse. Il modello overlapped porta i seguenti vantaggi: • Un più rapido tempo di caricamento (all'incirca tra il 10 e il 50 percento) quando viene usato un indirizzamento assoluto dovuto all'assenza della fase di rilocazione • Un maggior controllo della memoria virtuale, poiché è l'utente che decide l'area dove le informazioni relative alle applicazioni risiederanno • Una maggior efficienza della memoria virtuale ottenuta riducendo la possibilità di frammentazione della memoria. 13 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Capitolo 3 Task real-time I task di VxWorks sono l'unità base che rappresenta codice in esecuzione nel sistema stesso. Sono generati e messi in esecuzione dai processi a seconda delle necessità; non c’è un limite al numero di task in un processo, esso dipende esclusivamente dall’ammontare della memoria disponibile. 3.1 Creazione dei task Per creare un singolo task si utilizza la routine taskSpawn(), presente nella libreria taskLib, gli argomenti che devono essere passati sono: il nome del task, la sua priorità, le opzioni, la grandezza dello stack, l'indirizzo della main routine e 10 argomenti da passare alla main routine; la sintassi è quindi la seguente: id = taskSpawn ( name, priority, options, stacksize, main, arg1, ... , arg10 ); Alternativamente alla routine taskSpawn(), che crea e attiva un nuovo task, è consentito l’uso di: • taskCreate(): crea, ma non attiva un nuovo task • taskOpen(): apre un task oppure opzionalmente lo crea se non esiste • taskActivate(): attiva un task inizializzato 14 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Wind River raccomanda di usare le routine taskCreate() e taskActivate() solo quando le necessità di controllo sull'allocazione e sull'attivazione sono stringenti. I task in VxWorks possono essere creati o come oggetti privati, accessibili solo all’interno dello spazio di memoria in cui sono stati creati (kernel o processo), o pubblici, accessibili ovunque nel sistema. 3.2 Area stak di un task Alla creazione di un task deve essere specificata la grandezza dello stack riservato ad esso; determinare tale valore in maniera corretta può essere arduo o non immediatamente possibile. Per evitare situazioni di overflow e corruzione dei dati è opportuno riservare un’area stack molto più grande di quella prevista, in seguito monitorarla dalla shell tramite checkStack() o ti() e infine, dopo aver determinato l’effettivo utilizzo, correggerne la grandezza. In caso di utilizzo dell’MMU essa è arrotondata a un multiplo della grandezza delle pagina. Inoltre l’area stack di un task può essere protetta da una guard zone, inclusa tramite il componente INCLUDE_PROTECT_TASK_STACK; questa impedisce ad un task di superare la fine dello stack che gli è stato concesso, prevenendo corruzione di dati o di altri stack. 3.3 Hook routine Per estendere il set di funzionalità relative ai task nel sistema, VxWorks fornisce le cosiddette hook routine, invocate ogniqualvolta un task è creato, quando avviene un cambio di contesto o quando un task è cancellato. Ad esempio: • taskCreateHookAdd(): aggiunge una routine nel sistema che dovrà essere chiamata alla creazione di ogni task • taskCreateHookDelete(): aggiunge una routine nel sistema che dovrà essere chiamata 15 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time alla cancellazione di ogni task Queste e altre routine per la gestione dei task hook sono presenti nella libreria taskHookLib. 3.4 Task base A seconda della configurazione, nella fase di boot, VxWorks avvia una varietà di task di sistema che sono sempre in esecuzione (demoni). Vediamo i principali: • Root Task: il tRootTask è il primo task eseguito dal kernel, questo genera altri task come il logging task, l'exception task, il network task; normalmente il root task termina ed è cancellato quando completa tutte le inizializzazioni. • Logging Task: il tLogTask è usato dai moduli VxWorks per la gestione dei messaggi di log in modo da consentire ai task di non essere ritardati da operazioni di I/O sincrone. • Exception Task: il tExcTask supporta la libreria per la gestione delle eccezioni di VxWorks e si occupa di eseguire le azioni necessarie nei casi di eccezioni generate dall'esecuzione del codice dei task, in opposizione alle routine di interrupt che reagiscono ai segnali provenienti dall'ambiente, portando a termine funzioni che non sono servite dal gestore interruzione. È anche usato per azioni che non possono essere compiute nell'attuale contesto del task, come ad esempio l'auto-terminazione dei task. L’exception task deve avere la priorità massima nel sistema. • Network Task: il tNet0 è un task che consente ai processi di utilizzare le risorse della rete in VxWorks ed è principalmente usato dai driver network. Nei sistemi che sono configurati con più network daemon essi prendono il nome tNetn con n costituito da un identificativo numerico. 16 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time • Kernel Shell: se è stata inclusa la kernel shell nelle configurazioni di VxWorks, questa è generata come un task e prende il nome di tShellnum, dove num indica il numero della shell, poiché più shell possono essere attive contemporaneamente. Qualsiasi routine o task invocato dal kernel shell è eseguito nel contesto del tShellnum anziché essere creato. • Job Task: il tJobTask esegue i cosiddetti jobs, ossia chiamate di funzioni, per conto dei task. Questo esegue a priorità 0 quando è in attesa di una richiesta, e automaticamente cambia la propria priorità facendola corrispondere a quella del task che ha generato la richiesta quando dovrà servirla. 17 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Capitolo 4 Multitasking Lo sviluppo multitasking consente ad un’applicazione real-time di essere costituita da una serie di task indipendenti, ognuno caratterizzato da un proprio flusso di esecuzione e da un set di risorse di sistema. Il Multitasking fornisce il meccanismo fondamentale di un'applicazione per garantire controllo e capacità di reazione a eventi discreti del mondo reale. 4.1 Stati dei task Il kernel mantiene lo stato corrente di ogni task nel sistema, il quale potrà variare a seguito dell’esecuzione di determinate routine. All’atto della creazione attraverso taskSpawn(), il task assume immediatamente lo stato ready, mentre l’utilizzo di taskCreate() o taskOpen() genererà un task in stato suspended, esso potrà essere attivato immediatamente tramite taskActivate() che lo porterà nello stato ready. Elenchiamo adesso gli stati fondamentali dei task: • READY: il task non è in attesa di nessuna risorsa se non della CPU • PEND: il task è bloccato a causa della non disponibilità di alcune risorse • DELAY: il task è dormiente per una certa durata • SUSPEND: il task non è pronto per essere eseguito 18 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time • STOP: il task è stato fermato dal debugger È importante notare che lo stato dei task è additivo: un task può trovarsi simultaneamente in più stati, ad esempio DELAY + S indica che il task è sia nello stato delay che in quello suspend. La seguente figura mostra una semplice illustrazione delle transizioni dei task specificando inoltre quali sono le procedure che possono generare tali transizioni. Figura 2: stati dei task e transizioni 19 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time 4.2 Scheduling Per implementare una gestione multitasking è necessario uno schedulatore che assegna la CPU ai task pronti. VxWorks fornisce le seguenti opzioni di scheduling: • Lo schedulatore VxWorks tradizionale, basato su priorità con il supporto dell'algoritmo di schedulazione Round-Robin • Lo schedulatore VxWorks di thread POSIX, appositamente progettato per l’utilizzo di thread nei processi • La possibilità per il programmatore di sviluppatore il proprio schedulatore Nessuna delle opzioni consente la schedulazione di processi RTP, le uniche entità schedulabili sono i task e i thread. Indipendentemente dall’opzione scelta, Vxworks utilizza un algoritmo di scheduling basato su priorità, suddivise in 256 livelli numerati da 0 a 255 dove 0 indica il processo a priorità maggiore. Ad ogni task è assegnata una priorità all’atto della creazione, variabile durante il suo ciclo di vita. Le funzioni che controllano lo scheduling dei task sono: • taskPrioritySet(): cambia la priorità di un task • taskRtpLock(): disabilita il cambio di contesto nel processo • taskRtpUnLock(): abilita il cambio di contesto nel processo L'uso della funzione taskPrioritySet() fa si che il task sia posizionato alla fine della coda dei task pronti relativa al nuovo livello di priorità assegnato. Le funzioni taskRtpLock() e taskRtpUnLock() garantiscono l'esplicita disabilitazione e abilitazione dello scheduler; in particolare un task che chiama la taskRtpLock() fa si che 20 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time nessun altro task del medesimo processo possa prelazionarlo, se tale task si blocca o si sospende allora lo schedulatore manderà in esecuzione il prossimo task a priorità maggiore. È importante sottolineare che il blocco della prelazione non previene il cambio di contesto dovuto al gestore delle interruzioni. 4.2.1 Scheduler VxWorks tradizionale È lo schedulatore di default ed è incluso nel sistema tramite il componente INCLUDE_VX_TRADITIONAL_SCHEDULER. Mantiene una coda per i task ready, gestita con politica FIFO, per ogni livello di priorità. Quando la CPU è disponibile, essa viene assegnata al task che si trova in testa alla coda a priorità più alta. La posizione di un task nella coda può variare a seconda delle operazioni svolte sul task: • Se il task è prelazionato questo andrà in testa alla coda con la sua stessa priorità. • Se il task passa nello stato di pended, delayed, suspended oppure stopped sarà rimosso dalla sua attuale coda, e quando tornerà ad essere ready verrà posizionato alla fine di una coda scelta in basa alla sua priorità. • Se la priorità di un task cambia tramite la funzione taskPrioritySet(), sarà inserito alla fine della relativa coda. • Se è chiamata la funzione taskRotate(), il task sarà spostato dalla testa all'ultima posizione della sua coda. Poiché, come precedentemente affermato, lo schedulatore VxWorks tradizionale è uno schedulatore con prelazione basata su priorità, quando è presente un task ready con priorità maggiore di quello al momento in esecuzione, quest'ultimo sarà prelazionato e il suo contesto salvato e sostituito con quello del task a priorità maggiore. L'esempio in figura mostra il task t1 prelazionato a favore di t2 a sua volta prelazionato a favore t3. Quando t3 termina, t2 riprende ad eseguire seguito a sua volta da t1. 21 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Figura 3: scheduling prioritario Lo svantaggio di questo algoritmo di scheduling si manifesta quando sono presenti più processi pronti con il medesimo livello di priorità: dopo aver assegnato la CPU a uno dei task questo la manterrà per tutto il tempo necessario, rischiando di ritardare eccessivamente l'esecuzione degli altri task con la medesima priorità. Questo problema è risolto affiancando all'algoritmo di scheduling basato su priorità quello Round-Robin; viene quindi assegnato ad ogni task un quanto di tempo time-slice entro cui puo mantenere la CPU. Questa politica di sheduling non va a intaccare la schedulazione di tipo prioritario precedentemente descritta, infatti, in ogni istante, indipendentemente dal quanto di tempo, un processo può essere interrotto a favore di un altro a priorità maggiore; se ciò accade il processo che è stato prelazionato riprenderà l'esecuzione in seguito e gli verrà assegnato il quanto di tempo residuo. La seguente figura mostra il contributo dell’algoritmo Round-Robin alla schedulazione di tipo prioritario. 22 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Figura 4: scheduling prioritario con Round-Robin L'algoritmo di scheduling Round-Robin è quindi usato per schedulare task che hanno la stessa priorità, questo è abilitato dalla chiamata kernelTimeSlice(), che riceve come argomento la durata del time-slice, se tale valore è 0 allora lo schedulatore Round-Robin verrà disabilitato. 4.2.2 Scheduler VxWorks di thread POSIX L’implementazione dello schedulatore di thread POSIX in VxWorks migliora lo scheduler tradizionale VxWorks e fornisce servizi addizionali di scheduling consentendo l’esecuzione di thread nei processi. Lo schedulatore di thread POSIX schedula task e thread in modo pressochè uguale allo scheduler VxWorks tradizionale, con una differenza nella gestione della priorità tra task e thread: quando la priorità di un thread è abbassata, lo schedulatore posiziona il thread in testa alla coda relativa alla nuova priorità; se invece è un task a subire la diminuzione di priorità questo sarà posizionato alla fine della relativa coda, come avviene nella schedulazione tradizionale in VxWorks. 23 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Per usufruire dello schedulatore di thread POSIX è necessario includere il componente INCLUDE_POSIX_THREAD_SCHEDULER. 4.3 Intertask ed interprocess communication Vediamo adesso quali sono i meccanismi che legano la comunicazione fra task e fra processi. VxWorks ne fornisce un ampio set: • Memoria condivisa • Semafori • Code di messaggi e Pipe • Eventi VxWorks • Segnali 4.3.1 Memoria condivisa La comunicazione fra processi avviene attraverso la condivisione un'area accessibile a più applicazioni real-time, organizzata in un singolo blocco contiguo di memoria virtuale. La libreria che consente la creazione e gestione di una memoria condivisa è la sdLib e consente di: • creare una regione di memoria condivisa • mappare la regione nel contesto di memoria del processo così che possa accedervi • cambiare gli attributi di protezione della regione • eliminare la regione dal contesto di memoria di un processo quando questo non è ha più bisogno • cancellare la regione quando essa non è più associata ad alcun processo Affinchè le applicazioni possano usufruire di una memoria condivisa, il componente INCLUDE_SHARED_DATA deve essere incluso in VxWorks. 24 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Una regione di memoria condivisa può essere creata con sdCreate() ed è mappata immediatamente nel contesto di memoria del creatore. I parametri richiesti per la creazione sono: il nome della regione, la sua grandezza, l'indirizzo fisico, gli attributi della MMU, e altri due parametri che regolano la persistenza della regione e la sua accessibilità. Gli attributi della MMU servono a gestire il tipo di permessi di accesso alla regione condivisa: read only, read/write, read/execute, read/write/execute Un processo utente che conosce il nome della regione vi può accedere tramite la funzione sdOpen(). Le modalità di cancellazione della memoria condivisa variano in base alla presenza dell'opzione SD_LINGER all'atto della creazione: se non presente la regione è cancellata quando l'ultimo processo mappato sulla regione chiama la sdUnmap() oppure quando termina; se presente la regione è cancellata solo quando è chiamata la sdDelete() quando nessun processo vi è più collegato. Per evitare di lasciare la memoria condivisa in uno stato inconsistente, bisogna garantire l'accesso mutuamente esclusivo alla risorsa mediante: la disabilitazione delle interruzioni, la disabilitazione della prelazione e il blocco delle risorse tramite semafori; quest'ultima rappresenta sicuramente il miglior approccio. 4.3.2 Semafori VxWorks affronta la problematica della mutua esclusione nell'accesso ad una risorsa condivisa e della sincronizzazione di un task con eventi esterni mediante l’implementazione di un sistema semaforico ottimizzato, garantendo un veloce e sicuro meccanismo di comunicazione fra task. 25 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time In base all’utilizzo previsto distinguiamo: • Semafori binari: i più rapidi e multiuso. Garantiscono la mutua esclusione proteggendo la risorsa critica con routine di blocco e rilascio e la sincronizzazione bloccando un task fino al manifestarsi di un evento. • Semafori mutex: ottimizzati per i problemi inerenti la mutua esclusione, come l'inversione di priorità o la cancellazione sicura, non consentono sincronizzazione. La priority inversion è risolta implementando la politica di priority-inheritance tramite l'opzione SEM_INVERSION_SAFE inserita alla creazione del semaforo; la cancellazione sicura è risolta con l'opzione SEM_DELETE_SAFE che impedisce ad un task di essere cancellato quando è situato in un’area critica. • Semafori di conteggio: configurabili ad un qualunque valore maggiore uguale di 0, specificato alla creazione. • Semafori read/write: distinguono l'operazione da eseguire sulla risorsa. In molti casi VxWorks fornisce un set di operazioni di controllo uniche per tutti i tipi di semafori, quelle fondamentali sono: • semTake(): decrementa il valore del semaforo, se questo era 0 blocca il task che l'ha invocata che assume lo stato pend • semGive(): incrementa la variabile interna ed eventualmente risveglia un task bloccato Esistono inoltre funzioni relative a specifici tipi di semaforo, ad esempio: • Creazione del semaforo: semXCreate() con X che varia in base al tipo di semaforo: B binario, M mutex, C conteggio, RW read/write 26 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time • Blocco e sblocco di semafori read/write: semRTake(), semWTake(), semRWGive() Notiamo quindi che un semaforo read/write utilizza delle routine diverse dagli altri semafori; deve essere, infatti, specificato il tipo di accesso all’atto della chiamata bloccante. Si ha: • semRTake(): per operazioni di lettura, è concesso l'uso concorrente della risorsa • semWTake(): per operazioni di scrittura, è permesso solo l'uso esclusivo della risorsa All'atto di una semRWGive() sarà data precedenza ai task che hanno fatto richiesta di accesso in scrittura, indipendentemente dalla priorità dei processi. All'interfaccia uniforme dei semafori VxWorks aggiunge speciali opzioni: • timeout: restringe la possibilità di chiamare la semTake() ad uno specifico periodo di tempo; chiamate al di fuori di tale periodo restituiscono un errore. • queuing: permette di scegliere il meccanismo di accodamento tra FIFO e prioritario. • interruptible: un task pendente non può essere interrotto da un segnale, tale comportamento può cambiare inserendo l'opzione SEM_INTERRUPTIBLE all'atto della creazione del semaforo. • VxWorks events: un semaforo appena sbloccato può mandare eventi VxWorks a specifici task. 4.3.3 Code di messaggi In VxWorks il meccanismo base di comunicazione fra task in un ambiente monoprocessore è lo scambio di messaggi; a questo scopo sono messe a disposizione due librerie: msgQLib creata espressamente per VxWorks e mqPxLib basato sullo standard POSIX 1003.1b per l'estensione real-time. 27 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Ogni task implementa la gestione delle code di messaggi, tramite le seguenti routine: • msgQCreate(): alloca ed inizializza una coda di messaggi, richiede come input i seguenti parametri: il massimo numero di messaggi accodabili, la dimensione massima in byte di ogni messaggio, il meccanismo di gestione della coda (MSG_Q_FIFO / MSG_Q_PRIORITY). • msgQDelete(): termina e dealloca una coda di messaggi. • msgQSend(): invia un messaggio in una coda, se nessun task attende messaggi, questo è bufferizzato, altrimenti è mandato al primo task in attesa sulla coda. • msgQRecive(): riceve un messaggio, se presente, da una coda; altrimenti si blocca e viene posto nella coda dei task che attendono messaggi. Nella routine msgQSend() è possibile specificare la priorità del messaggio mediante le opzioni MSG_PRI_NORMAL e MSG_PRI_URGENT, rispettivamente il messaggio sarà posto alla fine o all'inizio della coda. Come per i semafori, un task in attesa su una coda di messaggi non è può essere interrotto a meno che la coda non sia stata creata con l'opzione MSG_Q_INTERRUPTIBLE. Possono inoltre essere inviati eventi VxWorks a specifici task, quando un messaggio giunge in una coda sulla quale non c'è un task in attesa. Per utilizzare i servizi offerti dalle code di messaggi, VxWorks include il componente INCLUDE_MSG_Q. 4.3.4 Pipe Sono dispositivi virtuali I/O controllati dal driver pipeDrv e costituiscono un'alternativa alle code di messaggi usate per l' I/O system di VxWorks. La routine pipeDevCreate() crea una pipe e la coda di messaggi sottostante, specificado il nome della pipe, il massimo numero di messaggi bufferizzabili e la lunghezza massima di 28 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time ciascun messaggio. Un task può essere bloccato su una pipe in seguito ai seguenti eventi: • leggere da una pipe vuota finchè il dato non è disponibile • scrivere in una pipe satura finchè non si libera nuovo spazio Le pipe forniscono un importante servizio assente nelle code di messaggi: la possibilità di usare la select(). Questa routine permette ad un task di attendere la disponibilità di dati su un qualsiasi dispositivo I/O all’interno di un set, tra cui socket network e dispositivi seriali. Per utilizzare i servizi offerti dalle pipe VxWorks include il componente INCLUDE_PIPES. 4.3.5 Eventi VxWorks Forniscono un mezzo di comunicazione e sincronizzazione tra task e: altri task, ISR, semafori, code di messaggi. Gli eventi vengono utilizzati poichè la loro gestione è leggera, infatti nessun oggetto viene creato. Un task può gestire fino a 32 eventi, identificati da flag, tali flag sono memorizzati in un registro eventi e sono codificati su 32 bit (i bit dal 25 al 32 sono riservati per Wind River). Da notare che un flag evento di per se non ha alcun significato, ma lo acquista in base a come è stato progettato il task che dovrebbe reagire a tale flag. Gli eventi sono simili ai segnali, inviati ai task in maniera asincrona, ma a differenza dei segnali la loro ricezione è sincrona e non richiede un gestore. Per poter usufruire dei servizi concessi tramite gli eventi, VxWorks deve essere configurato con il componente INCLUDE_VXEVENTS. Un task può attendere uno o più eventi, o semplicemente controllarne la ricezione tramite la chiamata eventReceive() bloccante. Possono essere ricevuti eventi da semafori o da 29 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time code di messaggi a patto che il task vi sia precedentemente registrato rispettivamente tramite semEvStart() e msgQEvStart(); solo un task alla volta può essere registrato a un semaforo o ad una coda di messaggi; inoltre un task può ricevere eventi da un altro task o dall'ISR inviati tramite la eventSend(). Una vota che un task è registrato ad un semaforo, riceverà un evento da questo nel caso di una chiamata semGive() e di assenza di task pendenti sul relativo semaforo. Se invece il task è registrato presso una coda di messaggi, riceverà un evento nel caso sia ricevuto un messaggio ma non ci sono task in attesa di questo. Per fermare l'invio di eventi da parte di un semaforo o di una coda di messaggi si usano rispettivamente le routine semEvStop() e msgQEvStop(). 4.3.6 Segnali Sono servizi del sistema operativo sviluppati per controllare eventi eccezionali e alterazioni asincrone del flusso di controllo; risultano essere simili in molti aspetti alle interruzioni hardware. In accordo con POSIX, VxWorks utilizza 63 segnali, ognuno dei quali ha uno specifico numero identificativo e un'azione predefinita, il valore 0 è riservato al segnale NULL. I segnali possono essere mandati da task ad altri task o a processi, e posso essere ricevuti o ignorati, ciò dipende dalla configurazione della maschera dei segnali del ricevente; alcuni di essi tuttavia non possono essere ignorati come SIGKILL o SIGSTOP. Il loro invio avviene mediante la routine sigqueue() che ammette bufferizzazione, mentre la ricezione implica l’interruzione dell’esecuzione corrente del task e l’avvio del gestore segnali. Per utilizzare i servizi dei segnali VxWorks include il componente INCLUDE_SIGEVENT. 30 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Capitolo 5 I/O system Il sistema I/O di VxWorks è sviluppato per presentare una semplice e uniforme interfaccia indipendente per ogni tipo di dispositivo, includendo: • dispositivi orientati ai caratteri come terminali o righe di comunicazioni • dispositivi a blocchi ad accesso casuale come dischi • dispositivi virtuali come pipe e socket • dispositivi di monitoraggio e controllo come dispositivi I/O digitali o analogici • dispositivi network che consentono l’accesso a dispositivi remoti Il sistema I/O di VxWorks fornisce librerie C standard sia per l’I/O base che per quello bufferizzato, rispettivamente compatibili con UNIX e con ANCI C. Per usufruire dei servizi dell’I/O system bisogna includere il componente INCLUDE_IO_SYSTEM. Il seguente diagramma illustra le relazioni esistenti fra i diversi componenti del sistema I/O disponibili per i processi real-time. 31 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Figura 5: Panoramica sul sistema I/O In VxWorks l’accesso ai dispositivi di I/O avviene attraverso l’utilizzo di file, che possono essere riferiti a: • un dispositivo non strutturato come un canale di comunicazione seriale o una pipe • un dispositivo ad accesso casuale strutturato contenente un file system 5.1 I/O base È il livello più basso dell’I/O in VxWorks e la sua interfaccia è compatibile con le primitive I/O nella libreria standard C. Ci sono sette routine I/O base: creat(), remove(), open(), close(), read(), write(), ioctl(). Al livello I/O base i file sono identificati da un descrittore, valore intero restituito dalla chiamata open() o creat() e utilizzato come parametro dalle altre routine. I descrittori dei file non sono globali, ed il kernel, così come ogni RTP, ha il suo set; i task all’interno del kernel o di uno specifico processo condividono i descrittori del file. Il numero massimo di file descriptor che possono essere aperti simultaneamente in un processo è specificato nella file descriptor table . 32 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Tre descrittori file hanno un particolare significato e sono: • 0 usato per lo standard input (stdin) • 1 usato per lo standard output (stdout) • 2 usato per lo standard error output (stderr) Questi sono usati per rendere indipendente l’applicazione dall’attuale assegnamento I/O. 5.2 I/O bufferizzato La libreria buffered I/O è compatibile con la libreria stdio di UNIX e Windows e fornisce il supporto completo ANSI C. Nonostante il sistema I/O di VxWorks sia efficiente, le chiamate di basso livello generano un seppur ridotto overhead. Per rendere tale sistema più efficiente la libreria stdio implementa uno schema di buffering in cui i dati sono letti e scritti in grandi segmenti e bufferizzati privatamente, tale bufferizzazione è trasparente all’applicazione essendo automaticamente gestita dalle routine e dalle macro stdio. Ad esempio per aprire un file con stdio si utilizza la routine fopen() invece di open(), questa ritorna un puntatore associato con una struttura di tipo FILE contenente il file descriptor per accedere ai dati del file corrispondente. 5.3 I/O asincrono VxWorks consente di gestire le operazioni di input e output in maniera asincrona (AIO Asynchronous Input/Output), disaccoppiandole dall'attività di particolari task quando sono logicamente indipendenti. Il beneficio che ne consegue è un'aumento dell'efficienza generale garantito dalla possibilità di servire operazioni I/O esclusivamente quando tutte le risorse necessarie sono disponibili, evitando, quindi, inutili blocchi. Per includere il supporto AIO bisogna configurare VxWorks con il componente INCLUDE_POSIX_AIO. 33 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Le principali routine POSIX AIO, presenti nella libreria aioPxLib, sono: • aio_read(): crea un'operazione asincrona di lettura • aio_write(): crea un'operazione asincrona di scrittura • lio_listio(): crea una lista di massimo LIO_MAX richieste I/O asincrone • aio_returned(): rileva lo stato di un'operazione AIO completata • aio_suspended(): mette in attesa finché un'operazione di AIO è conclusa, interrotta o scaduta Ogni routine AIO, tra i suoi parametri, presenta un AIO control block (aiocb), il chiamante deve specificare lo spazio riservato all'aiocb e questo deve rimanere disponibile durante tutta l'operazione AIO. Ogni aiocb descrive una singola operazione. 34 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Capitolo 6 File system VxWorks mette a disposizione una grande varietà di file system adattabili a differenti tipi di applicazioni. Più file system posso essere presenti contemporaneamente su un singolo sistema VxWorks, su più istanze. Un’istanza (vedi figura seguente) è quindi composta da un file system, da un block device, e dall’XBD extended block device che fornisce servizi per quello specifico blocco. Figura 6: Istanza VxWorks 35 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time I file system usati per i dispositivi removibili fanno uso di un file system monitor, utilizzabile includendo il componente INCLUDE_FS_MONITOR, per la rilevazione automatica del dispositivo inserito e del relativo file system. Illustriamo quali sono i principali file system utilizzabili in VxWorks: • VRFS Virtual Root File System: un file system virtuale per l’uso di applicazioni che richiedono file system POSIX. Si presenta come una semplice cartella nella quale gli altri dispositivi e file system possono accedere. Per includere VRFS in VxWorks bisogna configurare il kernel con il componente INCLUDE_VRFS. • HRFS Highly Reliable File System: un file system transazionale sviluppato per sistemi real-time: non viene mai a trovarsi in uno stato inconsistenete ed è rapido nel recupero in caso di cali di alimentazione, organizza in maniera efficiente la gestione gerarchica dei file e delle cartelle, ed è configurabile con politiche di commit. È necessario includere il componente INCLUDE_HRFS. • dosFs: un file system compatibile con MS-DOS, può essere usato su memorie flash. Le principali caratteristiche sono: gestione gerarchica di file e cartelle, possibilità di scelta tra allocazione contigua o non contigua dei file, capacità di avviare VxWorks da un file system dosFs. • rawFS: fornisce un semplice file system che tratta un intero disco come un singolo grande file, ciò ottimizza l’occupazione di memoria e le performance se non sono richieste funzioni complesse; tutte le operazioni di lettura e scrittura fanno riferimento ad indirizzi composti dalla combinazione della prima locazione del disco e di un offset, espresso in byte. 36 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time • cdromFS: permette alle applicazioni di leggere dati da CD-ROM formattati in accordo con lo standard ISO 9660; per accedere a tali dati vengono usate le primitive POSIX I/O standard. • ROMFS Read-Only Memory File System: un semplice file system configurato in sola lettura che memorizza file e cartelle in modo lineare e fornisce la possibilità di raggruppare applicazioni VxWorks con il sistema operativo. È necessario includere il componente INCLUDE_ROMFS. • TSFS Target Server File System: progettato per fini di sviluppo e diagnostica, virtualizza le caratteristiche del file system relativo al target supportando tutte le funzionalità di VxWorks, in un ambiente completamente localizzato sulla macchina host; fornisce tutte le caratteristiche I/O dei driver network per l’accesso remoto, senza richiedere alcuna risorsa target, ad eccezione di quelle indispensabili per la comunicazione tra sistema target e host. Bisogna includere il componente INCLUDE_WDB_TSFS. 37 Analisi del sistema operativo VxWorks per lo sviluppo di applicazioni real-time Conclusione Sono state descritte le caratteristiche salienti del sistema operativo VxWorks. Quello che se ne può trarre, oltre a una mera conoscenza tecnica, e quello che può far apprezzare VxWorks è la possibilità concessa al singolo programmatore di modificare ed eventualmente aggiungere pezzi rilevanti di logica al sistema operativo. Una prova di ciò è la possibilità di programmare un proprio schedulatore, oppure modificare la logica generale di scheduling, ad esempio, sostituendo l'algoritmo Round-Robin del sistema (il quale genera comunque overhead a causa dei continui cambi di contesto) con un algoritmo di tipo cooperativo più ancestrale (lo aveva la famiglia dei Windows 3.x, ad esempio, prima che subentrasse Windows NT) in cui è l’applicativo stesso che cede spontaneamente la CPU per favorire il completamento di tutti i task facendolo, però, il minor numero di volte e solo quando serve. La possibilità di intervenire sui meccanismi del codice di sistema non è cosa molto ben vista, ai nostri giorni, da parte dei produttori di sistemi operativi proprietari. L'opportunità che offre in quest'ambito un prodotto come VwWorks lo rende, in qualche modo, più vicino ai sistemi aperti e ciò offre un'apprezzabile chance al singolo sviluppatore di esprimere le proprie capacità creative. 38 Bibliografia [1] Wind River Systems, Inc. 2008. “VxWorks Application Programmer’s Guide, 6.7” [2] Wind River Systems, Inc. 2007. “VxWorks Kernel Programmer’s Guide, 6.6” [3] Wind River Systems, Inc. 2008. “VxWorks BSP Developer’s Guide, 6.7” [4] Wind River Systems, Inc. 2006. “VxWorks Workbench User’s Guide, 2.5” [5] Wind River Systems, Inc. 2005. “VxWorks Simulator User’s Guide, 6.1” [6] Wind River Systems, Inc. “www.windriver.com” [7] Mario Malcangi. 2011. “Sistemi embedded e sistemi operative real-time” [8] Rafael V.Aroca, Glauco Caurin. “A Real Time Operating Systems (RTOS) Comparison” [9] Jim Collier. 2004. “An Overview Tutorial of VxWorks Real-Time Operating System” [10] Colin Walls. 2010. “AMP vs SMP” [11] IEEE Computer Society. 2004. “1003.13 IEEE Standard for Information Technology - Standardized Application Environment Profile (AEP) - POSIX Realtime and Embedded Application Support” 39