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