UNIVERSITA’ POLITECNICA DELLE MARCHE Facoltà di Ingegneria Corso di laurea triennale in Ingegneria Informatica e dell’Automazione ANALISI DI SISTEMI OPERATIVI REAL-TIME IL SISTEMA OPERATIVO SHARK Relatore: Prof. Aldo Franco Dragoni Candidato: Pianosi Luca Anno Accademico 2007/2008 1 Indice 1 Sistemi operativi real-time 1.1 Cos’è un sistema operativo real-time 1.2 Caratteristiche dei sistemi real-time 1.3 Hard e soft real-time 1.4 Architettura dei sistemi real-time 1.5 Scheduling dei sistemi real-time 1.5.1 Schedulazione dei processi omogenei 1.5.2 Schedulazione generale in tempo reale 1.5.3 Schedulazione a frequenza monotòna 1.5.4 Schedulazione con priorità alla scadenza più vicina 1.6 Alcuni esempi di RTOS 2 Il sistema operativo S.Ha.R.K. 2.1 Architettura di S.Ha.R.K. 2.1.1 OSLibrary 2.1.2 Generic kernel 2.1.3 Libraries 2.1.4 Applicazioni 2.1.5 Scheduling 2.1.6 Gestione delle risorse condivise 2.2 Programmazione in S.Ha.R.K. 2.2.1 File di inizializzazione 2.2.2 Il task di inizializzazione 2.2.3 La funzione principale: main 2.2.4 I task 2.3 Modelli 2.3.1 Modelli di task 2.3.2 Modelli di risorse 2.3.3 Modelli mutex 2.3.4 Funzionamento del model mapper 3 Installazione di S.Ha.R.K. 3.1 Piattaforme supportate 3.2 Installazione 3.2.1 Linux 3.2.2 FreeDos 3.2.3 Windows Millenium/NT/2000/XP 3.3 Come partizionare il disco fisso 4 Sperimentazione su macchina virtuale 4.1 Installazione di FreeDos 4.2 Installazione di S.Ha.R.K. 4.3 Test di una demo: JUMPBALL 2 Capitolo 1 Un sistema di elaborazione dovrebbe avere le seguenti caratteristiche: - Semplice ed economico; - Capace di sfruttare al massimo le risorse a disposizione; - Capace di massimizzare la velocità di elaborazione (throughput); - Essere adeguato alle esigenze di una multiutenza; - Essere in grado di rispettare dei vincoli temporali nelle interazioni con il mondo esterno; - Essere affidabile (reliability). Naturalmente il sistema operativo, implementando sulla macchina fisica una macchina virtuale, ha molta responsabilità nel raggiungimento di questi obiettivi. Vi sono molte situazioni in cui il comportamento temporale di un sistema è fondamentale, ad esempio nell’interazione con il mondo esterno, sede di fenomeni asincroni. 1.1) Cos’è un sistema operativo real-time Un sistema operativo real-time o in tempo reale (abbreviato in RTOS) è un sistema operativo specializzato per il supporto di applicazioni software real-time. Questi sistemi vengono utilizzati tipicamente in ambito industriale (controllo di processo, pilotaggio di robot, trasferimento di dati nelle telecomunicazioni) o comunque dove sia necessario ottenere una risposta dal sistema in un tempo massimo prefissato. Da un punto di vista puramente teorico l'intervallo di tempo in cui il sistema operativo/applicativo deve reagire non ha importanza, infatti un sistema operativo in RT non deve essere necessariamente veloce, la cosa importante è che risponda entro un tempo massimo ben conosciuto. Un sistema in RT deve garantire una elaborazione rapida dal punto di vista temporale, anche se è possibile che la risposta non sia precisissima. 3 Ad esempio una funzione di calcolo può calcolare il peso di un oggetto senza giungere alla determinazione del milligrammo perché deve comunque fornire una risposta in un preciso tempo da quando si è posto il peso nella bilancia. Tale ragionamento non significa che si possono anche dare risposte errate ma che bisogna spostare l'attenzione sul tempo della risposta. Questi tipi di sistemi devono dare l'opportunità allo sviluppatore di conoscere a priori le tempistiche in cui si ottiene la risposta. Viceversa un sistema operativo "tradizionale" deve garantire un'elaborazione corretta dal punto di vista logico, anche se è possibile tollerare che qualche risposta arrivi in anticipo o in ritardo di tempo. In un sistema RT il carico di lavoro è scomponibile in 3 componenti: - Attività costituite da azioni periodiche - Azioni aperiodiche - Attività di sottofondo Lo scopo temporale di un’azione è l’intervallo di tempo tra l’istante in cui si verifica l’evento che attiva l’azione (triggering event) e l’istante in cui tale attività deve essere completata (deadline). Si definisce deadline l’istante di tempo dopo il quale la computazione non è semplicemente in ritardo bensì è errata. 4 Le deadline sono vincoli di tempo stringenti imposti ai tasks dall’ambiente esterno. Ad esempio, se un sistema di controllo di un processo industriale richiede che vengano controllati dei sensori ogni p unità di tempo, il task relativo dovrà essere in grado di portare a termine il controllo entro p unità di tempo, indipendentemente da quali sono gli altri task del sistema. 1.2) Caratteristiche dei sistemi real-time Un sistema per poter operare in "tempo reale" deve possedere le seguenti caratteristiche: - Deve avere una architettura detta "multithread preemptive" che sia in grado quindi di gestire molteplici sottoprocessi; - Deve essere di tipo deterministico ed operare con un comportamento predeterminabile in ogni situazione; - Deve essere in grado di gestire la sincronizzazione dei sottoprocessi; - Deve in sostanza garantire tempi di intervento predicibili. Si rammenta che tempo predeterminato non è sinonimo di velocità: per un sistema real-time è importante solo poter prevedere in modo sicuro quale sarà il tempo di esecuzione di una determinata operazione e non che quella operazione venga eseguita in fretta. I prodotti delle famiglie Windows e Unix non soddisfano le caratteristiche del realtime: ad esempio, pur gestendo l'esecuzione di più processi contemporaneamente, non è possibile prevedere in alcun modo quale sarà il tempo di esecuzione di un singolo processo. Inoltre l'utilizzo di hard disk per la conservazione dei dati rende impossibile stabilire con certezza quanto tempo sarà necessario per reperire l'informazione utile alla corretta esecuzione del codice. 5 1.3) Hard e soft real-time I sistemi RT si possono dividere in due categorie: - I sistemi "hard" richiedono un rigida precisione nella risposta in termini temporali, infatti il mancare una scadenza ha come conseguenza quello di invalidare il funzionamento dell'intero sistema. Un esempio di sistema "hard" potrebbe essere quello di una catena di montaggio, in cui basta che un pezzo abbia un ritardo e l'intera catena si blocca perché quel pezzo è indispensabile. - I sistemi "soft" si limitano ad un rispetto statistico (tolleranza) dei vincoli di tempo che, qualora prolungati, portano ad un degrado dell'applicazione. Degrado che può essere comunque tollerato, in funzione dell'importanza per l'utilizzatore in termini di costo. Sostanzialmente questa distinzione si traduce nella diversa quantificazione dei costi di una possibile inesattezza temporale del sistema. Un esempio di sistema soft real time può essere un riproduttore DVD, in cui il mancato rispetto dei vincoli si traduce in un degrado della qualità del filmato, ma non pregiudica il proseguimento della riproduzione. Come precisato sopra, la differenza tra i due tipi di sistemi sta nei tempi di risposta. In un sistema Hard real-time i tempi di risposta devono assolutamente essere rispettati, altrimenti è minato il funzionamento dell'intero processo. In certi sistemi il mancato rispetto dei tempi previsti può risultare pericoloso, basti pensare a un programma delle superfici di volo di un aereo oppure ad un software di controllo di una centrale nucleare. 6 Vediamone le principali caratteristiche: - I sistemi operativi general purpose (non generati per uno scopo specifico) non supportano la funzionalità hard real-time. - Deve essere garantito il rispetto della deadline (tempo massimo per concludere un processo) da parte degli algoritmi di scheduling. Un sistema Soft real-time è invece caratterizzato dal fatto che se un tempo di risposta non viene rispettato non è minacciato il funzionamento dell'intero sistema, insomma l'inesattezza temporale può essere tollerata. - I sistemi operativi general purpose (sistemi real-time embedded, come i palmari e sistemi di allarme) supportano la funzionalità soft real-time. - Quando è attivo un processo critico, esso ha la priorità su tutti gli altri processi, fino ad operazione conclusa. - È opportuno cercare di ridurre al minimo i fattori negativi riguardanti i tempi di risposta 1.4) Architettura dei sistemi real-time I sistemi operativi real-time fornisco alle applicazioni un'astrazione dell'hardware (Hardware Abstraction Layer, HAL). L'Hardware Abstraction Layer mette a disposizione delle applicazioni una serie di servizi (primitive real-time). E’ un insieme di funzioni di I/O il più possibile generiche e semplici, il cui compito è di tenere conto di tutte le differenze fra dispositivi fisici diversi, al posto del programma che li userà, nascondendogli la vera identità e natura di essi: per esempio il programma, invece di aprire personalmente un file chiederà all'HAL di farlo per lui e l'HAL, appena esaudita la richiesta, gli passerà un riferimento al file per la lettura (o lo leggerà per lui). 7 I programmi comunicano al sistema operativo le operazioni da compiere e il sistema operativo provvede a effettuare le modifiche necessarie. Questo consente di modificare l'hardware deposto alla visualizzazione senza dover modificare tutti i programmi. Basta modificare lo strato che accede all'hardware! L’Hardware Abstraction Layer può essere utilizzato per emulare componenti non presenti nel sistema operativo o nell'hardware. Per esempio le moderne schede grafiche non gestiscono nativamente le modalità CGA, EGA o VGA. Queste modalità vengono emulate dall'HAL che provvede a tradurre le istruzioni che gestiscono queste modalità in comandi comprensibili dalla scheda grafica. L'emulazione sfruttando un HAL viene utilizzata in molti sistemi per consentire la retro compatibilità con applicazioni arcaiche. 1.5) Scheduling dei sistemi real-time Gli eventi a cui un sistema real-time deve poter reagire possono essere classificati in: - periodici, che si verificano ad intervalli di tempo regolari; - aperiodici, che si verificano in modo imprevedibile. 8 Nel caso di eventi periodici, se esistono m eventi e se l’evento i arriva con periodo Pi e richiede Ci secondi di tempo di CPU per essere gestito, il carico può essere gestito solo se: (1) Un sistema real-time che rispetta questo vincolo è detto schedulabile. Gli algoritmi di scheduling real time possono essere distinti in: - statici: la decisione di schedulazione è presa prima che il sistema inizi l'esecuzione dei processi. Questi metodi richiedono che le informazioni complete circa il lavoro da fare e le scadenze da rispettare siano disponibili in anticipo rispetto all'esecuzione dei processi - dinamici: la decisione di schedulazione è presa durante l'esecuzione dei processi. Non hanno restrizioni circa la conoscenza anticipata sui tempi di esecuzione e le scadenze da rispettare. Nel seguito verranno analizzate alcune politiche di scheduling real time, facendo riferimento al particolare contesto delle applicazioni multimediali. Infatti, i sistemi operativi che supportano applicazioni multimediali, differiscono da quelli tradizionali per tre aspetti principali: - la schedulazione dei processi - il file system - la schedulazione del disco 9 1.5.1) Schedulazione di processi omogenei E’ la situazione che si presenta quando più processi con uguali richieste e vincoli temporali devono essere serviti in modo efficiente dalla politica di scheduling. Ad esempio, una tale situazione si presenta per un server video che deve supportare la visualizzazione di un numero fisso di video tutti caratterizzati dalla stessa frequenza dei frame (frame rate), risoluzione video, frequenza di trasmissione dati, etc. In questa situazione una semplice ma efficace politica di scheduling è il round-robin. Infatti, tutti i processi sono ugualmente importanti, hanno la stessa quantità di lavoro da svolgere e si bloccano quando hanno terminato l'elaborazione del frame corrente. L'algoritmo di schedulazione può essere ottimizzato aggiungendo un meccanismo di temporizzazione per assicurare che ogni processo sia eseguito alla frequenza corretta. 1.5.2) Schedulazione generale in tempo reale Il semplice modello precedente si presente raramente nella pratica. Un modello più realistico prevede la presenza di più processi che competono per l'uso della CPU, ciascuno con il proprio carico di lavoro e le proprie scadenze temporali. Nel seguito supporremo che il sistema conosca la frequenza con cui eseguire ciascun processo, quanto lavoro debba compiere ogni processo e la successiva scadenza temporale. Come esempio di ambiente in cui lavori uno schedulatore multimediale in tempo reale si considerino i tre processi A, B e C di Fig. 1. Il processo A viene eseguito ogni 30ms e ogni frame richiede 10ms di tempo di CPU. In assenza di competizione sarebbe eseguito nei periodi A1, A2, A3, ecc, ciascuno 30ms dopo il precedente. Ogni periodo di CPU gestisce un frame e ha una scadenza, cioè deve terminare prima che inizi il successivo. I processi B e C in sono eseguiti rispettivamente 25 e 50 volte al secondo, con tempi di calcolo di 15ms e 5ms. 10 Fig. 1: Tre processi periodici che visualizzano ciascuno un film. Le frequenze dei frame e i tempi di elaborazione per frame sono diversi per ciascun film. Il problema diventa quello di schedulare A, B e C per essere certi che rispettino ciascuno le proprie scadenze temporali. Prima di cercare un algoritmo di schedulazione, è necessario valutare se questo insieme di processi sia effettivamente schedulabile. A questo fine è possibile utilizzare l'Eq. (1), che nel caso dei processi A, B e C dell'esempio produce: 10/30+15/40+5/50 = 0.808 del tempo di CPU, ed il sistema dei processi è quindi schedulabile. Esistono sistemi in tempo reale in cui i processi possono subire o meno prelazione. Nei sistemi multimediali i processi sono generalmente prelazionabili: un processo la cui scadenza temporale sia a rischio può interrompere il processo in esecuzione prima che esso completi l'elaborazione del proprio frame; quando ha terminato, il processo precedente può continuare. 1.5.3) Schedulazione a frequenza monotòna Il classico algoritmo statico di schedulazione in tempo reale per processi periodici e prelazionabili è l’RMS (Rate Monotònic Scheduling, schedulazione a frequenza monotòna). E’ utilizzabile per processi che soddisfano le seguenti condizioni: - ogni processo periodico deve essere completato entro il suo periodo di tempo; - nessun processo è dipendente dagli altri; - ogni processo necessita della stessa quantità di tempo di CPU per ogni periodo di esecuzione; - i processi non periodici non hanno scadenze temporali; - la prelazione dei processi avviene senza sovraccarico di lavoro per il sistema. 11 Le prime quattro condizioni sono ragionevoli, mentre l'ultima rende più semplice la modellazione del sistema. L’RMS assegna a ciascun processo una priorità prefissata uguale alla frequenza con cui deve essere eseguito. Ad esempio, un processo che debba essere eseguito ogni 30ms (33 volte/s) acquisisce priorità 33; un processo da eseguire ogni 40ms (25 volte/s) acquisisce priorità 25, mentre un processo da eseguire ogni 50ms (20 volte/s) acquisisce priorità 20. Dato che le priorità variano linearmente con la frequenza (numero di volte al secondo in cui il processo è eseguito), il metodo è detto a frequenza monotòna. Durante l'esecuzione, lo schedulatore esegue sempre il processo pronto a più alta priorità, prelazionando, se necessario, il processo in esecuzione. Fig. 2: Esempio di schedulazione in tempo reale con RMS e EDF Tale figura illustra il funzionamento dell'algoritmo di schedulazione a frequenza monotona relativamente ai processi dell'esempio di Fig. 1. I processi A, B e C hanno rispettivamente priorità statiche 33, 25 e 20. Come conseguenza, quando A deve andare in esecuzione prelaziona ogni altro processo; B può prelazionare C, mentre C per andare in esecuzione deve attendere fino a quando la CPU non è libera. 1.5.4) Schedulazione con priorità alla scadenza più vicina L'algoritmo EDF (Earliest Deadline First, schedulazione con priorità alla scadenza più vicina), è un algoritmo dinamico e pertanto non richiede né che i processi siano periodici, né che abbiano lo stesso tempo di esecuzione per periodo di CPU. 12 Con questo approccio, è sufficiente che un processo che ha bisogno della CPU annunci la sua presenza e la scadenza temporale. Lo schedulatore mantiene una lista dei processi eseguibili, ordinata rispetto alla scadenza temporale; l'algoritmo esegue il primo processo della lista, cioè quello con scadenza temporale più vicina. Quando un nuovo processo è pronto, il sistema controlla se la sua scadenza preceda quella del processo correntemente in esecuzione; in caso affermativo il nuovo processo prelaziona quello corrente. Fig. 3: Esempio di schedulazione in tempo reale con RMS e EDF La Fig. 2 presenta un esempio di schedulazione con EDF. Un secondo esempio che confronta l’RMS e l’EDF è mostrato in Fig. 3. E’ interessante notare come nell'esempio riportato in tale figura l'algoritmo RMS fallisca. Questo è dovuto al fatto che, utilizzando priorità statiche, l'algoritmo funziona solo se l'utilizzo della CPU non è troppo elevato. È possibile dimostrare che, per ogni sistema di processi periodici, se: (2) allora è garantito il funzionamento dell’RMS (condizione sufficiente). Per m uguale a 3, 4, 5, 10, 20, 100 le massime utilizzazioni permesse sono 0.780, 0.757, 0.743, 0.718, 0.705 e 0.696. Per m che tende all'infinito, l'utilizzo massimo della CPU tende in modo asintotico a ln2 ≈ 0.69. 13 Questo significa che per m = 3, l’RMS funziona sempre se l'utilizzazione della CPU è uguale o minore di 0.780. Nell'esempio di Fig. 3 l'utilizzo della CPU, calcolato con l'Eq. (1), è così elevato (0.975) da non permettere il funzionamento dell’RMS. Il caso di Fig. 2 è invece una situazione “fortunata" in quanto anche se l'utilizzazione della CPU è 0.808, quindi maggiore del limite imposto dall’RMS per tre processi (0.780), l'algoritmo riesce ugualmente a schedulare i processi. Al contrario, l’EDF funziona sempre per qualunque insieme di processi schedulabile e può raggiungere il 100% di utilizzo della CPU. Il prezzo di questo è pagato in termini di una maggiore complessità dell'algoritmo EDF rispetto all’RMS. 14 1.6) Alcuni esempi di RTOS Esistono numerosi tipi di sistemi operativi real-time; ne elencheremo solo alcuni a titolo di esempio: Proprietari - LynxOS http://www.lynuxworks.com - Nucleus http://www.acceleratedtechnology.com/embedded/nuc_rtos.html - OSE www.enea.com - QNX http://www.qnx.com - RTXC Quadros RTOS http://www.quadros.com/products Open source - HaikuOS http://haiku-os.org/ - eCos http://sources.redhat.com/ecos - FreeRTOS http://www.freertos.org - Nut/OS http://www.ethernut.de - Prex http://freshmeat.net/projects/prex - RTAI http://www.aero.polimi.it/~rtai/ - S.Ha.R.K. http://shark.sssup.it Soffermeremo la nostra attenzione proprio sul sistema operativo S.Ha.R.K. del quale analizzeremo le caratteristiche fondamentali e l’installazione su calcolatore. 15 Capitolo 2 S.Ha.R.K. è l'acronimo di Soft Hard Real-time Kernel. È un kernel real-time didattico, sviluppato con l'obiettivo di facilitare l'implementazione e il test di nuovi algoritmi di scheduling, server aperiodici e protocolli di gestione delle risorse. Nasce nel 2000 all'interno della Scuola Superiore Sant'Anna di Pisa e deriva da un vecchio progetto del medesimo istituto, HaRTiK. Attualmente è alla versione 1.5.4 ed è rilasciato sotto licenza GPLv2; il kernel supporta i drivers per la maggior parte dei dispositivi hardware, un’interfaccia modulare per la specifica degli algoritmi di scheduling e la gestione avanzata degli eventi temporali. Si pone i seguenti obiettivi: - Semplicità nello sviluppo di nuove applicazioni; - Ampia flessibilità nella modifica delle politiche di scheduling; - Aderenza allo standard POSIX. Le principali caratteristiche di S.Ha.R.K. sono: - Kernel fortemente modulare e leggero; - Presenza di device drivers per l'hardware più comune; - Interfaccia modulare per la specifica di politiche di scheduling della CPU; - Interfaccia modulare per la specifica di politiche di gestione delle risorse condivise. 2.1) Architettura di S.Ha.R.K. E’ un kernel ad architettura dinamica e configurabile creato per supportare applicazioni hard, soft e non real-time con algoritmi di scheduling intercambiabili. E’ interamente modulare in termini di politiche di scheduling, server aperiodici e protocolli per il controllo del parallelismo, cosa che in un sistema operativo tradizionale non è invece prevista. 16 Tale modularità è ottenuta con il sistema di partizionamento delle attività tra un kernel generico e una serie di moduli che possono essere registrati in fase di inizializzazione per configurare il kernel in base a specifici requisiti di applicazione. Il maggior beneficio di questa architettura del kernel è che un’applicazione può essere sviluppata indipendentemente da ogni configurazione di sistema, così che nuovi moduli possono essere aggiunti o rimpiazzati nella stessa, in maniera da poter valutare gli effetti di una specifica politica di scheduling in termini di predicibilità, overhead e performance. Inoltre il sistema è conforme a quasi tutte le specifiche POSIX 1003.13 PSE52. 2.1.1) OSLibrary OSLib è una libreria per lo sviluppo di sistemi operativi che implementa una astrazione di una macchina generica in grado di fornire alcuni servizi di base. Tra questi abbiamo: - Cambio di contesto tra processi; - Gestione della temporizzazione; - Gestione degli interrupt; - Un subset della libreria di runtime per il linguaggio C. 17 E’ simile all’Hardware Abstraction Layer (HAL) di Windows NT solo che il suo compito non è quello di creare un’astrazione delle risorse hardware bensì ne fornisce un accesso più facile e sicuro nascondendo i dettagli di implementazione lasciando allo sviluppatore del sistema operativo la parte concettuale e di alto livello del lavoro. OSLibrary è suddivisa in quattro distinte categorie: - xlib, la libreria extender - libc, la libreria standard C - libm, la libreria matematica - kl, la libreria del kernel In relazione alle proprie richieste, un’applicazione può sfruttare i link alla sola parte di codice di cui ha bisogno senza bisogno di importare l’intera libreria. Per esempio se un’applicazione non deve utilizzare interrupt e timer e non necessita neanche della libreria standard, può utilizzare la sola xlib riducendo di conseguenza la dimensione del codice. 18 2.1.2) Generic kernel Il kernel generico permette di separare l'algoritmo di scheduling dalla gestione delle applicazioni e dai meccanismi interni del kernel medesimo. Non implementa nessun algoritmo di scheduling, ma si limita a posporre le decisioni in merito ad opportuni moduli esterni. Il kernel è detto “generico” proprio perché fornisce soltanto le primitive necessarie allo scheduling senza specificare alcun algoritmo. Ciò significa che non dispone dei concetti di priorità, deadline, code di task, ecc… Per ottenere piena modularità il kernel generico non dovrebbe essere modificato durante l’implementazione di un nuovo algoritmo. Per fare ciò è prevista una struttura dati chiamata task descriptor e contenuta nel file: include/kernel/descr.h Tale descrittore è diviso in due parti fondamentali. Una generale, comune a tutti i moduli e altre che variano da modulo a modulo. La parte generale contiene le informazioni utilizzate dal kernel generico per implementare le primitive generiche, come il contesto, l’indirizzo dello stack, la sua dimensione, i codici di errore… 19 Come già detto, non vi è alcuna informazione su deadline, priorità o tempi di riattivazione; queste vengono infatti utilizzate da uno specifico algoritmo di scheduling e quindi saranno contenute nell’apposito modulo. I punti fondamentali di questa struttura dati sono: - DWORK task_ID : numero progressivo assegnato al task in fase di creazione; - LEVEL task_level : questo campo indica il modulo che detiene il task; - BYTE *stack : è un puntatore all’indirizzo della memoria utilizzata come stack per il task; - WORD status : lo stato del task; - PID shadow : è il puntatore a shadow. Il kernel generico include poi uno stimatore del tempo di computazione dei task, denominato Job Execution Time o JET. Questo componente è utilizzato per misure di tipo statistico, per controllare le risorse utilizzate e per implementare meccanismi di protezione temporale. L'implementazione dell'algoritmo risiede in un modulo esterno, che viene configurato a run-time per mezzo dell'ausilio di uno specifico componente del kernel, il Model Mapper. Inoltre pone una distinzione tra schedulazione e dispatching. La schedulazione è interpretata come l’attività con cui il kernel generico richiede al modulo le indicazioni per l’esecuzione dei task mentre il dispatching come l’attività con la quale si ordina al modulo l’esecuzione del task in questione. 2.1.3) Libraries I driver dei dispositivi sono una parte critica dei sistemi real-time. Cercare di adattare IRQ e TIMER HANDLER, provenienti da un dispositivo, all’interno del contesto di un task, è una priorità per i sistemi operativi come S.Ha.R.K. 20 L'esportazione delle API (Application Programming interface) del kernel avviene per mezzo di uno strato di librerie intermedie. Queste librerie permettono a S.Ha.R.K. di supportare l'hardware più comune. I driver sono quelli esportati da Linux 2.6. E’ stato progettato e testato un livello di Emulazione di Linux. Tale livello fornisce un mezzo indipendente con il quale è possibile compilare i driver originali senza creare conflitti con S.Ha.R.K. e le OSlib. Un interfaccia glue-code permette l’accesso ai driver API. Il livello di emulazione necessita di uno specifico modulo del kernel per funzionare. Tale modulo ha due importanti obiettivi: - creare un contesto ad alta priorità di esecuzione per IRQ e TIMER HANDLER dei driver; - mantenere il comportamento dei driver entro certe costrizioni real-time, evitando che un possibile malfunzionamento o un abuso di risorsa possa causare un errore di sistema. 21 Entrambe i punti sono garantiti per mezzo del modulo INTDRIVE che è un modulo ad alta priorità creato per la gestione delle richieste dei driver. Per caricare il livello di emulazione il modulo INTDRIVE deve essere presente all’interno del sistema. TIME { _ _kernel_register_levels_ _(void *arg) INTDRIVE_register_level(INTDRIVE_Q,INTDRIVE_D,INTDRIVE_FLAG); EDF_register_level(EDF_ENABLE_ALL); } Dopo la registrazione di questo modulo può iniziare la procedura di inizializzazione, che deve essere fatta all’interno dell’initfile, prima di entrare nell’applicazione realtime. Durante tale procedura i dispositivi possono bloccare il sistema per un lasso di tempo impredicibile quindi nessun dispositivo utilizzato deve essere pronto prima che i task real-time siano schedulati. Una possibile implementazione standard è la seguente: TASK _ _init_ _(void *arg) { Struct multiboot_info *mb = (struct multiboot_info *)arg; set_shutdown_task(); device_drivers_init(); sys_atrunlevel(call_shutdown_task, NULL, RUNLEVEL_SHUTDOWN); _ _call_main_ _(mb); return NULL; } int device_drivers_init(){ KEYB_PARMS kparms = BASE_KEYB; LINUXC26_register_module(); PCI26_init(); INPUT26_init(); KEYB26_init(&kparms); return 0; } La funzione device_drivers_init() chiama tutte le funzioni di inizializzazione. LINUXC26 è appunto il livello di emulazione di Linux e deve essere caricato per primo. 22 L’ordine di inizializzazione segue l’albero delle dipendenze dei driver in figura sotto: Se tale ordine non è rispettato la procedura di inizializzazione fallisce. Un altro punto critico è la sequenza di terminazione (o shutdown). Quando viene chiamata una sys_end() o una exit() il sistema deve chiudere i driver dei dispositivi il prima possibile. Sfortunatamente lo shutdown dei driver deve essere chiamato quando il kernel è ancora in modalità multitasking init device_drivers_close () { KEYB26_close(); INPUT26_close (); return 0; } Il RUNLEVELS di S.Ha.R.K. da la possibilità di implementare una procedura sicura e trasparente per terminare il sistema senza compromettere la stabilità dei driver. Come per l’inizializzazione, la sequenza di terminazione deve seguire l’ordine delle dipendenze. 23 La figura sopra, mostra i passi per ottenere una uscita di sistema sicura. Tuttavia, se il sistema è in overload durante la procedura di uscita, il task di terminazione non può essere schedulato. Una possibile soluzione è quella di dare alta priorità a questo task, o di creare un’applicazione che non provoca la saturazione della CPU. Se il task di esecuzione non viene eseguito, un timeout (default 3 secondi) forza l’uscita del sistema. 2.1.4) Applicazioni Un’applicazione è un insieme di tasks cooperanti che condividono uno spazio di indirizzamento comune. Le comunicazioni tra tasks avvengono per mezzo di buffer condivisi, i cui accessi devono essere regolati da un qualche meccanismo di sincronizzazione. 24 Attenzione! Il kernel generico non implementa nessun meccanismo di protezione della memoria. 2.1.5) Scheduling I moduli di scheduling della CPU implementano specifici algoritmi con l'ausilio di set di dati e funzioni specifiche. L'implementazione è indipendente dagli altri moduli presenti nel sistema; in questo modo è possibile definire un gran numero di moduli con differenti configurazioni. 25 I moduli di scheduling sono organizzati per livelli: Ad ogni livello corrisponde uno ed un solo modulo. I livelli corrispondo alla priorità dei task da schedulare; il livello 0 è quello a priorità più alta. Il model mapper si occupa di selezionare il modulo adatto per lo scheduling di un task, il quale sarà assegnato al livello corrispondente alle sue richieste di QoS. Come abbiamo già detto S.Ha.R.K. ha tra i suoi obiettivi principali quello di facilitare l’implementazione e il test di nuovi algoritmi di scheduling. Di seguito esponiamo alcune convenzioni da seguire per la creazione di moduli. Tali convenzioni possono essere utili per comprendere come scrivere nuovi moduli utilizzando lo stesso stile adottato dai moduli standard forniti con il kernel di S.Ha.R.K. Possono essere riassunte nei seguenti punti: - Ogni modulo è composto da due file, un file .h e uno .c . Il primo registrato nella directory include/modules mentre l’altro nella directory kernel/modules; - Ogni Funzione di Registrazione registra solo un descrittore di livello. In questo modo il livello a cui un modulo è registrato può essere trovato esaminando il file di inizializzazione; - I modelli di task usati dai moduli sono elencati nel file include/kernel/model.h; - I nomi delle funzioni interne sono di tipo static e si presentano nella forma nomemodulo_nomefunzione, dove nomemodulo è il nome del file .c ; 26 - Un modulo può esportare alcune funzioni per realizzare uno specifico comportamento; queste funzioni hanno un primo parametro denominato level, in modo da risalire alla struttura dati del modulo. Un’applicazione che dipende da una specifica configurazione di un modulo può utilizzare tali funzioni a patto che conosca il livello al quale il modulo è registrato; - I prototipi delle funzioni esportate da un modulo devono essere inclusi nel file .h; - I moduli non possono utilizzare variabili globali in quanto differenti istanze di un modulo possono essere registrate allo stesso tempo nel sistema. In genere scrivere un nuovo modulo di scheduling richiede la creazione di nuovi file. Per semplificare distribuzione e integrazione di nuovi moduli nel kernel generico, nessuna modifica deve essere apportata al kernel. Oltre a questo, devono poi essere rispettate alcune regole di senso comune: - Un nuovo modulo di scheduling deve consistere di un solo file .h e un solo file .c ; moduli composti da più file ne devono riportare una spiegazione nella loro documentazione; - Insieme al modulo di scheduling, il progettista deve creare: 1) un file di inizializzazione, simile a quelli presenti nella directory kernel/init, che spiega come il modulo debba essere inizializzato; 2) minimo un programma test che mostra le funzionalità del modulo; - Nuove definizioni di dati utilizzate dai moduli devono necessariamente essere inserite nel file .h del modulo stesso. Le nuove costanti devono essere diverse da quelle di default. Sul sito ufficiale di S.Ha.R.K. si può trovare un esempio di struttura base di un modulo di scheduling da poter utilizzare come linea guida per crearne di nuovi. 27 2.1.6) Gestione delle risorse condivise I moduli per la gestione delle risorse condivise sono implementati con la stessa logica di quelli per lo scheduling della CPU. In entrambi i casi le funzioni dei moduli debbono essere chiamate dopo aver disabilitato gli interrupt. Ogni decisione deve essere presa in pochi µsec! Non possiamo accettare latenze dovute ad interrupt. 2.2) Programmazione in SHARK Il sistema supporta per la compilazione sia la piattaforma Windows con il compilatore DJGPP sia la piattaforma Linux con il compilatore GCC; l’host Windows, per l’esecuzione delle applicazioni realizzate, necessita di un programma di estensione del DOS, mentre Linux utilizza il bootloader GRUB. La programmazione delle applicazioni viene fatta in linguaggio C; la struttura standard delle applicazioni prevede: - un file di inizializzazione; - un task di inizializzazione; - una funzione principale, main, per la creazione dei vari tasks; - una serie di tasks per effettuare le varie elaborazioni. 28 2.2.1) File di inizializzazione Il file di inizializzazione provvede ad impostare i livelli di gestione dei vari tasks presenti nel sistema. La struttura base del file di inizializzazione fornita dagli sviluppatori è la seguente: /* Inclusione degli header files */ #include “modules/edf.h” #include “modules/cbs.h” #include “modules/rr.h” #include “modules/dummy.h” #include “modules/sem.h” #include “drivers/keyb.h” /* Define del tick del sistema */ #define TICK 1000 TIME__kernel_register_levels__(void *arg) { /* Variabile di appoggio del parametro di boot */ struct multiboot_info *mb = (struct multiboot_info *)arg; /* Inizializzazione dei livelli di gestione dei tasks */ EDF_register_level (EDF_ENABLE_ALL); CBS_register_level (CBS_ENABLE_ALL, 0); RR_register_level (RRTICK, RR_MAIN_YES, mb); /* Inizializzazione del livello dummy di gestione dei tasks */ dummy_register_level( ); /* Inizializzazione dei moduli di gestione dei semafori */ SEM_register_module ( ); CABS_register_module ( ); /* Restituzione del valore del tick di sistema */ return TICK; } 2.2.2) Il task di inizializzazione Il task di inizializzazione effettua le impostazioni dei dispositivi hardware necessari all’esecuzione delle applicazioni, ovvero della tastiera, del file system e del meccanismo dei semafori per la gestione delle sezioni critiche; questo task provvede anche a chiamare la funzione principale main: 29 TASK__init__(void *arg) { /* Variabile di appoggio del parametro di boot */ struct multiboot_info *mb = (struct multiboot_info *)arg; /*Variabile di appoggio della configurazione della tastiera */ KEYB_PARMS Kparms = BASE_KEYB; /* Impostazione delle porte Hard Real Time */ HARTPORT_init ( ); /* Impostazione della tastiera */ Keyb_def_ctrlC (kparms, itaMap); KEYB_init (&kparms); /* Chiamata del main */ __call_main_ (mb); return (void *) 0; } 2.2.3) La funzione principale: main La funzione principale, come abbiamo visto, viene chiamata dal task di inizializzazione e provvede alla creazione dei vari tasks dell’applicazione real-time che provvederanno all’esecuzione delle varie elaborazioni; la funzione main è scritta con la sintassi ANSI C secondo il formato: int main (int argc, char **argv) e viene utilizzata per la creazione dei vari tasks. In genere non prevede un ciclo infinito per evitare che la CPU sia costantemente sotto carico e permettere quindi la gestione degli eventi temporali generali dei tasks. Al termine dell’esecuzione della funzione principale il sistema deve essere arrestato con la funzione: void sys_end (void) 30 2.2.4) I tasks I tasks di S.Ha.R.K. sono definiti usando le funzioni standard del linguaggio C che ritornano un tipo VOID* e che possono avere un argomento anch’esse di tipo VOID*, che viene passato al momento della creazione del task. La struttura di un generico task prevede una parte di inizializzazione delle variabili e delle strutture necessarie alla elaborazione; il corpo del task è quindi costituito da un ciclo in cui viene eseguito il codice di elaborazione finché non viene verificata la condizione di uscita, tramite le primitive TASK_ENDCYCLE(.) oppure TASK_SLEEP( ), che provoca la restituzione del valore di ritorno: void * nometask (void *arg) { /* Parte di inizializzazione */ … for(;;) { /* Codice di istanza */ … funzione_di_fine_task ( ); } return valore; } La creazione dei tasks avviene con la seguente funzione: PID task_create (char *name, TASK (*body) (..), TASK_MODEL *m, RES_MODEL *r) L’attivazione del task viene effettuata con la seguente funzione: int task_activate (PID pid) mentre la fine dei tasks viene realizzata con la funzione: int task_kill (PID pid) 31 Come abbiamo visto la funzione di creazione dei task, oltre al nome e alla funzione che costituisce il corpo dello stesso, prevede anche un parametro relativo al modello; questo parametro racchiude informazioni inerenti la tipologia del task quali il periodo, la deadline e il worst case execution time (wcet). Il programmatore può passare come parametro uno di quelli predefiniti, oppure può crearne di nuovi. Un task può essere periodico o aperiodico; i primi sono attivati automaticamente dal kernel in un determinato momento mentre quelli aperiodici possono essere attivati da una esplicita chiamata di sistema o in occorrenza di un certo evento. I task possono inoltre avere diversi livelli di criticità, in particolare possiamo avere task: - HARD : Sono i task più critici; per questo motivo in fase di creazione sono soggetti ad algoritmi garantiti. Per questa categoria di task il sistema pone la sua attenzione sulla deadline. Se una hard deadline non viene rispettata il sistema rilascia un’eccezione che comporta la terminazione del programma. - SOFT : Sono schedulati in maniera tale da non compromettere i task di tipo hard e sono caratterizzati dal fatto che le deadline posso anche non essere rispettate. - NRT : La sigla sta per Non Real Time. Sono task che lavorano in background in accordo con le loro priorità prefissate e sono solitamente utilizzati a fini di monitoraggio o correzione di errori (debugging). 32 Ogni task può poi attraversare diversi stadi: - EXE : In un qualsiasi momento nel sistema è presente un solo task in questo stato ed è proprio quello attualmente in esecuzione. - READY : Include tutti i task attivati e pronti per essere eseguiti. - SLEEP : Include tutti i task aperiodici che hanno terminato un lavoro e attendono la prossima attivazione. Ogni task creato ma non attivato si trova in questo stato. - IDLE : E’ come lo stato precedente ma è relativo ai task periodici - BLOCKED : Include tutti i task bloccati da un semaforo. 2.3) Modelli Uno dei punti di forza di S.Ha.R.K. è quello di permettere agli utenti di implementare e testare facilmente nuovi algoritmi di scheduling. In particolare, il kernel è stato ideato con i seguenti obiettivi: - ottenere indipendenza tra i meccanismi del kernel e le politiche di scheduling per la gestione di task e risorse; - configurare il sistema in run-time specificando gli algoritmi da utilizzare per lo scheduling dei task e l’accesso alle risorse; - ottenere indipendenza tra applicazioni e algoritmi di scheduling. Queste caratteristiche sono particolarmente utili per comparare le performance di simili algoritmi sulla stessa applicazione. Infatti, l’indipendenza dei moduli permette all’utente di configurare e testare applicazioni senza ricompilare. L’indipendenza tra applicazioni e algoritmi di scheduling è ottenuta con l’introduzione del concetto di MODELLO. Ogni task richiede al sistema di essere schedulati in accordo con una data Qualità di Servizio (QoS) specificata dal modello. In altre parole, un modello è l’entità utilizzata da S.Ha.R.K. per separare i parametri di scheduling dai parametri di QoS richiesti da ciascun task. 33 In questa maniera, il kernel fornisce un’interfaccia comune per isolare la richiesta di QoS del task dalla reale implementazione dello scheduler. Il sistema operativo S.Ha.R.K. considera tre differenti tipi di modelli: - task models - resource models - mutex models 2.3.1) Modelli di task I modelli per i task sono definiti nel file include/kernel/model.h. La struttura generale di un modello di task è la seguente: typedef struct { WORD pclass; LEVEL level; size_t stacksize; void *stackaddr; WORD group; void *arg; DWORD control; } TASK_MODEL; I parametri più importanti sono: - STACKSIZE: Consente di specificare la dimensione (in bytes) dello stack richiesta dal task. - GROUP: Specifica a quale gruppo il task appartiene. Il kernel generico raggruppa i task in base a questo campo e fornisce primitive quali GROUP_ACTIVATE oppure GROUP_KILL. - CONTROL: Consente di specificare particolari proprietà per il task. Ad esempio, si può creare un task che non può essere prelazionato, oppure bloccato in attesa che si verifichi una particolare condizione. 34 2.3.2) Modelli di risorse I modelli per le risorse sono definiti nel file include/kernel/model.h Sono concepiti in maniera simile ai modelli per i task. Ognuno di essi varia in base alla risorsa che deve gestire, dunque è molto difficile evidenziare campi comuni a più modelli. Per questa ragione la struttura in linguaggio C che rappresenta la classe base di un modello di risorsa contiene solo campi che forniscono informazioni sul reale tipo dello stesso. S.Ha.R.K definisce due tipi di modelli di risorse, utilizzati nell'implementazione di PCP (Priority Ceiling Protocol), SRP (Stack Resource Policy) e PIP (Priority Inheritance Protocol): - Nel caso di PCP viene aggiunta solo la priorità statica per la creazione del task; - Nel caso di SRP viene invece aggiunta la descrizione del livello di preemption dello stesso; - Nel caso di PIP può essere facilmente applicato agli scheduling a priorità prefissata per porre un limite ai tempi di blocco. 2.3.3) Modelli mutex Quando un semaforo mutex viene creato, questi modelli vengono utilizzati per specificare quale protocollo per l’acceso alle risorse si deve utilizzare. Per implementare l’inizializzazione di un mutex in maniera modulare, ricaviamo una struttura chiamata mutexattr_t da una struttura base simile a quella utilizzata per i modelli di risorse. Per la gestione di questi modelli si utilizza un set di attributi che sono contenuti nel file include/kernel/model.h. 35 2.3.4) Funzionamento del model mapper Il dispositivo che si occupa dell’assegnazione dei vari task ai moduli opportuni prende il nome di Model Mapper ed il suo funzionamento è schematizzato nella figura sotto: Quando un task è creato la richiesta di QoS è specificata da un gruppo di parametri. Task differenti possono avere differenti parametri ma ce ne sono alcuni che ciascuno deve necessariamente avere, come la dimensione dello stack. Non tutti devono avere parametri riguardanti la loro deadline ma alcuni si. Perciò i parametri vengono suddivisi in due parti. La prima parte, detta parte generale, che contiene tutti i parametri che tutti i task hanno in comune e la seconda, detta parte dipendente dal modello, contenente invece i parametri specifici per quel task. Il kernel di S.Ha.R.K. non sa come interpretare i modelli, semplicemente li indirizza verso il model mapper. Questo dispositivo passa i modelli ai relativi moduli in accordo con un proprio ordine interno. Il modulo poi controlla se può processare il modello leggendo la parte comune dello stesso e se può converte i parametri QoS negli appropriati parametri di scheduling. Per far ciò il modulo utilizza uno speciale componente chiamato QoS Mapper. Tuttavia se il modulo non può occuparsi del modello lo invia nuovamente al model mapper che sceglierà un nuovo modulo e vi spedirà il modello. Tale procedura si ripeterà finché non trova il modulo appropriato. 36 I moduli di scheduling sono moduli esterni che il kernel di S.Ha.R.K. usa per schedulare i processi. Come riportato sulla guida di S.Ha.R.K. 1.5 : “… i moduli esterni possono implementare algoritmi periodici di scheduling, gestione di soft task attraverso server real-time, protocolli di gestione dei semafori e politiche di gestione delle risorse…” Ogni modulo del sistema è indipendente da ciascun altro e si compone di un certo numero di funzioni e dati. Le funzioni di ciascun modulo possono essere suddivise in due categorie: - funzioni pubbliche - funzioni private Le funzioni pubbliche sono quelle funzioni che possono essere chiamate direttamente dal kernel generico per determinare quali sono le primitive da utilizzare. Alcune funzioni pubbliche si occupano di un singolo processo mentre altre del modulo stesso. Le funzioni private invece possono essere chiamate solo da funzioni pubbliche o private di altri moduli e non dal kernel. Come riportato sulla guida di S.Ha.R.K. 1.5 tali funzioni possono: “… esportare un’interfaccia nella parte pubblica dello stesso o di un altro modulo…” Per esempio, un modulo EDF può esportare un’interfaccia in un server aperiodico che permetta allo stesso di posizionare i task nella coda dei processi pronti dell’EDF. 37 Capitolo 3 S.Ha.R.K. non è un sistema operativo stand-alone. Il kernel, una volta compilato, gira appoggiandosi ad un sistema operativo di tipo DOS. Linux è un kernel monolitico e modulare, che ha lo scopo di gestire il funzionamento di macchine più o meno complesse; S.Ha.R.K., invece, è un microkernel funzionale solo alla compilazione e all'esecuzione di determinate applicazioni. In pratica si limita a fornire le librerie necessarie alla compilazione. I motivi di questa scelta sono due: - Nessun costo di licenza - Possibilità di modificare il codice del sistema operativo ospite. È possibile provare S.Ha.R.K. in tre modi: - Installandolo su disco fisso; - Installandolo in una macchina virtuale; - Utilizzando il live-cd. Quale metodologia scegliere? Dipende da cosa dobbiamo fare! Possiamo dedicare a S.Ha.R.K. una partizione del nostro hard disk se abbiamo intenzione di utilizzarlo in maniera intensiva, se vogliamo solo provare il funzionamento di una piccola applicazione può essere conveniente utilizzare una macchina virtuale mentre se vogliamo dare solo un'occhiata alle applicazioni dimostrative, utilizzeremo il live-cd. Il presente capitolo riguarda l’installazione del sistema operativo S.Ha.R.K. tramite le risorse presenti sul sito web http://shark.sssup.it 38 Come vedremo S.Ha.R.K. può essere utilizzato sotto Linux, WindowsTM o una versione di xDOS (preferibilmente FreeDos). 3.1) Piattaforme supportate Le applicazioni di S.Ha.R.K. così come il kernel, i modelli e i moduli possono essere sviluppati sotto le tre piattaforme principali elencate precedentemente. La seguente tabella mostra alcune caratteristiche dell’utilizzo di S.Ha.R.K. appunto sotto i tali sistemi: DOS Linux Windows No Yes Yes It dipends Yes Yes Slow Fast Fast Native compiler No Yes No Programmino/debug tools No Yes No TSRs Native Native Yes No No Graphical interface Good editors Compilation speed Dealing with long filenames Native execution Linux e Windows sono abbastanza simili in termini di facilità di programmazione. Mentre con l’uso di FreeDos il principale vantaggio sta nel fatto che non richiede di riavviare la macchina per eseguire le applicazioni, cosa necessaria invece per lo sviluppo sulle altre due piattaforme. 3.2) Installazione L’installazione cambia sensibilmente a seconda della piattaforma che si ha deciso di utilizzare e di seguito analizzeremo quindi passo dopo passo: - Installazione su Linux - Installazione su FreeDos - Installazione su Windows Millenium/NT/2000/XP 39 3.2.1) Linux 1) Scaricare il file shark-1.5.tar.bz2 da sito di S.Ha.R.K. 2) Digitare il comando tar xvjf shark-1.5.tar.bz2 3) Entrare nella directory del sistema tramite il comando cd shark 4) Editare il file di configurazione shark.gfg (questo passo è necessario per impostare le opzioni del compilatore e ottimizzare il kernel per una maggiore velocità e precisione) 5) Compilare il kernel, le librerie, i moduli e i driver dei dispositivi digitando make all dalla directory attuale (shark) 6) Entrare nella directory delle demo con il comando cd demos 7) Compilare i programmi dimostrativi lanciando il comando make dalla directory attuale 3.2.2) FreeDos 1) Scaricare i file unzip32.exe, mindj333.zip e shark15.zip dal sito di S.Ha.R.K. 2) Scompattare il compilatore digitando unzip32 –o mindj333.zip –d c: Se già si dispone l’applicativo PkZip si può utilizzare il comando pkunzip –o –d mindj333.zip c: 3) Entrare nella directory del compilatore digitando cd c:\djgpp 40 4) Installare il compilatore lanciando il comando install.bat 5) Settare le variabili di sistema con il comando setvar.bat; in questa maniera il compilatore è installato e pronto per compilare 6) Scompattare il file del sistema digitando unzip32 –o shark15.zip –d c: Come visto sopra se si dispone di PkZip si può utilizzare questo 7) Entrare nella directory del sistema con il comando cd c:\shark 8) Editare il file di configurazione shark.gfg cambiando il nome del compilatore di default 9) Compilare il kernel digitando dalla directory corrente il comando make 10) Entrare nella cartella delle demo con il comando cd demos 11) Compilare i programmi dimostrativi lanciando il comando make 12) Entrare nella directory di una delle demo che ci interessano e lanciamo il programma con il comando x <nome demo> In DOS si consiglia, anche se non è necessario, di caricare l’utility smartdrive (smartdrv 16000/x) per velocizzare l’accesso ai dischi durante i passi precedenti. Questa utility è però valida per MS-DOSTM mentre per FreeDos si può utilizzare lbacache. 41 3.2.3) Windows Millenium/NT/2000/XP 1) Scaricare i file unzip32.exe, mindj333.zip e shark15.zip dal sito di S.Ha.R.K. 2) Entrare nell’emulatore di DOS di Windows (prompt dei comandi) 3) Seguire le istruzioni precedentemente viste per l’installazione su DOS Si ricordi che non è possibile eseguire un’applicazione di S.Ha.R.K. tramite l’extender E.EXE se si sta utilizzando una emulazione di DOS! Quindi per testare una demo è necessario riavviare il sistema con il disco di boot di FreeDos. 3.3) Come partizionare il disco fisso Verrà spiegato come partizionare e formattare un hard disk in maniera adatta all’installazione di un ambiente multi-boot con la possibilità di scegliere tra 3 sistemi operativi: FreeDos e S.Ha.R.K., Linux e WindowsTM. I 5 passi fondamentali per mettere a punto l’installazione sono: - Partizionare l’hard disk - Installare WindowsTM - Installare FreeDos - Installare Linux - Impostare il boot loader 42 Per fare ciò necessitiamo di: - Linux fdisk utility - Una copia del CD di installazione di Linux - Una copia del CD di installazione di WindowsTM - Alcune FreeDos utilities - Una copia di S.Ha.R.K. Per compiere tutte le operazioni relative alla partizione dell’hard disk si suggerisce di utilizzare una distribuzione LiveCD di Linux, in particolare Knoppix, scaricabile da http://www.knoppix.org Il Linux LiveCD permette di utilizzare tutte le potenzialità dei tools di linux senza la necessità di installare niente sull’hard disk. Riavvia il PC dal cd dopo aver inserito il LiveCD di Knoppix e apri la shell. Devi essere il root user per avere il permesso per il passo successivo, quindi digita su lasciando vuota la password nella console. Poi lancia l’fdisk di Linux; digitando fdisk/dev/hdx Dove x identifica il tuo drive. Di solito si utilizza il comando fdisk/dev/hda 43 Per utilizzare i comandi di fdisk utilizzare la guida del programma: Serviranno sia i comandi per creare partizioni sia quelli per cambiare l’ID della partizione e scrivere la tabella di partizione nel disco. Si suggerisce di partizionare l’hard disk come segue: Partizione primaria Altra partizione Numero Dati allocati Tipo 1st FreeDos FAT32 2nd Windows FAT32/NTFS 3rd Linux extX/Reiser/… 4th Dati Qualsiasi … … … n Dati Qualsiasi Importante è che la partizione di FreeDos deve essere la prima partizione nell’hard disk altrimenti si potrebbero avere alcuni problemi con l’avvio di FreeDos. Si suggerisce di lasciare le altre partizioni come di tipo Linux in maniera tale che WindowsTM metterà tutti i file di boot nella sola partizione che può vedere in fase di installazione. Ora riavviare e installare WindowsTM nella sola partizione disponibile per quest’ultimo (N.B. le solo versioni di Windows testate sono state 2000 e XP). 44 Dopo l’installazione di WindowsTM, caricare di nuovo Knoppix e utilizzare l’fdisk per cambiare il tipo di partizione: cambiarla in FAT32 o FAT16. Formattare la prima partizione utilizzando il programma di formattazione di Windows e installare FreeDos. Si può scaricare l’intero pacchetto da: http://www.freedos.org Si suggerisce di scaricare il pacchetto e copiarlo manualmente nella partizione. Si può in alternativa utilizzare un CDboot di S.Ha.R.K. per copiare l’intero albero di FreeDos nel disco. Copiare un floppy boot di FreeDos utilizzando il comando sys eseguendo un comando del tipo: sys a: Copiare anche il programma sys.com nel floppy. Ora riavviare la macchina lasciando il floppy inserito e lanciare il comando sys x: x:\fdosboot.img Dove x: si riferisce alla lettera del drive della partizione nella quale FreeDos è stato installato. Ciò che hai fatto è stato creare un immagine del settore che può caricare FreeDos, ma invece di sovrascrivere il Master Boot Record (MBR) la scrivi nel disco. Poi inserire invece il disco di installazione di Linux. Riavviare il PC e installare il sistema Linux. Dopo l’installazione il boot manager GRUB dovrebbe essere installato nel tuo MBR. 45 Ora guarda il file di configurazione del GRUB (grub.conf) che è di solito posto nella directory /boot/grub. Le linee per il caricamento di Linux sono di solito aggiunte automaticamente dall’installer. La linea per il caricamento di WindowsTM è invece qualcosa del tipo: title Win2000 rootnoverify (hd0,1) chainloader+1 Di seguito è necessario aggiungere anche la linea di comando per caricare FreeDos title FreeDos-Shark rootnoverify (hd0,0) makeactive chainloader /fdosboot.img boot Ecco come dovrebbe presentarsi il file di configurazione del GRUB: default = 0 timeout = 10 splashimage = (hd0,2)/boot/grub/splash.xpm.gz title Fedora Core (2.6.8-1.521) root (hd0,2) kernel /boot/vmlinuz-2.6.8-1.521 ro root=LABEL=/ rhgb quiet initrd /boot/initrd-2.6.8-1.521.img title FreeDos rootnoverify (hd0,0) makeactive chainloader /fdosboot.img boot title Win2000 rootnoverify (hd,0,1) chainloader +1 Ora sei pronto per installare la distribuzione di S.Ha.R.K. seguendo le istruzioni viste nel paragrafo precedente (P. 2.2). 46 Capitolo 4 Nel seguente capitolo esamineremo come è possibile testare il sistema operativo senza installarlo fisicamente sulla nostra macchina. Gli applicativi necessari al nostro lavoro sono a licenza GPL (Gnu Public Licence) e liberamente scaricabili dal web. Li riassumiamo nell'elenco che segue. - VirtualBox, un software per la virtualizzazione di sistemi operativi realizzato dalla Innotek e reperibile sul sito http://www.virtualbox.org; - FreeDOS, implementazione libera del sistema operativo DOS. Le immagini necessarie all'installazione sono reperibili sul sito http://www.freedos.org; - Djgpp, porting del noto compilatore GCC (Gnu C Compiler) in ambiente DOS. Si può scaricare nella sezione download del sito di S.Ha.R.K, come archivio compresso denominato mindj333.zip; - Il codice sorgente di S.Ha.R.K v1.5.4, reperibile sul sito http://shark.sssup.it, anch'esso come archivio compresso. I pacchetti con il codice sorgente di S.Ha.R.K. e del compilatore possono essere posizionati, per comodità, nella directory principale C:\ . 4.1) Installazione di FreeDos Prima di procedere con l'installazione di S.Ha.R.K. è necessario installare FreeDOS. In questa sede analizzeremo appunto l’installazione di tale sistema operativo su una macchina virtuale create per mezzo del programma di virtualizzazione VirtualBox. Analizzeremo ora, passo dopo passo, tutte le operazioni da compiere per la creazione di una macchina virtuale e l’installazione di un sistema operativo in ambiente virtuale. 47 1. Il primo passo consiste nel creare una macchina virtuale. VirtualBox fornisce un comodo wizard che guida l'utente attraverso l'intero processo di creazione; il wizard si avvia cliccando sul pulsante “Nuova” nella schermata principale. 2. Prima di tutto è necessario inserire un nome da assegnare alla nostra macchina virtuale e il tipo di sistema operativo che si intende utilizzare. 3. In seguito si richiede di scegliere la quantità di memoria RAM da allocare alla macchina virtuale nel momento in cui la si manderà in esecuzione. FreeDOS può funzionare egregiamente con soli 640 KB di memoria, per cui pochi MB sono più che sufficienti per i nostri scopi. 48 4. Ora il wizard ci consiglia di creare un HD virtuale per il nostro sistema operativo. Cliccando su “Nuovo” possiamo scegliere le caratteristiche per il nostro HD: - Tipo di immagine: possiamo scegliere di assegnare una dimensione fissa al nostro HD virtuale (opzione “a dimensione fissa”), oppure di farlo espandere a seconda delle richieste (opzione “ad espansione dinamica”). La seconda opzione può essere utile a risparmiare qualche MB di memoria su disco. - Se si sceglie l'opzione “ad espansione dinamica”, il wizard chiederà di limitare superiormente lo spazio destinato all'HD. Le nostre necessità sono molto limitate, per cui 512 MB dovrebbero essere più che sufficienti. 49 A questo punto abbiamo completato la creazione dell'HD virtuale sul quale andremo ad installare il nostro sistema operativo. Ora lanciamo la macchina virtuale che abbiamo appena creato tramite il pulsante AVVIA. Tale macchina, così come creata, non ha installato alcun sistema operativo; come ci si può aspettare, dunque, il programma aprirà una nuova finestra di dialogo che ha il compito di guidare l'utente nella selezione di un media sorgente per l'installazione del sistema operativo. Quindi dobbiamo selezionare dalla voce media sorgente il file immagine di FreeDOS, ovvero fdfullcd.iso, se abbiamo scaricato la versione completa di FreeDOS. All’avvio si presenterà la seguente schermata, che fornisce una serie di opzioni. 50 Successivamente si presenta un altro menu. Ancora una volta selezionare la prima voce (default) e premere invio per continuare l'installazione di FreeDOS. Segue una schermata da cui è possibile impostare la lingua e la tastiera di sistema. Scegliere “Italiano” e premere invio per continuare. Fatto ciò, confermiamo finché non viene richiesta la creazione di una partizione sul disco virtuale. 51 Dare il comando INVIO sulla voce New Partition e successivamente sulla voce Primary Partition. Si apriranno delle finestre di dialogo nelle quali verrà richiesto: - Impostare la dimensione della partizione che per default è impostata a 512MB; confermare la scelta. - Inizializzare l’area di partizione; confermare. - Inizializzare l’intera area di partizione; confermare. Creata la partizione premiamo F3 e poi yes per riavviare la macchina virtuale. Al riavvio è sufficiente ripetere le operazioni precedenti, fino a che non compare la richiesta di formattare l’HD. Confermare tramite il comando yes. 52 Successivamente scegliere l’opzione CONTINUE WITH FREEDOS INSTALLATION. Ora partirà l’installazione dei vari componenti di FreeDos. Si seguiteranno una serie di schermate a fondo blu dalle quali è possibile selezionare e deselezionare i componenti del sistema operativo. Per muoversi tra i pacchetti è sufficiente utilizzare il tastierino direzionale. Tramite la barra spaziatrice è possibile selezionare o deselezionare un pacchetto. Premendo il tasto invio si procede nell'installazione. Una volta terminata l’installazione dei vari componenti ha inizio una breve fase di auto-configurazione del sistema. Prima di tutto il sistema richiede di installare un PACKET DRIVER, necessario per la connessione alla rete. Se non si hanno necessità particolari, è opportuno scegliere NO, evitando svariate problematiche di configurazione di rete. Più avanti, probabilmente a causa di un bug nel processo di installazione, il sistema chiederà di rimuovere il pacchetto del noto editor VIM32. L'utente non deve fare altro che confermare. 53 Di seguito verrà richiesto di impostare: - MAIL SERVER - E-MAIL USER-ID Digitare INVIO per saltare questa procedura, se non si necessita di un mail server. Con questo termina la fase di configurazione finale, ed il sistema chiede di essere riavviato. Confermare premendo Y ed invio. Al riavvio del sistema si presenta un breve menu con quattro scelte. Scegliere l’opzione n°3 “LOAD FREEDOS INCLUDING HIMEM XMS-MEMORY DRIVER”. Ci dovremmo trovare di fronte ad una schermata nera con scritto C:\>… Buon segno! Vuol dire che tutto è a posto, e FreeDOS è installato e funzionante. Non ci resta che dare il comando HALT e la macchina virtuale dovrebbe spegnersi. 4.2) Installazione di S.Ha.R.K. Prima di tutto bisogna editare il file di configurazione del sistema operativo. Decomprimiamo il file sorgente del sistema in C: in maniera tale da poter avere accesso ai file contenuti al suo interno. 54 Dal prompt dei comandi entriamo nella directory SHARK e da qui digitiamo: edit shark.cfg Lo spartano editor di MS-DOS è sufficiente a modificare il file di configurazione di S.Ha.R.K.; una volta digitato il comando sopra si aprirà una finestra a fondo blu come quella sottostante. Da qui avremo accesso alle righe di comando che ci permetteranno di settare il sistema operativo in base alle nostre esigenze. 55 Ecco i punti che devono essere modificati: 1) COMPILER SELECTION OPTION: Nella voce COMPILER impostare DJGPP. La voce di default è GCC4, compilatore standard in ambito Linux/Unix. 2) KERNEL IMAGE START POINT: Questa voce indica il primo indirizzo di memoria disponibile per il kernel di Shark. Un valore troppo basso per questa opzione potrebbe non consentire l'avvio del sistema; d'altra parte un valore troppo alto porta ad uno spreco di memoria RAM. Nel seguito vedremo come impostare correttamente l’indirizzo di memoria per la voce MEMSTART. 3) ENABLE TSC READ TIMER OPTIMIZATION: Nella voce TIMER-OPT impostare il valore relativo al proprio processore seguendo la legenda indicata. ( nel mio caso 1,8GHz => TIMER-OPT=2000) 4) SELECT THE EVENTS TRACER: Impostare il valore NO Una volta apportate le modifiche specificate, il sistema è configurato correttamente quindi dal menù file salvare le impostazioni ed uscire. Ora non ci resta che provare a compilare. 56 Usciamo dal prompt dei comandi e andiamo a creare l’immagine del sistema operativo usando un qualsiasi programma di masterizzazione (nel mio caso ho utilizzato Nero 7). Per creare l’immagine oltre al file sorgente di S.Ha.R.K. (ottenuto compattando il file shark154.zip) includiamo anche il nostro compilatore mindj333.zip e salviamo il file con estensione .iso in una directory a nostro piacimento (per comodità ho utilizzato la stessa directory in cui ho salvato l'immagine .iso di FreeDos). Una volta creata l’immagine dobbiamo fare in modo che la macchina virtuale, in fase di avvio, carichi anche il CD-ROM virtuale dal quale andremo a installare il file immagine di S.Ha.R.K. . Dal menù del programma scegliamo impostazioni, spuntiamo la componente CD/DVD-ROM e da qui impostiamo alla voce File immagine ISO l’immagine che abbiamo appena creato. 57 Ora non ci resta che avviare la macchina virtuale e copiare dall’immagine del CD i files che ci servono, quindi il compilatore e la directory contenente il sorgente del sistema operativo. Le istruzioni sono le seguenti: 1. Entrare nell’unità cd tramite il comando D: 2. Copiare il file contenente il compilatore: copy mindj333.zip c: 3. Tornare in C: ed estrarre il compilatore digitando il comando: unzip –o mindj333.zip –d c: 4. Dalla cartella C:\ DJGPP copiare il file X.exe con il comando: copy x.exe c:\fdos\bin A questo punto possiamo tornare in C: e lanciare il comando X e il sistema risponderà con una serie di informazioni tra cui il KERNEL IMAGE START POINT. Tale valore dovrà essere riportato sul file di configurazione di S.Ha.R.K. , nella variabile MEM-START. 58 Arrestiamo la macchina e dal prompt dei comandi andiamo a riaprire il file shark.cfg e scriviamo il valore dell’indirizzo appena ottenuto nell’apposita voce. Salviamo il file ed usciamo. Ora dobbiamo ricreare l’immagine del sistema operativo e sostituirla a quella utilizzata nel lettore CD-ROM virtuale. Questa volta però non avremo più bisogno di allegare anche il compilatore… Come prima facciamo ripartire la macchina virtuale e selezioniamo l’opzione n°3. Ora bisogna copiare la directory di S.Ha.R.K. con tutto il suo contenuto su C e lo faremo entrando nell’unità D: e digitando il comando: xcopy d:\shark\ c:\shark /e Il sistema risponderà chiedendo di specificare il tipo di file. Digitare: directory Con questo comando viene copiata su C: l’intera directory. Poi bisogna installare e settare il compilatore quindi entreremo nell’opportuna directory C:\DJGPP e digiteremo i seguenti comandi: - install.bat per installare il compilatore - setvar.bat per impostare le variabili Ora non rimane che compilare. Dalla cartella C:\SHARK diamo il comando make; in questa maniera compiliamo il sistema. Dopodiché entriamo nella directory C:\SHARK\DEMOS e lanciamo lo stesso comando per compilare anche le demo. S.Ha.R.K. dovrebbe essere attivo e funzionante sulla nostra macchina virtuale e non ci resta che testarlo avviando una demo; osserveremo la demo delle palline rimbalzanti JUMPBALL. 59 4.3) Test di una demo: JUMPBALL Prima di tutto avviamo la macchina virtuale ed entriamo nella directory contenente la demo in questione. Per fare ciò digitiamo i seguenti comandi cd shark\demos\jumpball dir/w Nella directory sono presenti i quattro file init relativi ai quattro diversi tipi di scheduling implementati per testare la demo: - ECP - ERN - RRN - RRP A titolo di test proveremo solo l’ultimo di questi scheduling e cioè il Round Robin con Priority Inheritance abbreviato, RRP. Nelle computazioni real-time, il Priority Inheritance è un metodo per eliminare i problemi di inversione di priorità. Utilizzando questo metodo di programmazione, l’algoritmo di scheduling aumenterà la priorità di un task alla massima priorità rispetto a tutti quelli in attesa. 60 L’idea di fondo di questo protocollo è quella che se un task durante l’utilizzo di una risorsa blocca altri task ad alta priorità, questo può ignorare la sua priorità assegnata e assumerne una anch’esso alta in maniera tale da continuare l’esecuzione della sua sezione critica senza essere prelazionato. Una volta terminata la sezione critica, il processo ritorna alla priorità originale. Dalla linea di comando avviare la demo tramite l’extender x: x rrp La demo parte e ci troviamo di fronte alla seguente schermata: Inizialmente viene creata un hard-task EDF garantito (palla bianca), poi utilizzando la barra spaziatrice si creano nuovi task questa volta soft che verranno schedulati in maniera differente a seconda dell’algoritmo di scheduling scelto in partenza. 61 Come si può notare in figura sotto con la creazione di nuovi task la CPU viene saturata quasi al 100%. Continuando ad inserire nuove palline il sistema dovrebbe andare in overload. Notiamo inoltre come inserendo nuovi task il task DUMMY (evidenziato dalla freccia rossa) diminuisca il proprio utilizzo di CPU. Il task dummy è un task come tutti gli altri che semplicemente crea un loop infinito; non fa niente al suo interno. Viene registrato tramite la chiamata al modello DUMMY_TASK_MODEL; tale modello viene utilizzato solo all’avvio del sistema e non può essere utilizzato da altri moduli. E’ ora importante comprendere perché necessitiamo di un modulo come questo. Diamo un’occhiata all’architettura del modulo di scheduling: quando il kernel necessita di schedulare un task, richiede a tutti i moduli registrati se ne hanno uno pronto. L’ipotesi è quella che ci deve essere sempre un task da schedulare. Il modulo di scheduling dummy è utilizzato per registrare un modulo che ha sempre un task pronto per essere schedulato (anche se quest’ultimo non fa niente), appunto il task dummy. 62 In conclusione possiamo affermare che pur lavorando in ambiente virtualizzato il sistema funziona correttamente. Per poter sfruttare al massimo le potenzialità di S.Ha.R.K. è comunque conveniente installarlo fisicamente sulla macchina in modo da eliminare l’affaticamento indotto dalla virtualizzazione sulla macchina host. 63