Università Politecnica
delle Marche
Facoltà di Ingegneria
Corso di Laurea in Ingegneria Informatica e dell’Automazione
Porting su architettura Cris AXIS ETRAX 100LX
del sistema operativo Xenomai
Tesi di Laurea di:
Lorenzo RUGGERI
Relatore:
Prof. Aldo Franco DRAGONI
Anno Accademico 2010/2011
Alla mia famiglia
Indice
Introduzione
vi
1 I sistemi operativi real-time e Linux
1.1
1.2
1.3
2
Caratteristiche generali di un RTOS . . . . . . . . . . . . . .
2
1.1.1
Task periodici/aperiodici e hard/soft real-time . . . .
2
1.1.2
Sistemi Hard e Soft real-time . . . . . . . . . . . . . .
4
1.1.3
Schedulazione dei sistemi real-time . . . . . . . . . . .
6
1.1.4
Caratteristiche di un sistema real-time
. . . . . . . .
10
. . . . . . . . . . . . . . . . . . . . . . . . . . .
12
1.2.1
Interruzioni hardware e software . . . . . . . . . . . .
12
1.2.2
Schedulazione a code multiple . . . . . . . . . . . . . .
15
1.2.3
Schedulazione CFS . . . . . . . . . . . . . . . . . . . .
16
1.2.4
Architettura modulare e licenza Open Source . . . . .
18
1.2.5
Il livello applicativo
. . . . . . . . . . . . . . . . . . .
19
1.2.6
Portabilità . . . . . . . . . . . . . . . . . . . . . . . . .
20
1.2.7
Compilazione, debugging e test . . . . . . . . . . . . .
20
1.2.8
I problemi della schedulazione real time sotto Linux .
21
1.2.9
Strumenti di supporto . . . . . . . . . . . . . . . . . .
22
Soluzioni real-time per il kernel Linux . . . . . . . . . . . . .
23
1.3.1
Linux e le applicazioni real-time . . . . . . . . . . . . .
23
1.3.2
Caratteristiche RT del Kernel Linux . . . . . . . . . .
24
1.3.3
Approccio RT basato sullo sviluppo del kernel Linux .
26
1.3.4
Approccio RT basato sull’utilizzo di un kernel RT di
GNU/Linux
basso livello
. . . . . . . . . . . . . . . . . . . . . . .
2 Xenomai
2.1
28
30
Struttura del progetto . . . . . . . . . . . . . . . . . . . . . .
iii
30
iv
INDICE
2.2
Adeos pipeline
. . . . . . . . . . . . . . . . . . . . . . . . . .
32
2.2.1
Adeos e Xenomai . . . . . . . . . . . . . . . . . . . . .
35
2.2.2
I dominii di Xenomai . . . . . . . . . . . . . . . . . . .
36
2.2.3
Intercettare le chiamate di sistema . . . . . . . . . . .
38
2.2.4
Propagazione degli interrupt . . . . . . . . . . . . . . .
39
2.3
Installazione di Xenomai su x86 . . . . . . . . . . . . . . . . .
40
2.4
Testing della skin nativa . . . . . . . . . . . . . . . . . . . . .
45
2.5
Sviluppo di script per il testing . . . . . . . . . . . . . . . . .
46
3 Cris AXIS ETRAX 100LX
3.1
3.2
La FOX Board . . . . . . . . . . . . . . . . . . . . . . . . . .
52
3.1.1
Panoramica . . . . . . . . . . . . . . . . . . . . . . . .
52
3.1.2
Accensione . . . . . . . . . . . . . . . . . . . . . . . .
54
3.1.3
Modalità di accesso alla FOX Board . . . . . . . . . .
55
3.1.3.1
Web access . . . . . . . . . . . . . . . . . . .
55
3.1.3.2
FTP access . . . . . . . . . . . . . . . . . . .
56
3.1.3.3
TELNET access . . . . . . . . . . . . . . . .
57
Cris ETRAX . . . . . . . . . . . . . . . . . . . . . . . . . . .
57
3.2.1
CPU RISC . . . . . . . . . . . . . . . . . . . . . . . .
58
3.2.1.1
Registri . . . . . . . . . . . . . . . . . . . . .
58
3.2.1.2
Flag e Condition Code . . . . . . . . . . . . .
59
3.2.1.3
Organizzazione dei dati in memoria . . . . .
59
3.2.1.4
Formato delle istruzioni . . . . . . . . . . . .
60
Gestione degli interrupt su Linux/CRIS . . . . . . . .
61
3.2.2.1
Interrupt Paths . . . . . . . . . . . . . . . . .
61
3.2.2.2
Casi speciali . . . . . . . . . . . . . . . . . .
63
Software Development Kit (SDK) . . . . . . . . . . . . . . . .
64
3.3.1
I sistemi embedded e la cross compilazione . . . . . . .
64
3.3.2
Installazione e test del cross compilatore gcc-cris . . .
66
3.3.3
Distribuzione software . . . . . . . . . . . . . . . . . .
69
3.3.4
Configurazione e compilazione del firmware . . . . . .
72
3.3.5
Scrittura del firmware sulla board . . . . . . . . . . . .
73
3.2.2
3.3
52
4 Porting
76
4.1
Approccio al problema del porting . . . . . . . . . . . . . . .
76
4.2
Adeos patch per architettura CRIS . . . . . . . . . . . . . . .
77
INDICE
v
5 Conclusioni
83
Ringraziamenti
85
Bibliografia
85
Introduzione
I sistemi operativi in tempo reale (comunemente abbreviati con RTOS) rivestono un ruolo fondamentale nella nostra società coprendo diversi settori
applicativi, quali il controllo di impianti industriali, i sistemi di regolazione di volo, il controllo del traffico aereo, navale e ferroviario, i sistemi di
difesa militari, le missioni spaziali, i sistemi di telecomunicazione, i sistemi
multimediali, l’automazione industriale e la robotica.
La loro presenza è fondamentale quando si vuole ottenere una risposta
dal sistema entro un tempo prefissato. Un sistema operativo real-time non
deve essere necessariamente veloce rispetto al tempo medio di risposta: tuttavia il tempo di reazione di tali sistemi è importante e proprio per questo
si impongono limiti massimi su di esso (deadlines), in modo tale che le applicazioni possano essere eseguite rispetto a vincoli temporali determinati a
priori.
Nonostante la vastità degli scenari coinvolti, la maggior parte dei sistemi
real-time viene ancora progettata con tecniche empiriche, senza l’ausilio di
una metodologia scientifica consolidata. La conseguenza di ciò è una scarsa
affidabilità del software che, in applicazioni critiche, può causare gravi danni
a cose o persone.
Negli ultimi anni, è cresciuto un notevole interesse nell’utilizzo del sistema
operativo Linux per scenari real-time, specialmente nei sistemi di controllo.
L’architettura semplice e ordinata fornita da Linux garantisce robustezza
e ottime prestazioni, mentre la licenza GPLv2 permette di modificare e di
cambiare il codice sorgente in relazione alle necessità dell’utente.
Tuttavia Linux è stato progettato per essere un sistema operativo general purpose; pertanto presenta alcune questioni che (come per esempio le
latenze impredicibili a priori, supporto limitato per la schedulazione a tempo reale del task-set, risoluzione temporale dei timer insufficiente) possono
vi
Introduzione
1
rappresentare un problema per applicazioni real-time.
Per questi motivi sono state proposte alcune modifiche con lo scopo di
aggiungere le indispensabili caratteristiche «real-time» al kernel Linux sia per
piattaforme hardware di utilizzo generico (x86), sia per soluzioni embedded
(come per esempio ARM) utilizzate per le applicazioni di controllo.
Il lavoro svolto in questa tesi, ha come obiettivo quello di analizzare il
funzionamento del sistema Xenomai, in particolare della patch Adeos, al fine
di effettuarne il porting sul sistema embedded a nostra disposizione, ovvero
una FOX Board ETRAX 100LX, la cui architettura CRIS non è supportata
direttamente da Xenomai, a differenza di altre architetture più utilizzate
(tipo ARM).
Nel Capitolo 1 di questa tesi vengono illustrati i concetti basilari che
caratterizzano un sistema operativo real-time, le tecniche più recenti per la
schedulazione in tempo reale e la schedulazione utilizzata dal kernel Linux.
Nel Capitolo 2 viene descritta, in dettaglio, l’architettura di Xenomai e
la sua struttura modulare, con una particolare attenzione al funzionamento
dello strato introdotto dalla patch Adeos e alla gestione degli interrupt. In
questo capitolo viene illustrata anche la procedura di installazione di Xenomai su architettura x86. Inoltre, vengono presentati alcuni test creati appositamente per verificare il funzionamento del sistema e prendere confidenza
con le interfacce di programmazione.
Nel Capitolo 3 viene descritta dettagliatamente la piattaforma embedded
utilizzata per il porting, insieme all’ambiente di sviluppo fornito dalla Axis.
Per una maggiore dimestichezza con il sistema embedded, viene esposta la
procedura per la cross compilazione del kernel Linux 2.6.26 e successivamente
la procedura per il porting di quest’ultimo sulla piattaforma in uso.
Nel Capitolo 4 viene affrontato il problema del porting del sistema Xenomai per l’architettura a nostra disposizione, basato sulla patch Adeos per
ARM.
Per finire, nel Capitolo 5 vengono illustrate le conclusione di questo lavoro
di tesi.
Capitolo 1
I sistemi operativi real-time e
Linux
1.1
Caratteristiche generali di un RTOS
Un sistema operativo real-time o in tempo reale è 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 dati nelle telecomunicazioni) o, comunque, dove sia necessario ottenere una risposta dal sistema entro un tempo
prefissato.
Un sistema operativo real-time non deve essere necessariamente veloce:
non è importante l’intervallo di tempo in cui il sistema operativo/applicativo deve reagire; l’importante è che risponda entro un tempo massimo predeterminato. In altre parole il sistema deve essere prevedibile. In pratica un
sistema real-time deve garantire che una elaborazione (o task) termini entro
un dato vincolo temporale o scadenza (detta in gergo deadline).
Per garantire questo, è richiesto che la schedulazione delle operazioni sia
fattibile. Il concetto di fattibilità di schedulazione è alla base della teoria dei
sistemi real-time ed è quello che ci permette di dire se un insieme di task sia
eseguibile o meno, in funzione dei vincoli temporali dati.
1.1.1
Task periodici/aperiodici e hard/soft real-time
I task di un sistema real-time possono essere:
2
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
3
• periodici: quando un task consiste in una sequenza di attività avviate
con cadenza regolare;
• aperiodici: quando un task consiste in una sequenza di attività avviate
a intervalli irregolari.
I task periodici sono propri di un sistema di controllo a tempo discreto.
Quando si ha a che fare con task di tipo periodico, si parla anche di periodo
di esecuzione con il quale si intende il lasso di tempo che intercorre tra due
attivazioni del task. Solitamente si fa coincidere la deadline con la fine del
periodo, poiché questo è il limite massimo di esecuzione di un task.
I task di un sistema real-time possono essere ulteriormente distinti in:
• soft real-time: un task che non rispetta la sua scadenza (in gergo si dice
sfondare la deadline) provoca un danno non irreparabile al sistema.
Il superamento della deadline produce un degrado delle prestazioni
proporzionale al tempo di superamento;
• hard real-time: un task che superi temporalmente la sua deadline
provoca un danno irreparabile al sistema.
Sostanzialmente questa distinzione si traduce nella diversa quantificazione dei costi di una possibile inesattezza temporale del sistema. Un esempio
di task 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; mentre un task hard realtime può essere il controllore della temperatura del nocciolo di una centrale
nucleare, dove il mancato rispetto dei vincoli temporali può provocare un
evidente disastro.
Oltre alla sua criticità (hard o soft), un task real-time Ji può essere
caratterizzato dai seguenti parametri, (vedi Fig. 1.1):
• Arrival time ai : (anche detto Release time ri ) è il tempo in cui il task
diventa pronto per l’esecuzione;
• Computation time Ci : è il tempo massimo necessario al processore per
eseguire completamnte il task senza interruzioni;
• Absolute Deadline di : è il tempo entro cui il task deve essere completato
per evitare danni al sistema (se hard) o un degrado delle prestazioni (se soft);
• Relative Deadline Di : è l’intervallo entro cui il task deve essere completato
a partire dal tempo di arrivo (Di = di − ri );
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
4
Figura 1.1: Parametri tipici di un processo real-time
• Start time si : è il tempo in cui viene eseguita la prima istruzione del task;
• Finishing time fi : è il tempo in cui il task termina la sua esecuzione. È
anche detto Completition time;
• Response time Ri : è la differenza tra il finishing time e il release time:Ri =
fi − ri ;
• Lateness Li : Li = fi − di rappresenta il ritardo di completamento del task
rispetto alla deadline. Si noti che se un task termina prima della sua deadline,
la sua lateness è negativa;
• Tardiness o Exceeding time Ei = max(0, Li ): rappresenta il tempo in cui
un processo è rimasto attivo oltre la propria deadline;
• Laxity o Slack time Xi : Xi = di − ai − Ci rappresenta il ritardo di attivazione massimo che un task attivo può subire in coda pronti per non eccedere
la sua deadline.
1.1.2
Sistemi Hard e Soft real-time
I sistemi real-time si dividono in due categorie:
• i sistemi «hard» sono quelli che possono garantire la fattibilità di
schedulazione di un insieme di task hard e soft real-time;
• i sistemi «soft» sono quelli che possono garantire la fattibilità di schedulazione di un insieme di soli task soft real-time.
Per chiarire meglio il concetto si pensi a un sistema real-time come a un
sistema che, dato un insieme di n task, ognuno con i propri vincoli temporali
(deadline del task i-esimo), è in grado di minimizzare la funzione di costo
definita come:
Ks =
n
X
Ki (t)
i
dove Ki (t) è la funzione costo del task i-esimo definita come:
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
5
Figura 1.2: Esempi di funzioni di costo per diverse tipologie di task

0
Ki (t) =
∞
se t ≤ di
se t > di
se il task i-esimo è di tipo hard real-time, o come:

0
Ki (t) =
f (t)
se t ≤ di
se t > di
se il task i-esimo è di tipo soft real-time, dove si è indicato con f (t) una
funzione monotona crescente all’aumentare del tempo che scorre.
Così come mostrato nella Fig. 1.2 i task di tipo hard real-time dovranno
essere schedulati in modo da terminare a un istante t0 minore della deadline,
in modo da far valere la funzione di costo 0 e non ∞; mentre i task soft
real-time dovranno anche loro far valere 0 la funzione di costo relativa ma,
in questo caso, uno sfondamento della deadline t0 > di non manderà il costo
globale del sistema all’infinito (equivalente al disastro).
In entrambi i casi il comportamento real time è ottenuto dividendo un
programma in processi, il cui comportamento è prevedibile e noto in anticipo.
Questi processi hanno normalmente una vita breve e possono essere eseguiti
nella loro completezza in un tempo inferiore al secondo.
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
6
Nel momento in cui viene rilevato un evento esterno, lo scheduler dei
processi ha il compito di determinare una opportuna sequenza di esecuzione
dei processi, tale da garantire il rispetto di tutte le scadenze.
Come descritto nella sezione precedente, gli eventi a cui un sistema real
time deve poter reagire sono classificati in task periodici e aperiodici. Nel
caso di eventi periodici, se esistono m task e se il task i-esimo arriva con
periodo Pi e richiede Ci secondi di tempo di CPU per essere gestito, il carico
può essere gestito solo se:
M
X
Ci
i=1
Pi
≤1
Un sistema real time che rispetta questo vincolo è detto schedulabile.
Per esempio, consideriamo un sistema soft real time con tre eventi periodici
rispettivamente di 100, 200 e 500 millisecondi. Se questi eventi richiedono 50,
30 e 100ms di tempo di CPU per la loro esecuzione, il sistema è schedulabile
in quanto: 50/100+30/200+100/500 = 0, 5 + 0, 15 + 0, 2 ≤ 1.
Se un quarto evento, con periodo di 1 secondo, si aggiunge al sistema,
l’insieme dei processi rimarrà schedulabile fino a quando a questo evento non
verranno richiesti più di 150ms di tempo di CPU per occorrenza. Notare
che, nel calcolo precedente, si suppone implicitamente che l’overhead per il
cambio di contesto sia così piccolo da poter essere trascurato.
I sistemi operativi general purpose non supportano la funzionalità hard
real-time, ma possono supportare quelle di tipo soft (per esempio Linux è un
sistema soft real-time).
1.1.3
Schedulazione dei sistemi real-time
Ciò che differenzia un sistema non real-time da un RTOS è principalmente
lo schedulatore. Lo schedulatore è un algoritmo che controlla l’accesso alla
CPU da parte dei vari processi del computer.
La schedulazione dei processi è fondamentale per garantire l’efficienza computazionale del computer, garantendo un utilizzo ottimizzato della
CPU tra processi che evolvono parallelamente, tenendo conto degli interrupt
prodotti dai controller delle varie unità I/O, dalle memorie e dagli hard-disk.
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
7
Figura 1.3: Task
Concetti generali
I possibili stati di un task sono:
• ready : pronto all’esecuzione, in attesa della CPU;
• pended : in attesa di una risorsa richiesta dal task per procedere
nell’esecuzione;
• delayed : in attesa della fine di un timeout pressato;
• suspended : lo stato iniziale di ogni task appena creato.
Tutti i task in stato ready sono pronti per essere eseguiti. L’accesso alla
CPU di un task rispetto a un altro, è funzione dell’algoritmo implementato
nello schedulatore.
Gli aspetti più importanti di uno schedulatore sono:
PRIORITÀ: la priorità è un indice che definisce l’importanza che un processo ha nei confronti degli altri. Si possono distinguere priorità interna (definita dal sistema in base a diversi requisiti tipo
memoria, I/O, tempo di esecuzione) ed esterna (definita dall’utente). In aggiunta si definisce una priorità dinamica (aging).
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
8
Se un processo ha una priorità bassa verrà sempre scavalcato da
processi a priorità più alta. Il rischio è che tale processo non
venga eseguito mai (starvation). Perciò quando un processo è
pronto per essere eseguito da troppo tempo, la sua priorità viene
aumentata dinamicamente dallo scheduler;
PREEMPTION: se nella coda esiste un processo pronto a essere eseguito,
con priorità maggiore di quello in esecuzione nella CPU, lo scheduler forza il rilascio della CPU e passa a eseguire il processo a
più alta priorità. Un esempio può essere l’I/O. Se un processo
ad alta priorità subisce un interrupt per la gestione di un I/O, lo
scheduler passa la CPU al processo successivo. Se lo scheduler
non fosse dotato di diritto di prelazione (preemption), il processo
ad alta priorità dovrebbe aspettare che il processo a bassa priorità finisca prima di completare il suo ciclo. Con la preemption,
invece, la CPU viene liberata dal processo a bassa priorità nell’istante in cui il processo ad alta priorità ritorna pronto a essere
eseguibile (cioè quando ha finito l’I/O).
Algoritmi di schedulazione real-time
Gli algoritmi di scheduling real time sono 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.
Successivamente verranno analizzate alcune politiche di scheduling realtime, 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 e la schedulazione del disco.
Schedulazione a frequenza monotona Il classico algoritmo statico di
schedulazione in tempo reale per processi periodici e prelazionabili è RMS
(Rate Monotonic Scheduling, schedulazione a frequenza monotona). È utilizzabile per processi che soddisfino le seguenti condizioni:
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
9
1. ogni processo periodico deve essere completato entro il suo periodo di
tempo;
2. nessun processo è dipendente dagli altri;
3. ogni processo necessita della stessa quantità di tempo di CPU per ogni
periodo di esecuzione;
4. i processi non periodici non hanno scadenze temporali;
5. la prelazione dei processi avviene istantaneamente e senza sovraccarico
di lavoro per il sistema.
Le prime quattro condizioni sono ragionevoli, mentre l’ultima rende più
semplice la modellazione del sistema. RMS assegna a ciascun processo una
priorità prefissata uguale alla frequenza con cui deve essere eseguito.
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
monotona.
Durante l’esecuzione, lo schedulatore esegue sempre il processo pronto
a più alta priorità, prelazionando, se necessario, il processo in esecuzione.
È stato dimostrato che RMS è ottimale rispetto alla classe di algoritmi di
schedulazione statici.
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 e di CPU.
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.
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
10
È interessante notare che, utilizzando priorità statiche, l’algoritmo RMS
funziona solo se l’utilizzo della CPU non è troppo elevato.
È possibile dimostrare per ogni sistema di m processi periodici che il
funzionamento di RMS è garantito (condizione sufficiente) se:
m
X
Ci
i=1
Pi
≤ m · (21/m − 1)
Per m che tende all’infinito, l’utilizzo massimo della CPU tende in modo
asintotico a ln2 = 0.69. Questo significa che per m = 3, RMS funziona
sempre se l’utilizzazione della CPU è uguale e o minore di 0.780.
Al contrario, EDF funziona sempre per qualunque insieme di processi
schedulabile e può raggiungere il 100% di utilizzo della CPU. Il prezzo di
tutto ciò è pagato in termini di una maggiore complessità dell’algoritmo
EDF rispetto a RMS.
1.1.4
Caratteristiche di un sistema real-time
Un sistema real-time dovrebbe possedere le seguenti caratteristiche:
• schedulazione ottima: tutti i task sono noti a priori così come i
vincoli temporali. Dovrebbe essere possibile avere uno schedulatore
che minimizzi la funzione di costo Ks presentata prima;
• condivisione delle risorse: i task sono entità separate che concorrono a uno stesso scopo: pertanto non è necessario avere spazi di
indirizzamento separati;
• garanzia di esecuzione: tutti i task di tipo hard real-time devono terminare entro le proprie deadline. Nel caso in cui arrivi un nuovo
task, o un task non possa completare entro la deadline, una notifica anticipata del sistema può essere utilizzata per impedire l’esecuzione del
nuovo task o per recuperare l’esecuzione del task che sta per sfondare;
• prevedibilità delle chiamate di sistema: il sistema deve essere
in grado di valutare i tempi di calcolo di ogni task per determinare la
schedulazione fattibile. Ogni chiamata di sistema deve avere un tempo
di esecuzione massimo ben definito in modo da non introdurre ritardi
indefiniti.
I prodotti delle famiglie Windows e Unix non soddisfano le caratteristiche
tipiche di un sistema real-time: per esempio, pur gestendo l’esecuzione di più
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
11
processi con pre-rilascio, 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, dispositivi
USB o altri dispositivi che introducono forti latenze di esecuzione da parte della CPU, rende impossibile stabilire con certezza quanto tempo sarà
necessario per reperire l’informazione utile alla corretta esecuzione del codice.
Anche il sistema operativo Windows CE1 viene erroneamente considerato
da alcuni un sistema operativo real-time, quando in effetti non esiste nessuna prerogativa architetturale a livello hardware e software che dia modo di
prestabilire il tempo di esecuzione di una serie di operazioni.
Esistono diversi fattori che causano la non prevedibilità nella risposta del
sistema operativo, i principali sono:
• il dma: può rubare il bus alla CPU ritardando l’esecuzione di un task
critico. In un sistema real-time si preferisce disattivarlo o usarlo in
modalità timeslice, dove si assegna in maniera costante e fissa il bus al
DMA, anche se non ci sono operazioni da fare;
• la cache: può causare non prevedibilità poiché esistono casi in cui
essa fallisce, causando ritardi nell’accesso alla memoria da parte della
CPU. Ipotizzando il peggiore dei casi si preferisce non usarla affatto;
• meccanismi di gestione della memoria: queste tecniche non devono introdurre ritardi non prevedibili durante l’esecuzione di task critici: la paginazione può causare dei page fault intollerabili per un sistema hard real-time. Tipicamente si usa la segmentazione o la partizione
statica della memoria;
• interrupt: sono generati da dispositivi periferici quando devono scambiare delle informazione con la CPU. Se queste interruzioni si verifcano
durante l’esecuzione di un task critico, generano ritardi non prevedibili
ed è quindi necessario trattarle in maniera opportuna;
• i sistemi di power management: sono meccanismi hardware che
possono rallentare la CPU o far eseguire a essa del codice utile a dissipare minor energia. Essi vengono disattivati in quanto è chiaro che, in
un sistema real-time, è importante non sfondare una deadline piuttosto
che consumare poca energia.
Tra gli RTOS commerciali troviamo il POSIX-conformant (per esempio
LynxOS che è Unix compatibile) e non POSIX-conformant come per esempio
VxWorks (che supporta in parte gli standard POSIX).
1
sistema operativo sviluppato da Microsoft per dispositivi portatili e sistemi embedded
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
12
Per quanto riguarda i sistemi Open Source è possibile l’uso di Linux,
con necessarie precauzioni, opportunatamente applicate in soluzioni come
Xenomai, RTAI, RT-Preempt e altre ancora.
1.2
GNU/Linux
In questa sezione si darà una panoramica generale sulle opportunità, i vantaggi e i limiti di utilizzo del sistema operativo GNU/Linux per i sistemi
embedded. Verranno considerati sia gli aspetti più strettamente tecnici, sia
quelli legati genericamente al modello di sviluppo software.
Si noti che parliamo qui di GNU/Linux (e non semplicemente di Linux) per sottolineare il fatto che trattiamo non solo del nucleo del sistema
operativo (Linux appunto) ma anche delle applicazioni nonché dei sistemi
di sviluppo: editor, sistemi di controllo della configurazione, compilatori,
debugger, programmi di test.
Buona parte di questi programmi sono il frutto diretto del progetto GNU
(www.gnu.org), mentre la totalità di essi (compreso ovviamente Linux stesso) è, o meglio può essere, a seconda delle scelte degli utenti, software libero,
distribuito secondo la licenza GNU GPL o altre licenze libere. Alcune riflessioni saranno dedicate ai vantaggi dell’uso di software libero nello sviluppo
di applicazioni embedded.
Non ci occuperemo, invece, specificamente di distribuzioni Linux embedded commerciali, ma molti dei concetti qui esposti si applicano ugualmente
anche a esse.
Il nucleo (kernel) Linux è funzionalmente equivalente a un kernel Unix,
e per questo motivo è molto diverso, nonché molto più complesso, della
maggior parte dei tradizionali sistemi operativi per applicazioni specifiche
(con esigenze di tempo reale o meno), nati per CPU con limitate risorse di
calcolo e sistemi con piccole quantità di memoria (sia RAM sia ROM).
Qui di seguito si riportano alcune delle caratteristiche più significative,
con particolare riferimento agli aspetti che di norma risultano meno familiari
per gli utenti provenienti da altre esperienze.
1.2.1
Interruzioni hardware e software
Le interruzioni («interrupt» o IRQ per «interrupt request») sono alla base
del funzionamento di un sistema multiprocesso.
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
13
Normalmente l’esecuzione del codice da parte del processore («CPU»,
«central processing unit») è sequenziale, poichè viene seguito il flusso logico
del programma in esecuzione senza distrazioni. Infatti, le prime macchine
adottavano esclusivamente questo modo di funzionamento.
Poco dopo, però, si è pensato di permettere l’interruzione del normale
flusso di istruzioni eseguito dal processore da parte di eventi esterni. Oggi
questo meccanismo è ampiamente usato e gli eventi che interrompono il processore sono normalmente associati a una periferica che richiede attenzione:
per esempio la pressione di un tasto, l’invio di un pacchetto dalla rete o lo
scattare del tempo sull’orologio di sistema.
Il meccanismo usato per riportare le interruzioni al processore può essere dei più vari. Nel caso più semplice si tratta di un unico filo collegato
con il mondo esterno, attraverso il quale un circuito dedicato, chiamato PIC
(«programmable interrupt controller»), comunica la sua richiesta di attenzione. La CPU interromperà il suo lavoro e interrogherà il PIC per sapere
quale periferica ha richiesto attenzione. In certi sistemi il processore viene raggiunto da vari segnali, corrispondenti a richieste di interruzioni con
priorità diversa e potrebbe non essere necessario un PIC esterno.
In altri casi molte periferiche risiedono fisicamente all’interno del processore e viene quindi realizzata un’architettura con più livelli di priorità con
un solo segnale di IRQ proveniente dall’esterno. A tale segnale può essere
associato o meno un controllore programmabile.
Altre variazioni sul tema sono le cosiddette trap (spiegate più avanti),
le interruzioni non mascherabili (NMI, «non maskable interrupt») e tutte le complicazioni introdotte nel mondo PC negli ultimi anni (APIC, IOAPIC, MSI, MSI-X). Queste ultime possono essere tranquillamente ignorate,
in quanto l’interfaccia offerta dal kernel verso i suoi moduli è indipendente
dalla gestione di basso livello implementata nel caso specifico.
Anche la gestione dei livelli di priorità, quando presente, può essere
ignorata dal codice dei driver. Questi possono usare il semplice modello
a due livelli in cui il driver può chiedere la disabilitazione temporanea delle
interruzioni per poi riabilitarle, oppure non toccare niente del tutto.
Le cosiddette trap (trappole), sono interruzioni generate dal processore
quando non riesce a eseguire un’istruzione macchina, perché si tratta di una
divisione per zero, ovvero l’indirizzo di memoria cui deve accedere non è
valido, oppure l’istruzione non è definita nel set di istruzioni della CPU.
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
14
In tutti questi casi l’esecuzione passa al sistema operativo con un meccanismo simile o identico (a seconda delle scelte dei progettisti hardware) a
quello utilizzato nella gestione di interruzioni esterne. Il sistema operativo
può analizzare la situazione e correggere il problema (per esempio recuperando una pagina di dati dallo spazio di swap) oppure «punire» il programma
che si è comportato male.
In un sistema Unix la punizione consiste nell’invio al processo di un
segnale; nei tre casi elencati si tratta: di SIGFPE (floating point exception),
SIGSEGV (segmentation violation) e SIGILL (illegal intruction). Il processo
può essere predisposto per intercettare questi segnali e cercare di recuperare
la situazione, altrimenti verrà terminato.
Anche le interruzioni software si avvalgono del meccanismo hardware
delle interruzioni (ancora una volta, un meccanismo simile o identico) al
fine di trasferire il controllo al sistema operativo. Nel set di istruzioni del
processore è in genere definita una istruzione INT (o SWI – software interrupt
– o equivalente) che trasferisce il controllo al sistema operativo proprio come
una trap o una interruzione esterna.
Il sistema operativo, analizzando lo stato del processore, estrae gli argomenti passati dal programma e provvede a eseguire la richiesta o a ritornare
un codice di errore. Per esempio, su piattaforma x86 le chiamate di sistema
per il kernel Linux sono implementate dall’interruzione numero 0x80. Il registro EAX contiene il numero della chiamata di sistema e, all’uscita, il valore
di ritorno; gli altri registri contengono gli argomenti della chiamata di sistema. Per i dettagli implementativi è interessante leggere <asm/unistd.h>
per la propria architettura.
Rispetto ai requisiti imposti dallo scenario real-time, una prima differenza rilevante riguarda il fatto che il nucleo non è compilato assieme all’applicazione, ma vive in maniera indipendente. Il meccanismo di accesso ai
servizi del nucleo è costituito dalle chiamate di sistema, implementate attraverso interruzioni software. Inoltre il codice del nucleo funziona sempre in
modalità «supervisore»: ha quindi accesso a tutte le risorse hardware della
macchina, gestisce direttamente interruzioni ed eccezioni e si occupa di tutte
le funzionalità di basso livello (vedi Fig. 1.4).
Al contrario, il codice dell’applicazione viene eseguito in modalità utente,
ed è quindi limitato per quanto riguarda l’accesso fisico al sistema, oltre a
non poter direttamente gestire interruzioni ed eccezioni. Dal punto di vista
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
15
Figura 1.4: Linux e la sua gestione interrupt hardware e software
della scalabilità Linux ha lo svantaggio di richiedere sempre un file system
per poter funzionare.
Si noti tuttavia che ciò non implica necessariamente che il sistema sia
dotato di un dispositivo di memorizzazione di massa. È infatti possibile
usare svariati supporti fisici per memorizzare un file system: RAM, ROM,
memoria FLASH, il file system di un’altra macchina con cui si è collegati via
rete, e così via.
1.2.2
Schedulazione a code multiple
La parte del nucleo del sistema operativo che decide istante per istante quale
sia il processo che debba avere il controllo della CPU prende il nome di
schedulatore. A ogni processo vengono assegnati due attributi fondamentali:
una priorità e una politica di schedulazione.
In Linux esistono due code di processo: i processi soft real-time (scheduler
RR o FCFS con priorità statica) e i processi utente (scheduler RR, non realtime con priorità dinamiche). La gestione avviene tramite tick (crediti). A
ogni processo sono assegnati un certo numero di crediti che equivalgono a
unità di tempo di utilizzo della CPU.
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
16
Figura 1.5: Modello dello scheduler Linux
Quando un processo diventa pronto o in attesa gli si sottrae il numero di
tick utilizzati.
Al momento del dispatch (rimozione di un processo dalla CPU e sostituzione con un altro) viene eseguito il processo con maggiori crediti. Quando
tutti i processi pronti hanno esaurito i crediti, questi vengono riassegnati.
∀ processoi
CREDIT Ii =
CREDIT Ii
+ P RIORIT A0
2
Pertanto gli obiettivi che Linux si propone, per quanto riguarda lo scheduling, sono un’equa distribuzione della CPU tra i diversi processi e l’evitare
che un solo processo possa impadronirsi di tutta la banda del processore per
evidenti ragioni di sicurezza.
Per ulteriori approfondimenti si rimanda alla pagina di manuale [1] relativa alla chiamata di sistema sched_setscheduler(2).
1.2.3
Schedulazione CFS
Dalla versione 2.6.23 in poi del kernel Linux, viene utilizzato l’algoritmo di
schedulazione CFS (Completely Fair Scheduler). Questo scheduler invece di
basarsi sul meccanismo delle code multiple, utilizza un albero RB [2] per la
gestione del task-set.
L’80% dell’implementazione dell’algoritmo CFS (Completely Fair Scheduler) può essere riassunta in una singola frase: il CFS fondamentalmente
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
17
modella una CPU ideale e accurata multi-tasking sull’hardware reale.
Una CPU multi-tasking «ideale» è una CPU che ha il 100% dell’occupazione di banda e che può eseguire ogni task alla stessa velocità, in parallelo,
ognuno alla velocità di
1
nrrunning
(dove nrrunning è il numero di task attivi).
Per esempio se ci sono due processi attivi, li esegue ognuno al 50% delle
proprie possibilità, completamente in parallelo.
Nell’hardware reale possiamo eseguire un solo processo alla volta: mentre
un task viene eseguito, gli altri, che rimangono in attesa della CPU, sono in
svantaggio, perchè il task corrente si appropria ingiustamente del tempo di
CPU.
Nel CFS, questo sbilanciamento è espresso e tracciato tramite un valore, associato a ogni task, p->wait_runtime espresso in nanosecondi, dove
wait_runtime è il tempo totale di CPU che il processo dovrebbe ancora
ottenere affinchè il sistema ritorni perfettamente «giusto» e bilanciato.
Su una piattaforma hardware «ideale», il valore p->wait_runtime dovrebbe essere sempre pari a zero; nessun task dovrebbe trovarsi «sbilanciato»
rispetto al tempo ideale di suddivisione del tempo di CPU totale
1
nrrunning .
La politica di scelta di attivazione del task sucessivo è basata proprio sul
valore p->wait_runtime ed è veramente semplice: il processo che ha il valore
più alto di p->wait_runtime sarà quello scelto. In altre parole, il CFS prova
ad attivare il task che ha bisogno urgente di altro tempo di CPU.
Quindi il CFS cerca di dividere, quanto più possibile, il tempo totale di
CPU tra tutti i processi attivi, come accadrebbe idealmente in un hardware
multitasking.
La parte restante dell’implementazione del CFS, che non rientra in questa
semplificazione, è relativa a poche cose aggiunte: come i livelli di priorità, la
gestione dei sistemi multiprocessore e alcune variazioni per il riconoscimento
dei processi non attivi (vedi Sez. 1.1.3).
In pratica funziona così: il sistema esegue il processo per un po’. Quando
questo si sospende (o viene eseguito lo scheduler) l’uso della CPU viene
accreditato al task: il tempo in cui ha usato la CPU fisica viene sotratto dal
tempo totale che gli spettava p->wait_runtime, corretto del tempo ulteriore
che comunque gli sarebbe spettato.
Appena il valore di p->wait_runtime diventa così basso che un altro
processo diventa «il prossimo task» tra quelli conservati nell’albero RN ordinato in base al tempo (viene introdotta anche una certa distanza temporale
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
18
in modo da non sovra-schedulare i task e non sporcare la cache); al processo
corrente viene sotratta la CPU e concessa al nuovo task.
Il valore di rq->fair_clock descrive il tempo di CPU in cui il processo
è stato in esecuzione rispetto al tempo che avrebbe dovuto ottenere dallo
scheduler. Perciò usando questo valore si può misurare il tempo di CPU che
un task si aspetta di ottenere.
Tutti i processi attivi sono ordinati nell’albero RN secondo la chiave
rq->fair_clock- p->wait_runtime; il CFS concede la CPU sempre al task
collocato nell’albero più a sinistra.
Con il procedere del sistema, i processi che diventano avviabili vengono
inseriti nell’albero da «destra» lentamente e, a mano a mano che viene effettuata la schedulazione, a ogni processo viene data la possibilità di spostarsi
sempre più a sinistra in modo tale che possa giungere alla CPU in un periodo
di tempo deterministico.
1.2.4
Architettura modulare e licenza Open Source
Una caratteristica del kernel Linux è quella di poter essere esteso mentre il
sistema funziona (mediante il meccanismo dei moduli). Questo consente il
supporto di piattaforme differenti dal punto di vista delle periferiche hardware, senza dover ricompilare il nucleo. I driver di periferica fanno di norma
parte del codice del kernel e sono accessibili come voci del file system.
Uno dei grossi vantaggi dell’uso di software libero nei sistemi embedded
è che lo sviluppo dei driver, e in generale di porzioni/moduli del kernel, è
ampiamente facilitato rispetto ai sistemi tradizionali, vista la disponibilità totale dei sorgenti del kernel e quindi di un numero notevole di esempi
funzionanti sul campo, nonché di un’ampia documentazione.
Nel caso di utilizzo di macchine con MMU (Memory Management Unit),
sempre più frequente in virtù dei costi via via più bassi del silicio, è possibile
sfruttare funzioni molto avanzate sia dal punto di vista della protezione della
memoria, e quindi dell’affidabilità del sistema, sia da quello delle funzionalità.
In particolare la disponibilità di chiamate come mmap(), che consente
di mappare i contenuti di un file o genericamente di un dispositivo su un
intervallo di indirizzi virtuali, rende il kernel Linux particolarmente attraente
anche se non è abilitato per la gestione degli interrupt.
Il modo Unix/Linux di creare nuovi processi risulta ostico ai programmatori embedded tradizionali.
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
19
Quello che Linux fa è «clonare» un processo per poi sostituirne l’immagine con quella del programma da eseguire, contrariamente a quanto accade
con i sistemi operativi basati su thread, in cui la creazione di un nuovo thread comporta una sola chiamata. Uno dei parametri necessari è l’indirizzo
del punto di ingresso del thread stesso. Si tenga tuttavia presente che anche
sotto Linux è possibile la programmazione a thread. Esistono infatti diverse
implementazioni dei cosiddetti «thread POSIX», definiti dalla norma POSIX
1003.1c.
Infine Linux dispone di una grande quantità di meccanismi di comunicazione interprocesso, eredità dei suoi predecessori: si va dai segnali, la forma
più arcaica, ad altre più sofisticate (pipe, fifo, semafori, code di messaggi,
memoria condivisa, socket). La varietà di possibili soluzioni impone all’utente una buona conoscenza di tutte le alternative, al fine di scegliere la più
adatta alle esigenze dell’applicazione.
1.2.5
Il livello applicativo
Per ciò che concerne la rete, il sistema GNU/Linux fornisce una grande
quantità di applicazioni già scritte e collaudate da tempo, ma l’aspetto più
interessante consiste nel cambio radicale di ottica rispetto all’approccio tradizionale. La novità consiste nel fatto che la filosofia Unix (da cui Linux
discende da un punto di vista tecnico) privilegia la modularità.
Di conseguenza, ciò che verrebbe visto da un programmatore embedded
tradizionale come un’unica applicazione da scrivere in C, può essere spezzato
in una serie di programmi tra loro interagenti, alcuni dei quali già esistenti
e altri da scrivere in linguaggi diversi a seconda dell’opportunità tecnica. È
inutile rimarcare tutti i vantaggi dell’approccio modulare.
E’ importante, invece, sottolineare che, con l’approccio presentato, la fase
di test di modulo risulta semplificata perchè il modulo stesso è già un programma eseguibile, rendendo quindi inutile la costosa scrittura di programmi
di test ad hoc.
Infine evidenziamo il fatto che l’uso di linguaggi di scripting (per esempio
script della shell, tcl e le sue varianti, perl) può risolvere un gran numero di
problemi i quali risultano ostici in C, come per esempio lavorare con stringhe
ed espressioni regolari.
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
1.2.6
20
Portabilità
Sebbene in origine sia nato come un sistema operativo esclusivamente per
piattaforma x86 (e in particolare PC), il kernel Linux è stato portato su un
grande numero di architetture differenti, e anche su macchine non dotate di
MMU.
A livello di applicazioni, sono disponibili distribuzioni Linux libere che
coprono diverse architetture. Per esempio la distribuzione Debian, pur non
specifica per l’embedded, dispone di pacchetti per differenti piattaforme comunemente impiegate su macchine a impiego specifico (x86, ARM, PowerPC,
SH e altre).
A livello di librerie C, esistono progetti liberi nati per dare origine a
librerie poco esigenti dal punto di vista delle risorse richieste: bisogna tenere
presente che Linux non è un sistema operativo utilizzabile per sistemi con
risorse limitatissime.
L’aspetto più delicato è costituito dalla quantità di memoria a disposizione: di norma si prende come limite inferiore quello dei 2MB di memoria
RAM + 2MB di memoria ROM (o FLASH), anche se il limite effettivo varia
a seconda della piattaforma.
La potenza di calcolo richiesta alla CPU dal kernel non rappresenta un
fattore particolarmente critico. Nonostante a volte venga considerato «scomodo» e/o «poco usabile», Linux rappresenta una piattaforma ideale anche
per ciò che concerne la macchina host, cioè quella in cui si sviluppa il codice
che girerà sul campo.
Questo è vero anche per quello che riguarda i compilatori, i sistemi di
controllo della configurazione, il test e il debugging.
1.2.7
Compilazione, debugging e test
L’insieme di binutils (assembler, linker e altri programmi di utilità) e di gcc
(GNU compiler collection, il compilatore GNU) costituisce la toolchain standard sotto Linux. Si tratta di strumenti di sviluppo di livello professionale, in
grado di produrre codice binario per un numero vastissimo di CPU e sistemi
operativi.
Il debugger standard sotto Linux fa parte del progetto GNU e si chiama
gdb. La versione «base» di gdb prevede l’uso da linea di comando, ma esistono anche versioni grafiche (come per esempio Insight e ddd). Inoltre sotto
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
21
Linux il debugging delle applicazioni può essere effettuato sia mediante il
debugger vero e proprio, sia attraverso strumenti di tracing come per esempio strace(1), che evidenzia la sequenza delle chiamate di sistema effettuate
dall’applicazione sotto esame.
Per ciò che consente il testing, soprattutto se si parla di applicazioni di
rete, la disponibilità di linguaggi di scripting e di programmi come netcat(1)
o tcpdump(1) rende possibile la scrittura rapida di programmi di test anche
relativamente complessi. Il debugging del kernel e dei suoi moduli è invece
più problematico.
Per scelta progettuale, infatti, il kernel non dispone (in modo nativo)
di un modulo per il debugging. Si ricorre quindi di norma ad altri metodi,
tutti sostanzialmente riconducibili ad attività di tracciamento. Esistono però
anche patch del kernel che consentono il debugging vero e proprio.
1.2.8
I problemi della schedulazione real time sotto Linux
Essendo un kernel multitasking e multiutente, Linux, come altri Unix, non è
stato scritto pensando al tempo reale «stretto», ossia ad applicazioni in cui
il mancato rispetto dei tempi di esecuzione specificati porti a un malfunzionamento grave.
La conseguenza di quanto si è detto in precedenza è il fatto che lo schedulatore Linux non ha il concetto di «deadline», ovvero il tempo entro il
quale una certa attività andrà svolta. Non è quindi adatto ad applicazioni in
tempo reale perchè non può conoscere i requisiti temporali dei vari processi
real-time.
Si potrebbe a questo punto pensare che, sostituendo lo schedulatore tradizionale con uno opportunamente riscritto, sia possibile aggirare il problema:
questo non è vero a causa di un secondo ordine di motivi.
Bisogna, infatti, chiedersi quando possa avvenire un cambio di contesto, ossia quando effettivamente il controllo della CPU possa passare da un
processo a un altro.
La risposta è che i cambi di contesto, sotto Linux, possono avvenire solo al
passaggio dallo spazio del kernel (kernel space) allo spazio delle applicazioni
(user space), ossia per esempio al ritorno da una chiamata di sistema oppure
al ritorno da un interrupt handler, quando l’interruzione avviene mentre la
CPU sta eseguendo codice dell’utente.
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
22
Motivo di ciò è l’esigenza di preservare l’integrità delle strutture dati interne del nucleo, integrità che verrebbe compromessa se il cambio di contesto
potesse avvenire in qualsiasi momento. A causa di questo fatto, non è possibile calcolare un limite superiore per il tempo che intercorre tra un cambio
di contesto e il successivo.
Prendiamo per esempio il caso in cui avvenga un grande numero di interruzioni annidate mentre la CPU si trova già all’interno del codice del kernel.
Se si verifica una condizione del genere, non si può prevedere il ritorno al
codice utente e quindi procedere a un eventuale cambio di contesto.
Se il controller IDE continua a interrompere senza che sia possibile uscire
dallo spazio del kernel, il processo utente di elaborazione (anche se a priorità
più alta possibile) non può riprendere il controllo perché lo scheduler non
può girare. Per maggiori informazioni si può consultare l’articolo «Linux e
real time (prima parte)» su [4].
1.2.9
Strumenti di supporto
Linux fornisce strumenti validi anche per ciò che concerne l’editing (per esempio emacs), il controllo della configurazione dei moduli (cvs), la stesura della
documentazione (texinfdeto, sgmtools), e in generale tutto ciò che riguarda
lo sviluppo di software.
Il modello di sviluppo da adottare quando si utilizza software libero,
deve essere diverso rispetto a quello che si impiega tradizionalmente. Salvo
infatti che non ci si affidi a una distribuzione commerciale o si stipuli un
contratto di consulenza, il sistema GNU/Linux è distribuito senza garanzia
di funzionamento.
Questo viene spesso considerato da molti utenti, o meglio dai loro manager, come un difetto. In realtà, non si riesce ad afferrare che il software
libero fornisce anche dei diritti e di conseguenza delle opportunità in più
rispetto al software proprietario: la disponibilità del codice sorgente per la
lettura, lo studio e le modifiche consente di correggere malfunzionamenti,
effettuare personalizzazioni, nonché imparare dal codice e dagli errori altrui
e in qualche caso contribuire attivamente allo sviluppo.
Infine è bene tener presente che sia il kernel sia gli altri pacchetti liberi di
un sistema GNU/Linux sono opera di comunità di sviluppatori, con i quali si
può stabilire un contatto per ricevere aiuto, scambiare esperienze e consigli.
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
23
Pertanto, qualora si volesse realizzare un sistema operativo real-time
basato su Linux, occorrerebbe tenere in considerazione queste problematiche.
Nel prossimo capitolo viene presentata la soluzione «ibrida» del sistema
Xenomai, il quale permette di ottenere un sistema operativo GNU/Linux di
tipo real-time.
Un recente studio sui sistemi operativi utilizzati nel mercato dei sistemi
embedded [3] ha dimostrato come la percentuale di utenti che ha scelto di
non utilizzare GNU/Linux Embedded, a causa di lacune nelle caratteristiche
di tipo real time, sia scesa dal 25% al 18% negli ultimi tre anni.
Da questi dati si può dedurre come il sistema operativo GNU/Linux,
sebbene nato non nell’ottica di un utilizzo specifico per i sistemi embedded,
stia sviluppando quelle capacità tali da renderlo adatto anche al mondo real
time, in cui la velocità di risposta, ma soprattutto il determinismo (sia degli
eventi, sia delle tempistiche), giocano un ruolo fondamentale.
1.3
1.3.1
Soluzioni real-time per il kernel Linux
Linux e le applicazioni real-time
Lo sviluppo e la diffusione del sistema operativo Linux e delle applicazioni
real-time, soprattutto in contesti embedded, ha portato alla ricerca di soluzioni congiunte in cui sistemi con caratteristiche funzionali di tipo real-time
fossero basati sul sistema operativo Linux.
L’utilizzo di software Open Source offre notevoli vantaggi in termini di
costi, flessibilità, sviluppo e supporto. I sistemi embedded ricoprono invece
un ruolo sempre più importante in settori come l’automazione, l’automotive,
le telecomunicazioni e il digital entertainment.
Come già anticipato Linux rappresenta una grande opportunità: esso
è un sistema operativo stabile, libero, in grado di fornire il supporto per
moltissime architetture hardware e sviluppato costantemente.
Esiste però anche un grande problema: Linux non è stato concepito per
essere un sistema operativo real-time. Come noto, Linux è nato ispirandosi
a Unix e il suo target principale sono sempre stati i server e recentemente i
computer desktop.
Linux è stato sempre utilizzato in ambito scientifico anche per sistemi
embedded, ma non è mai stato sviluppato con la finalità di essere un sistema
operativo veramente real-time. Recentemente si è cercato di trovare soluzioni
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
24
finalizzate per avvicinare il sistema operativo Linux al mondo real-time, in
modo da rendere questo connubio conveniente.
Consideriamo ora le motivazioni tecniche che hanno reso necessario uno
sviluppo mirato di Linux in direzione delle applicazioni real-time. La considerazione chiave da fare è la seguente: scelte progettuali che risultano ottime per sistemi operativi general-purpose sono spesso pessime per i sistemi
operativi real-time.
Un sistema operativo general-purpose non può operare correttamente
in ambito real-time, in quanto è stato sviluppato per raggiungere buone
prestazioni in contesti con diverse applicazioni in esecuzione contemporanea,
ma senza la possibilità di ottimizzare le prestazioni di alcuna di esse in
particolare.
Inoltre alcune delle principali caratteristiche dei sistemi operativi generalpurpose risultano essere, di fatto, delle limitazioni per le prestazioni in
ambito real-time.
Per i sistemi multiprocesso è molto importante minimizzare l’overhead
dovuto al context-switch: se però da una parte uno scheduler che opera
con una risoluzione temporale piuttosto bassa (ovvero effettua lo scheduling
non troppo frequentemente) riesce a limitare questo overhead, dall’altra esso
rende molto difficile, se non impossibile, mandare in esecuzione un processo
time-critical nei tempi corretti.
Consideriamo ora lo sviluppo di un sistema operativo dal punto di vista
opposto. Un modo per migliorare le prestazioni real-time è quello di aggiungere dei nuovi punti di preemption, dove il sistema operativo possa fermare
l’esecuzione di un processo e fornire le risorse a un processo critico.
Tuttavia questo approccio, aumentando l’overhead dovuto ai contextswitch, riduce le prestazioni generali di un sistema multiprocesso. Normalmente, invece, i sistemi operativi general-purpose vengono sviluppati cercando di ottimizzare le prestazioni medie, lasciando che il comportamento
worst-case non sia deterministico.
1.3.2
Caratteristiche RT del Kernel Linux
Il kernel Linux non è stato sviluppato con l’intenzione di ottenere un sistema
operativo strettamente real-time.
Tuttavia, visto il crescente interesse per l’utilizzo di Linux in applicazioni
embedded con stringenti vincoli temporali, recentemente si è in parte orien-
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
25
tato lo sviluppo del kernel Linux nella direzione dei sistemi real-time, tanto
che alcuni definiscono il kernel 2.6 (l’ultima versione stabile disponibile del
kernel Linux) come «Born to be embedded».
Il kernel 2.6 presenta infatti alcune caratteristiche fortemente orientate
al real-time, come il nuovo scheduler O(1) scritto da Ingo Molnar e alcune
patch per la gestione dell’interruzione dell’esecuzione dei processi. I dettagli
delle caratteristiche real-time per le diverse versioni di kernel Linux saranno
approfondite in seguito. L’interesse per l’utilizzo di Linux in sistemi realtime ha portato alla nascita di diversi progetti finalizzati a trovare soluzioni
in grado di rendere il sistema operativo Open Source adatto ad applicazioni
di tipo real-time.
Questi progetti, sviluppati inizialmente sul kernel 2.4, possono essere
suddivisi in due categorie in base all’approccio adottato nella soluzione del
problema:
1. sviluppo del kernel Linux per inserire funzionalità e caratteristiche
tipiche degli RTOS;
2. inserimento di un nuovo strato software costituito da un kernel strettamente real-time; tale strato viene posizionato tra l’hardware e il kernel
Linux. Quest’ultimo sarà visto dal kernel real-time come un semplice
processo.
Il primo approccio (mostrato nella Fig. 1.6), cerca quindi di rendere il
kernel Linux maggiormente orientato ad applicazioni real-time, andando a
modificare il kernel stesso negli aspetti caratteristici dei sistemi operativi
real-time.
In particolare si cerca di modificare il comportamento dello scheduler e
le caratteristiche di preemption sui processi.
Il secondo approccio vuole invece astrarre il kernel Linux dall’hardware
del sistema frapponendovi un kernel real-time, diverso e indipendente dal
kernel Linux, in grado di gestire adeguatamente i processi real-time e di
trattare Linux come un processo a bassa priorità.
In questo modo si riesce a garantire il corretto trattamento dei processi
real-time direttamente con il kernel di basso livello, e a mantenere la compatibilità con le applicazioni Linux che vengono eseguite quando il sistema
non è impegnato da applicazioni real-time.
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
26
Figura 1.6: Struttura di un sistema operativo basato sul primo approccio
(sinistra) e sul secondo approccio (destra)
1.3.3
Approccio RT basato sullo sviluppo del kernel Linux
Questo tipo di approccio si pone l’obiettivo di rendere Linux maggiormente
adatto ad applicazioni in tempo reale apportando delle modifiche sostanziali
in alcune funzionalità del kernel, strategiche dal punto di vista dell’utilizzo
in contesti real-time. I due punti fondamentali su cui si agisce sul kernel
sono:
1. scheduler;
2. gestione della preemption.
Lo scheduler deve essere in grado di svolgere il suo compito considerando
le richieste dei processi RT e deve eseguirlo in un tempo predicibile.
Lo scheduler del kernel Linux 2.4 non fornisce le garanzie necessarie per
un suo utilizzo in un sistema real-time. Il tempo di scheduling dipende infatti
linearmente dal numero di task che deve gestire, caratteristica che rende non
predicibile il suo tempo di esecuzione.
Questo scheduler viene quindi considerato O(N). Per rimediare a tale
problema Ingo Molnar ha scritto uno scheduler completamente nuovo che,
grazie a una diversa gestione del processo di scheduling, ottempera ai suoi
compiti in un tempo indipendente dal numero di task che deve gestire e
pertanto viene indicato come O(1).
Le principali caratteristiche dei due scheduler sono:
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
27
1. Scheduler O(N): questo scheduler suddivide il tempo in «epoche»,
periodi in cui ogni task può utilizzare il proprio timeslice. I timeslice di
ogni task vengono calcolati insieme all’inizio di ogni epoca (la durata
di tale operazione dipende linearmente dal numero di task in esecuzione nel sistema). Al termine di un’epoca i task potrebbero non aver
utilizzato tutto il loro timeslice: in questo caso il timeslice inutilizzato
sarà considerato nel calcolo del timeslice per l’epoca successiva;
2. Scheduler O(1): questo scheduler (ideato da Ingo Molnar) permette
di definire un tempo massimo di esecuzione indipendente dal numero
di task in esecuzione nel sistema. Esso è incentrato su una struttura
chiamata runqueue che tiene traccia di tutti i task assegnati a una
certa CPU (quindi ci sarà una runqueue per ogni CPU presente nel
sistema). Ogni runqueue contiene due array: l’active array e l’expired
array. Tutti i task vengono inizialmente posti nell’active array, per poi
essere trasferiti uno alla volta nell’expired array quando hanno utilizzato tutto il timeslice che gli era stato assegnato. Durante il trasferimento
da un array all’altro viene calcolato il nuovo timeslice. Quando l’active
array è vuoto si torna alla situazione iniziale con un semplice swap dei
puntatori dei due array. Questo permette di realizzare la transizione
tra epoche diverse in un tempo costante. L’ordine in cui i task vengono
schedulati dipende ovviamente dalla loro priorità. Per individuare il
task a massima priorità in un tempo limite predeterminabile, si è adottata una struttura particolare per i due array: essi sono infatti array di
linked-list in cui è presente una lista per ogni livello di priorità. Quando un task viene inserito nell’array, esso viene in realtà appeso alla
linked-list corrispondente al livello di priorità che gli è stato assegnato.
Per trovare il task a maggiore priorità è così sufficiente individuare la
prima lista di task ed estrarne il processo da mandare in esecuzione.
La preemption, ovvero la possibilità di interrompere l’esecuzione di un
processo per passare il controllo delle risorse a un altro, deve essere gestita
in modo da garantire che un processo RT riesca a ottenere la disponibilità
delle risorse in un tempo utile a eseguire correttamente (sia dal punto di
vista logico sia, soprattutto, temporale) la propria elaborazione.
Nel kernel Linux 2.4 i problemi principali che si riscontrano nella gestione della preemption sono dovuti all’impossibilità di interrompere l’esecuzione
dei processi eseguiti in kernel space, al limitato numero di chiamate allo scheduler (che comportano l’esecuzione monolitica di grandi parti di codice) e alla
mancanza di timer ad alta risoluzione, necessari per gestire uno scheduling
molto frequente.
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
28
Per ovviare a questi problemi sono state preparate delle patch per il
kernel, in parte poi adottate anche nel kernel Linux 2.6 ufficiale.
Le principali patch realizzate per agevolare la preemption nel kernel
Linux sono:
• Preemption Patch: consiste nel permettere che lo scheduler venga
eseguito e che un task perda il controllo mentre si trova a eseguire
del codice appartenente al kernel. Ovviamente un task può essere
interrotto solo quando esegue le sezioni del kernel in corrispondenza
delle quali la preemption può effettivamente avere luogo senza traumi;
• Low-Latency Patch: consiste semplicemente nel modificare il kernel,
spezzando i loop nel codice e inserendo delle chiamate allo scheduler
per aumentare il numero di punti di preemption. Questa patch richiede
cambiamenti piuttosto pesanti e diffusi; inoltre raggiunge migliori risultati sui tempi di latenza del kernel rispetto alla Preemption Patch. La
funzionalità di questa patch è anche chiamata Voluntary Preemption;
• High Resolution Timers Patch: introduce la possibilità di utilizzare timer ad alta risoluzione (fino all’ordine dei microsecondi).
1.3.4
Approccio RT basato sull’utilizzo di un kernel RT di
basso livello
Questo tipo di approccio si pone l’obiettivo di rendere Linux adatto ad applicazioni in tempo reale inserendo un nuovo strato software, costituito da
un kernel realmente real- time, tra l’hardware e il kernel Linux.
Il kernel RT di basso livello gestisce direttamente i processi real-time e
tratta il kernel Linux come un processo a bassa priorità.
Lo scheduler del kernel real-time tratta il kernel Linux come se fosse il
task di idle. Linux viene eseguito solo quando non ci sono task RT da lanciare
e il kernel RT è inattivo.
I task Linux non possono mai bloccare interrupt o proteggere sé stessi dalla preemption del kernel RT. Questo comportamento è reso possibile
dall’emulazione software del controllo hardware degli interrupt.
I due principali progetti che adottano questo genere di approccio sono Xenomai e RTAI, sviluppati rispettivamente dal progetto DENX e dal DIAPM
(Dipartimento di Ingegneria Aerospaziale del Politecnico di Milano [9]).
Considerando il maggior supporto dei forum, della mailing list e la licenza
di utilizzo GPL/LGPL si è deciso di approfondire Xenomai.
CAPITOLO 1. I SISTEMI OPERATIVI REAL-TIME E LINUX
29
Figura 1.7: Struttura di una soluzione real-time Linux con l’uso di un kernel
RT
Capitolo 2
Xenomai
In questo capitolo si descriveranno i dettagli della soluzione adottata per il
porting.
Si porteranno in luce tutti gli aspetti che hanno fatto sì che la nostra
attenzione si focalizzasse su questo sistema, a partire dalla struttura del
sistema, in particolar modo sulla pipeline Adeos, per finire con la descrizione
dei servizi offerti dalle interfacce di programmazione e con la loro modalità
di utilizzo.
2.1
Struttura del progetto
Il progetto Xenomai iniziato nel 2001, finì col fondersi, due anni più tardi, nel
progetto RTAI allo scopo di creare una piattaforma software gratuita, con
caratteristiche real-time, chiamata RTAI/fusion che si appoggiava al kernel
real-time di Xenomai. Nel 2005 il progetto tornò indipendente da RTAI per
continuare lo sviluppo autonomamente.
La caratteristica principale di Xenomai è la presenza di API (Application
Programming Interface) indipendenti tra loro. Accanto alle interfacce (chiamate anche skin) che emulano i principali sistemi operativi real-time proprietari, Xenomai permette di sviluppare delle applicazioni, sfruttando le potenzialità del kernel real-time e della sua perfetta integrazione con l’ambiente
Linux.
A questo proposito, Xenomai mette a disposizione due alternative per lo
sviluppo del software: una estensione real-time dello standard POSIX e una
30
CAPITOLO 2. XENOMAI
31
Figura 2.1: Schema dell’architettura di Xenomai
skin nativa, semanticamente vicina alle interfacce degli altri sistemi operativi
in tempo reale (VxWorks, pSOS+, uITRON, VRTX).
Al contrario di RTAI, Xenomai predilige principalmente la portabilità,
l’estensibilità e la mantenibilità.
I comportamenti richiesti dai tradizionali RTOS, relativi alla gestione
dei thread e alla loro sincronizzazione, agli interrupt e alla risoluzione dei
tempi, sono stati implementati nel nucleo esportando alcune classi di servizio.
Tali servizi rappresentano lo strato fondamentale su cui sviluppare ogni API
secondo le proprie esigenze.
Per rendere tale strato indipendente dall’architettura utilizzata, viene
fornito il supporto necessario per il controllo dell’hardware, attraverso un
sottostante layer software (Hardware Abstraction Layer) a cui si accede tramite una interfaccia standardizzata. In questo modo, il porting del kernel
real-time su un’altra architettura, consta solamente dell’implementazione di
questa interfaccia a basso livello per la piattaforma che si vuole utilizzare.
Questo strato software deve fornire le funzionalità di un RTOS ovvero
deve:
• inviare su richiesta gli interrupt esterni all’handler specifico;
CAPITOLO 2. XENOMAI
32
• fornire un modo per nascondere o meno gli interrupt;
• garantire la possibilità di creare nuovi thread di controllo nella forma
più semplice;
• fornire un supporto per interrupt periodici e aperiodici utilizzati nella
gestione dei timer;
• supportare l’allocazione di memoria non pageable.
In figura 2.1 risultano evidenti le posizioni dei vari componenti a partire dalla
pipeline Adeos, di cui si parlerà dettagliatamente nella sezione seguente, per
continuare col livello di astrazione dell’hardware e con il kernel real-time fino
ad arrivare alle varie interfacce di programmazione.
2.2
Adeos pipeline
Xenomai basa il suo funzionamento sul layer di virtualizzazione Adeos, che
è disponibile come patch del kernel.
Adeos abilita entità multiple, chiamate anche dominii, che permettono
di poter avere sullo stesso hardware un ambiente GNU/Linux e un RTOS
perfettamente funzionanti.
Tutti i dominii esistenti non sono necessariamente consapevoli dell’esistenza degli altri, ma ognuno di essi può e deve interagire con Adeos. Ogni
entità compete con l’altra per il processamento di eventi esterni, come gli
interrupt, e di eventi interni, come trap ed exception, a seconda della priorità
che è stata loro fissata.
Oltre alla virtualizzazione, Adeos consente di esportare ai dominii un’API
generica indipendente dall’architettura della CPU.
La struttura di Adeos può essere vista come una catena di dominii che
hanno il compito di gestire il controllo di eventi.
Un dominio è una componente software kernel-based che può chiedere al
layer Adeos di essere notificato quando si verificano determinati eventi:
• l’arrivo di un interrupt esterno o di un interrupt virtuale autogenerato;
• ogni chiamata di sistema richiesta dalle applicazioni Linux;
• tutte le altre chiamate di sistema generate dal codice del kernel, come
per esempio lo switch o l’uscita dei task di Linux e la notifica di segnali.
CAPITOLO 2. XENOMAI
33
Figura 2.2: Schema dell’astrazione introdotta dalla pipeline Adeos
Adeos assicura che gli eventi siano inviati in maniera ordinata ai vari dominii
secondo il rispettivo ordine di priorità nel sistema, in modo da assicurare
consegne tempestive e predicibili di tali eventi. Quanto detto si ottiene,
appunto, tramite l’assegnazione di una priorità statica a ogni dominio per
mezzo della quale viene determinato l’ordine di consegna degli eventi a tutti
i dominii.
I dominii attivi si trovano accodati secondo la loro priorità, realizzando
così l’astrazione della pipeline utilizzata da Adeos per ottenere un flusso
di eventi, ordinato dal dominio a priorità maggiore sino a quello a priorità
minore.
Tutti gli eventi in arrivo sono inviati all’inizio della pipeline, ovvero al dominio a più alta priorità, e inoltrati progressivamente fino all’ultimo dominio
esistente, quello a priorità più bassa.
Nella figura 2.2 viene illustrata una rappresentazione generica di un sistema basato su Adeos, in cui si possono osservare più dominii che condividono
trap e interrupt attraverso l’astrazione della pipeline.
Il kernel Linux può ricoprire qualunque posizione nella pipeline ed è marcato come dominio root, in quanto tutti gli altri dominii hanno bisogno di
Linux per poter essere installati, spesso come moduli del kernel dello stesso.
Uno stadio della pipeline occupato da un dominio può trovarsi in stallo,
il che significa che il successivo interrupt in arrivo non sarà consegnato all’handler del dominio e, allo stesso tempo, gli sarà impedito di scorrere verso
i dominii di priorità inferiore.
Quando uno stadio si trova in stallo, gli interrupt pendenti sono accu-
CAPITOLO 2. XENOMAI
34
Figura 2.3: Gestione di Adeos su dominii multipli con n CPU
mulati nel log degli interrupt del dominio ed eventualmente elaborati, una
volta rimosso lo stato di stallo dall’operazione interna di sincronizzazione.
I dominii utilizzano questa funzione, per proteggere le proprie sezioni
critiche da prelazioni indesiderate da parte dei propri interrupt handler. In
ogni caso, grazie alla virtualizzazione del controllo degli interrupt introdotto
da Adeos, un dominio di priorità più alta può comunque ricevere interrupt ed
eventualmente prelazionare un dominio a priorità più bassa. Questo significa
che se si utilizza Adeos, si può avere il kernel Linux che svolge le proprie
operazioni critiche e, allo stesso tempo, un sistema real-time in cima alla
pipeline, che sarà in grado di ricevere gli interrupt ogni volta senza alcun
ritardo.
Quando un dominio finisce di processare gli interrupt pendenti che ha
ricevuto, richiama un servizio di Adeos che porta la CPU al dominio successivo della pipeline, cosicché quest’ultimo possa processare i propri interrupt
e così via sino al dominio che si trova nell’ultima posizione della pipeline.
Nella figura 2.3 viene illustrato il modo in cui diversi dominii, eseguiti
su CPU multiple, condividono gli interrupt in arrivo attraverso l’astrazione
della pipeline introdotta da Adeos.
Risulta evidente che si deve mantenere una corretta memorizzazione de-
CAPITOLO 2. XENOMAI
35
gli interrupt pendenti quando uno stadio si trova in stallo. Questo viene
realizzato tramite un log che tiene conto del dominio i-esimo e della n-esima
CPU.
Gli interrupt non sono l’unico genere di eventi che può fluire attraverso
l’astrazione della pipeline. Gli eventi interni, innescati dal kernel Linux stesso
o dalle sue applicazioni, possono generare quelli che sono chiamati eventi di
sistema.
Gli eventi di sistema sono notifiche sincrone di trap, exception o di alcune
azioni del kernel Linux che sono notificate attraverso la pipeline.
Dal momento in cui questi eventi sono sincroni per definizione, non c’è
alcun modo di ritardare la loro notifica tramite l’operazione di stallo, non
essendo possibile posticipare la loro gestione. Un codice che fa partire un
evento di sistema, potrebbe non essere in grado di continuare senza l’intervento immediato dell’handler corrispondente. Per chiarire meglio quanto
detto, consideriamo un errore di paginazione: l’handler relativo deve attivarsi immediatamente al momento dell’exception relativa all’indirizzamento di
memoria.
L’operazione di stallo su un dato dominio ha significato solo per quanto
riguarda gli interrupt reali o virtuali.
2.2.1
Adeos e Xenomai
Tutte le caratteristiche di Adeos elencate sino a ora permettono l’implementazione di Xenomai accanto al kernel Linux. Come sistema in tempo reale,
è facile intuire che l’esigenza di Xenomai è quella di riuscire a gestire tutti
gli interrupt in arrivo, prima che il kernel Linux sia in grado di notarli. Inoltre, Xenomai deve assicurarsi di poterli elaborare immediatamente anche in
presenza dei tentativi di blocco del kernel Linux.
Xenomai deve assicurarsi di rafforzare la priorità di gestione dei propri
thread, anche quando questi si trovano in esecuzione in altri dominii.
Mettendo a disposizione queste caratteristiche, Adeos consente a Xenomai di avere latenze di interrupt predicibili nell’ordine dei micro-secondi, con
qualunque attività di Linux contemporaneamente in esecuzione. Tutto ciò,
abbinato alla tecnica di co-scheduling dei task di Linux, fornisce latenze di
scheduling dei thread real-time deterministiche.
In figura 2.1 si può notare la posizione di Adeos che risulta direttamente
a contatto con l’Hardware Abstraction Layer (HAL), posto al di sotto del
CAPITOLO 2. XENOMAI
36
nucleo di Xenomai. Molte delle richieste per i servizi Adeos sono diramate
proprio da quest’ultimo.
2.2.2
I dominii di Xenomai
Xenomai permette l’esecuzione di thread real-time sia in kernel-space, sia
entro lo spazio di indirizzi di un processo Linux. Di seguito ci si riferirà a
quest’ultimi come thread di Xenomai che non devono essere confusi con i
regolari task di Linux. Tutti i thread gestiti da Xenomai sono conosciuti
dal suo nucleo. Xenomai introduce quindi un vero supporto user-space per
il real-time, che determina la vera novità rispetto ai tradizionali sistemi a
doppio kernel, in cui le applicazioni RT potevano essere eseguite solo se
incorpate in moduli del kernel.
L’approccio di Xenomai nei confronti di Linux, a differenza di quello
realizzato dal sistema RTAI/LXRT, si può definire simbiotico. Infatti, i
thread di Xenomai possono andare in esecuzione non solo nel dominio a
maggior priorità della pipeline (dominio primario), ma anche nel dominio di
Linux (dominio secondario) che viene considerato ancora real-time anche se
soggetto a latenze di scheduling più elevate.
Affinché sia garantito un pieno supporto real-time ai thread che si trovano in esecuzione nel dominio secondario, è necessario che siano rispettati
i seguenti punti:
• schema di priorità comune: si deve fare in modo che il kernel
real-time e il kernel Linux condividano lo stesso schema di priorità. In
altre parole, un thread di Xenomai dovrebbe veder rispettata la sua
priorità qualunque sia il dominio di esecuzione. Il kernel Linux eredita
automaticamente la priorità del thread di Xenomai che è passato in modalità secondaria: ciò significa che i thread di Xenomai, in esecuzione
in modalità primaria, non prelazioneranno necessariamente quelli che
si trovano in modalità secondaria, a meno che la loro effettiva priorità
sia più alta.
CAPITOLO 2. XENOMAI
37
Figura 2.4: Migrazione tra dominii
Quanto descritto è esattamente l’opposto di quello che accade su RTAI,
dove i thread che migrano nel kernel Linux perdono allo stesso tempo
la loro priorità real-time. I task di Linux saranno sempre prelazionati
dai thread di Xenomai, in esecuzione in modalità primaria, mentre i
thread di Xenomai, passati in modalità secondaria, continueranno a
competere con questi ultimi tramite il sistema di priorità;
• predicibilità dei tempi di esecuzione del programma: i tempi
di esecuzione di un thread di Xenomai, in modalità secondaria, non
dovrebbero essere condizionati dalle attività di gestione degli interrupt
di Linux o, in termini più generici, da qualunque attività asincrona
che si verifica a livello del kernel. Il modo più semplice per evitare
che questo accada, è mandare il kernel Linux in starvation quando un
thread di Xenomai è in esecuzione nel dominio di Linux, cosicché non
vengano introdotti ritardi dovuti agli interrupt handler. Per mandare
in starvation il kernel Linux, si introduce un dominio intermedio di
Adeos in cui bloccare gli interrupt quando necessario. Tale dominio è
chiamato interrupt shield e va a occupare nella pipeline la posizione
tra il kernel real-time e il kernel Linux (Figura 2.5). L’interrupt shield
viene attivato ogni volta che un thread di Xenomai viene schedulato
dal kernel Linux e disattivato in tutti gli altri casi. L’abilitazione di
tale schermo può essere effettuata per una determinata applicazione
o in fase di compilazione di Xenomai marcando l’opportuna flag di
configurazione;
CAPITOLO 2. XENOMAI
38
Figura 2.5: Interrupt shield
• granularità del kernel Linux: per ottenere delle buone prestazioni in modalità secondaria, è necessario che il kernel Linux esponga
la sua sezione non prelazionabile per il più breve tempo possibile, cosicché si possa schedulare in tempi rapidi un thread di Xenomai che ha
effettuato lo switch di modalità. Inoltre, questo assicura che i thread
di Xenomai possono realizzare la migrazione dal dominio primario al
secondario entro un ridotto periodo di tempo, poichè questa operazione
deve attendere un punto di rescheduling. Xenomai beneficia dei continui miglioramenti riguardo alla prelazione del kernel, come per esempio
l’estensione PREEMPT_RT di Ingo Molnar [Rif]. Ovviamente i thread di Xenomai che vanno in esecuzione in modalità primaria non
sono affetti dal livello di granularità del kernel e beneficiano sempre
delle basse latenze garantite dal nano-kernel real-time, poichè non devono sincronizzarsi in alcun modo con le operazioni di Linux, le quali
vengono prelazionate incondizionatamente;
• gestione dell’inversione di priorità: sia il kernel real-time, sia
il kernel Linux devono gestire il caso in cui l’esecuzione di un thread ad
alta priorità viene impedita da un thread a priorità inferiore, che tiene
occupata una risorsa condivisa per un certo tempo. Xenomai fornisce
questo supporto, mentre il kernel Linux vi riesce solo nella sua variante PREEMPT_RT. Per questa ragione lo sviluppo di Xenomai segue
con attenzione lo sviluppo della patch PREEMPT_RT, nonostante la
release principale del kernel rimanga il sistema di riferimento.
In conclusione, quando il kernel di Xenomai viene caricato, la pipeline
Adeos contiene tre stadi attraverso i quali fluiscono tutti gli interrupt secondo
l’ordine di priorità: il dominio primario di Xenomai, il dominio dell’interrupt
shield e il dominio di Linux.
2.2.3
Intercettare le chiamate di sistema
Dal momento in cui le skin, le quali sono allocate nello stack del nano-kernel
real-time, possono esportare i propri set di servizi ai thread user-space di
CAPITOLO 2. XENOMAI
39
Xenomai, si deve gestire un modo in cui le corrispondenti chiamate di sistema
e le chiamate regolari del kernel Linux vengono spedite ai rispettivi handler.
Xenomai intercetta ogni chiamata di sistema relativa a exception o trap
inviata a tutti i dominii da parte dei thread real-time tramite il servizio di
Adeos adeos_catch_event().
In questo modo Xenomai riesce a:
• inviare le richieste per i servizi real-time dalle applicazioni ai rispettivi
handler, i quali sono implementati dalle varie interfacce di programmazione che sono in esecuzione sul kernel real-time;
• assicurarsi che ogni chiamata di sistema sia effettuata sotto il controllo
del dominio appropriato, effettuando se necessario una migrazione del
chiamante al dominio richiesto senza nessuna interruzione. Per esempio una chiamata di sistema avanzata da un thread di Xenomai, che
si trova in esecuzione nel dominio real-time, provocherà una migrazione automatica nel dominio di Linux prima che la richiesta venga
trasmessa all’handler. Al contrario, supponendo di avere un thread di
Xenomai che invoca una chiamata di sistema per Xenomai potenzialmente bloccante, questo viene spostato nel dominio real-time, prima
che il servizio sia assolto, cosicché il thread possa andare in sleep sotto
il controllo del kernel real-time.
In questo modo si riesce a ottenere una ottima integrazione dei thread
Xenomai in Linux.
2.2.4
Propagazione degli interrupt
Data la sua posizione in cima alla pipeline, il kernel real-time di Xenomai è
il primo a essere avvisato di un interrupt in arrivo, lo processa e lo segnala
in modo che possa essere inoltrato agli altri dominii della pipeline.
Ricevuta la notifica dell’arrivo di un interrupt, il kernel real-time effettua
un rescheduling e fa partire il thread a più alta priorità che lo gestisce.
Adeos ha due modalità di propagazione per gli interrupt attraverso la
pipeline:
• modalità implicita: ogni interrupt in arrivo è marcato automaticamente come pendente in ogni log di dominio che accetta tale interrupt;
• modalità esplicita: un interrupt, se necessario, viene propagato
manualmente al dominio confinante nella pipeline.
CAPITOLO 2. XENOMAI
40
La scelta della modalità di propagazione è settata all’interno di ciascun dominio. Per quel che riguarda Xenomai, viene sempre utilizzata la modalità
esplicita per tutti gli interrupt che vengono intercettati. Ogni handler deve
richiamare il servizio di propagazione esplicito per inoltrare un interrupt in
arrivo lungo la pipeline.
Nel caso in cui non sia definito l’handler per un dato interrupt, questo
viene propagato incondizionatamente al kernel Linux mantenendo quindi il
sistema funzionante.
2.3
Installazione di Xenomai su x86
Il primo passo da intraprendere per l’installazione di Xenomai sul kernel
vanilla è il recupero dei sorgenti dal sito ufficiale del progetto. Una volta recuperata l’ultima release stabile, disponibile al momento, si è passati
all’estrazione dell’archivio nella cartella /usr/src tramite il comando tar:
tar -xjvf xenomai-2.5.x.tar.bz2
La versione utilizzata in questa tesi è la 2.5.5.2.
Terminata l’estrazione dell’archivio, il passo successivo è controllare le
versioni del kernel Linux, supportate dalla patch Adeos, disponibili nella
sottocartella ksrc/arch/x86/patches e scaricare il relativo kernel vanilla [10].
Per l’installazione su x86 è stato utilizzato il kernel 2.6.35.7.
Sempre con il comando tar si effettua l’estrazione dei sorgenti del kernel
nella directory /usr/src. A quel punto resta solo da avviare lo script opportuno dell’archivio di Xenomai per applicare le patch e compilare quindi il
sottosistema real-time integrato nel kernel Linux.
Preparazione del kernel
Nella cartella scripts è presente lo script prepare-kernel.sh, che va utilizzato
con la seguente sintassi:
prepare-kernel.sh --linux=<linux-srctree>
[--adeos=<adeos-patch>] [--arch=<target-arch>]
–linux specifica il percorso della cartella dove sono stati estratti i sorgenti
del kernel vanilla, nel nostro caso la cartella /usr/src/linux-2.6.35.7.
CAPITOLO 2. XENOMAI
41
–adeos indica il percorso in cui si trova la patch Adeos da applicare sulla
cartella del kernel.
–arch specifica l’architettura del target.
Configurazione e preparazione del kernel
Terminata la fase di preparazione dei sorgenti del kernel, è necessario creare il file di configurazione. Questo può essere realizzato attraverso una configurazione grafica tramite il comando make menuconfig o in alternativa make
xconfig/gconfig.
Utilizzando il primo dei comandi appena elencati, ci si trova di fronte
alla seguente schermata:
Figura 2.6: Menu di configurazione del kernel
Per chi abbia già compilato un kernel Linux, è facile notare immediatamente una nuova voce nel menu di configurazione ovvero la sezione Real-time
sub-system, che comprende tutte le funzionalità relative al nano-kernel di
Xenomai.
Esplorando il contenuto del sottomenu, si trovano varie opzioni di configurazione come l’inclusione delle nuove classi di scheduling (es. Sporadic Ser-
CAPITOLO 2. XENOMAI
42
ver ), supporto per il debug, abilitazione delle varie interfacce di programmazione (POSIX, VxWorks, pSOS), settaggio dei watchdog e altre impostazioni
da valutare secondo le proprie necessità.
Da questa schermata di configurazione, si possono disattivare le parti
relative all’hardware che non verranno utilizzate, come per esempio i moduli
realtivi a Joystick, Touchscreen, etc.
Inoltre, è necessario controllare che determinate voci siano attive, mentre
altre funzionalità devono essere disabilitate poiché possono compromettere
le prestazioni, in termini di latenza, del kernel real-time.
Esaminando nel dettaglio, si deve fare attenzione alle seguenti voci del
menu di configurazione:
• in Processor type and features:
– Controllare che la voce relativa alla Interrupt pipeline sia abilitata.
Questo garantisce che l’implementazione della I-pipe aggiunta con
la patch Adeos sia attiva;
– Verificare che le voci Local APIC support on uniprocessors e IOAPIC support on uniprocessors siano abilitate.
• in Power management and ACPI options:
– Disabilitare tutto il sotto menu ACPI ;
– Disattivare il controllo di potenza avanzato APM ;
– Nella menu CPU frequency scaling disabilitare la voce riferita alla
CPU.
Oltre alle opzioni appena elencate, si è disattivata la voce SMP perchè
si è compilato il kernel con il supporto per un solo processore.
Terminata la fase di configurazione si deve uscire dall’interfaccia grafica
e salvare le modifiche. A questo punto nella source tree del kernel sarà
disponibile il file .config, con tutte le informazioni relative alla configurazione
appena effettuata.
Il passo successivo è la compilazione del kernel.
Compilazione e avvio del nuovo kernel
Tramite il comando make si può procedere alla compilazione del kernel.
Tuttavia si è scelto di adottare un accorgimento molto pratico, cioè quello di
CAPITOLO 2. XENOMAI
43
creare dei pacchetti .deb, così da avere disponibile l’installazione del kernel
patchato per qualunque altra macchina con architettura x86.
Per lanciare la compilazione e creare i pacchetti .deb, i repository di
Debian mettono a disposizione il pacchetto kernel-package che contiene lo
script per la compilazione e fornisce il modo di creare un’immagine del kernel
Linux.
Il comando per eseguire quanto detto è il seguente:
make-kpkg --initrd --append-to-version -xenomai
--revision 1.0 kernel_image kernel_headers
Le opzioni –append-to-version e –revision sono facoltative, ma utili per
distinguere diverse versioni di kernel customizzati.
La fase di compilazione è piuttosto lunga in quanto i sorgenti da compilare
sono molto corposi e numerosi. In un sistema multi-core si può velocizzare
questa operazione utilizzando l’opzione CONCURRENCY_LEVEL=p, dove
p indica il numero di processori che si vogliono dedicare alla compilazione
del kernel.
Terminata la compilazione, si ha a disposizione il file relativo all’immagine della source tree del kernel e il file contenente i vari header. Nel nostro
caso particolare avremo a che fare con i seguenti file:
• linux-image-2.6.35.7-xenomai.deb;
• linux-headers-2.6.35.7-xenomai.deb.
Per installare i pacchetti, si utilizza il comando dpkg specificando l’opzione
-i. Attraverso questa procedura l’immagine del kernel con i relativi headers
viene installata, inoltre viene anche aggiornato il bootloader (nel nostro caso
il grub).
Riavviando il sistema, troveremo visualizzata anche la voce del kernel
2.6.35.7-xenomai. Selezionandola si potrà avviare il kernel modificato con
l’inclusione del dominio real-time.
CAPITOLO 2. XENOMAI
44
Figura 2.7: Controllo sul buffer del kernel della corretta attivazione di
Xenomai
Per controllare che l’installazione sia andata a buon fine, si può filtrare
la voce Xenomai dal contenuto dei messaggi del buffer del kernel:
dmesg | grep -i xenomai
Il risultato prodotto in output sulla shell è visualizzato in figura 2.7
Dalla prima riga è possibile vedere che Adeos è stata attivata e si trova regolarmente in esecuzione e che il dominio relativo al kernel real-time
di Xenomai è stato attivato. Di seguito vengono elencati tutti i servizi di
Xenomai attivati in fase di configurazione.
L’ultimo step da eseguire è l’installazione del supporto user-space di Xenomai. Questo si realizza tramite l’esecuzione dello script configure presente
nella cartella /usr/src/xenomai.
Una verifica veloce della buona riuscita dell’installazione, si può riscontrare mandando in esecuzione alcuni programmi forniti con Xenomai.
Infatti, nella cartella /usr/xenomai/bin si possono trovare una serie di
binari da avviare che realizzano una serie di test sul sistema appena installato.
CAPITOLO 2. XENOMAI
2.4
45
Testing della skin nativa
L’interfaccia di programmazione nativa segue determinate linee guida. In
primo luogo si tratta di un’API compatta, che fornisce un numero contenuto di servizi, senza rinunciare ad avere un sistema stabile e di facile
programmazione nell’ambiente GNU/Linux.
Tali servizi vengono resi disponibili in maniera non ambigua, in modo
da non poter essere semanticamente confusi e lasciare allo sviluppatore il
compito di combinarli tra loro per ottenere il risultato desiderato.
Sebbene sia preferibile eseguire le applicazioni di Xenomai in user-space,
possono presentarsi dei casi in cui bisogna eseguire del codice in moduli del
kernel come per esempio nel caso di hardware poco performanti. Per questo
motivo la skin nativa fornisce lo stesso set di servizi real-time alle applicazioni
indipendentemente dal loro spazio di esecuzione. Inoltre, è possibile che
attività real-time che si trovano in spazi di esecuzione differenti, debbano
cooperare e questo è possibile tramite la condivisione degli stessi oggetti
messi a disposizione dalla skin.
Utilizzando la skin nativa si può inoltrare la stessa chiamata di sistema
per eseguire un’azione senza preoccuparsi dello spazio di esecuzione. Ciò
significa che un task sarà sempre rappresentato da un descrittore RT_TASK,
una coda da un RT_QUEUE e un semaforo da un RT_SEM qualunque sia
lo spazio in cui vengono utilizzati. Tali descrittori possono essere condivisi
tra gli spazi di esecuzione cosicché un servizio invochi un oggetto che è stato
creato nello spazio opposto.
Per creare una facile corrispondenza per la ricerca di questi descrittori
sul sistema Xenomai, esiste un registro unificato: esso permette a tutte le
categorie di servizi real-time di indicizzare ogni oggetto che creano in una
unica chiave simbolica definita dall’applicazione.
Come tutte le altre, la skin nativa è una parte opzionale del sistema
Xenomai ed è basata sullo stesso kernel real-time che fornisce una serie di
servizi.
I servizi dell’API nativa sono implementati dal modulo del kernel chiamato xeno_native e, come già detto, possono essere invocati sia in kernelspace, utilizzando le funzioni di chiamata inter-moduli, sia dal contesto userspace attraverso la libreria libnative.so invocando le opportune chiamate di
sistema.
CAPITOLO 2. XENOMAI
46
La skin nativa fornisce sei categorie di servizio e ognuna di esse definisce
un set di chiamate di sistema disponibili per entrambi gli space.
Tali categorie sono:
• gestione dei task: definisce il set di servizi relativi allo scheduling e
al controllo in senso lato dei task. Un’applicazione necessita di questa
chiamate di sistema per creare task e controllarne il comportamento.
Tutti questi servizi sono inclusi di base nel modulo dell’API nativa indipendentemente dalle opzioni di configurazione adottate. Essa utilizza
una scala di priorità per i task, compatibile con la scala POSIX per la
classe di schedulazione SCHED_FIFO, supportata da Linux (dove 99
rappresenta il livello di priorità più alto);
• servizi di timing: raggruppa tutte le chiamate di sistema relative alle
interrogazioni e alla gestione del timer di sistema. Questa categoria
comprende anche la creazione di timer chiamati Alarms;
• supporto di sincronizzazione: fornisce supporto ai task che necessitano di sincronizzare le proprie operazioni. Gli oggetti di sincronizzazione utilizzabili per questo scopo sono semafori, mutex, variabili
condizionali e event flag;
• messaggi e comunicazione: questa categoria implementa diversi
modi per lo scambio di dati tra i task real-time o tra attività real-time
in kernel-space e processi di Linux in user-space;
• gestione di dispositivi i/o: la skin nativa fornisce un supporto per
l’interazione con interrupt e per accedere alla memoria di dispositivi di input/output dal contesto user-space. Per quel che riguarda la
completa creazione di driver per dispositivi è disponibile l’interfaccia
RTDM;
• supporto di registri: consente l’accesso alle chiamate di sistema
da contesti di esecuzione differenti.
2.5
Sviluppo di script per il testing
Una volta installato il sistema Xenomai, si è scelto di sviluppare alcuni semplici, ma pragmatici script sulla skin nativa, per verificare il funzionamento
dello scheduler RT.
Uno script interessante prevede la creazione di due task real-time aventi
lo stesso periodo ognuno dei quali va a occupare la CPU rispettivamente per
CAPITOLO 2. XENOMAI
1
3
e per
2
3
47
del periodo, in modo da avere in totale una occupazione della CPU
pari al 100%.
Una possibile implementazione di questo test consiste nel seguente set di
istruzioni:
#i n c l u d e <s t d i o . h>
#i n c l u d e <s i g n a l . h>
#i n c l u d e <u n i s t d . h>
#i n c l u d e <s y s /mman. h>
#i n c l u d e <time . h>
#i n c l u d e <n a t i v e / t a s k . h>
#i n c l u d e <n a t i v e / t i m e r . h>
// uso d e l l a r t _ p r i n t f ( )
#i n c l u d e <r t d k . h>
RT_TASK task_desc_1 ;
RT_TASK task_desc_2 ;
v o i d task_body_1 ( v o i d ∗ a r g ) {
// t a s k 1 p e r i o d i c o , con p e r i o d o t 1=3s
r t _ t a s k _ s e t _ p e r i o d i c (NULL, TM_NOW, 3 0 0 0 0 0 0 0 0 0 ) ;
int i ;
f o r ( i =0; i <10; i ++) {
// c1=1s
r t _ p r i n t f ( " Busy w a i t 1 s . . . \ n " ) ;
rt_timer_spin ( 1 0 0 0 0 0 0 0 0 0 ) ;
rt_task_wait_period (NULL ) ;
}
}
v o i d task_body_2 ( v o i d ∗ a r g ) {
// t a s k 2 p e r i o d i c o , con p e r i o d o t 2=3s
r t _ t a s k _ s e t _ p e r i o d i c (NULL, TM_NOW, 3 0 0 0 0 0 0 0 0 0 ) ;
CAPITOLO 2. XENOMAI
48
int i ;
f o r ( i =0; i <10; i ++) {
// c2=2s
r t _ p r i n t f ( " Busy w a i t 2 s . . . \ n " ) ;
rt_timer_spin ( 2 0 0 0 0 0 0 0 0 0 ) ;
rt_task_wait_period (NULL ) ;
}
}
i n t main ( i n t argc , c h a r ∗ argv [ ] ) {
s i g n a l (SIGTERM, c a t c h _ s i g n a l ) ;
s i g n a l ( SIGINT , c a t c h _ s i g n a l ) ;
/∗ Avoids memory swapping f o r t h i s program ∗/
m l o c k a l l (MCL_CURRENT|MCL_FUTURE) ;
/∗ Perform auto−i n i t o f r t _ p r i n t b u f f e r s ∗/
/∗ i f t h e t a s k doesn ’ t do s o ∗/
rt_print_auto_init ( 1 ) ;
/∗ c r e a z i o n e t a s k RT ∗/
r t _ p r i n t f ( " c r e a z i o n e t a s k 1 c1 / t 1 =1/3\n " ) ;
r t _ t a s k _ c r e a t e (&task_desc_1 , " Task1 " , 0 , 9 9 , 0 ) ;
r t _ p r i n t f ( " c r e a z i o n e t a s k 2 c2 / t 2 =2/3\n " ) ;
r t _ t a s k _ c r e a t e (&task_desc_2 , " Task2 " , 0 , 9 9 , 0 ) ;
/∗ a v v i o t a s k RT ∗/
r t _ p r i n t f ( " t a s k 1 p r o n t o \n " ) ;
r t _ t a s k _ s t a r t (&task_desc_1 , &task_body_1 , NULL ) ;
r t _ p r i n t f ( " t a s k 2 p r o n t o \n " ) ;
r t _ t a s k _ s t a r t (&task_desc_2 , &task_body_2 , NULL ) ;
pause ( ) ;
r t _ p r i n t f ( " c a n c e l l a z i o n e t a s k \n " ) ;
r t _ t a s k _ d e l e t e (&task_desc_1 ) ;
CAPITOLO 2. XENOMAI
49
r t _ t a s k _ d e l e t e (&task_desc_2 ) ;
}
Come prima cosa si creano i due task RT con la funzione:
int rt_task_create (RT_TASK * task, const char * name, |
int stksize, int prio, int mode)
dove:
• task è l’indirizzo del descrittore di task Xenomai usato per memorizzare i dati del rispettivo task;
• name è una stringa ASCII e rappresenta il nome simbolico del task;
• stksize rappresenta la dimensione (in byte) dello stack del nuovo
task, passandogli zero come valore verrà utilizzata una dimensione
predefinita;
• prio è un intero che va da 0 a 99 e costituisce la priorità che si vuole
assegnare al task (0 indica la priorità minima, 99 la priorità massima);
• mode indica la modalità di creazione del task.
Nel caso in cui la creazione del task vada a buon fine, la routine restituirà
il valore intero zero.
E’ importante sottolineare che la funzione esplicitata può essere chiamata
sia in un contesto user-space, per esempio all’interno della funzione principale
di uno script, sia in un contesto kernel-space, per esempio all’interno della
funzione di inizializzazione del modulo (init_module).
Un’altra osservazione da tener presente è che dopo aver chiamato questa
procedura, è possibile il rescheduling dei task da parte del sistema Xenomai.
Inoltre, creato un processo, questo rimane inattivo fin tanto che non si chiama
la funzione:
int rt_task_start (RT_TASK * task, void(*)(void *cookie) |
entry, void * cookie)
dove: task, come già detto, è l’indirizzo del descrittore del task Xenomai e
entry è l’indirizzo del corpo della routine del task contenente le istruzione.
Anche in questo caso, se la routine ha avuto successo viene restituito il
valore intero zero. Dopo aver chiamato la procedura in questione, il task può
essere schedulato per la prima volta.
CAPITOLO 2. XENOMAI
50
Essendo queste due funzioni strettamente correlate è possibile ottenere
lo stesso risultato in un solo step, utilizzando la funzione rt_task_spawn()
la quale richiede, ovviamente, un numero maggiore di parametri.
Come accennato le procedure rt_task_start() e rt_task_spawn() vogliono come parametro anche l’indirizzo della routine contenente le istruzioni
che il processo deve svolgere. Quindi, in base alle ipotesi fatte per il nostro
test, dovremo far sì che i due task creati abbiano lo stesso periodo e, inoltre,
che un task occupi la CPU per il 33, 3% e l’altro per il 66, 6%.
E’ possibile rendere periodico un task richiamando la funzione:
int rt_task_set_periodic (RT_TASK * task, RTIME idate, |
RTIME period)
dove:
• task è l’indirizzo del descrittore del task di cui si vuole settare il
periodo;
• idate è la data (assoluta) di esecuzione della prima istruzione, in altre
parole costrituisce il ritardo affinchè la prima istruzione del processo venga eseguita. Nel nostro caso abbiamo impostato tale valore a
TM_NOW in modo da non avere alcun ritardo iniziale;
• period rappresenta il periodo del task, espressa in nanosecondi o in
clock ticks.
E’ possibile eseguire le azioni in più periodi utilizzando, per esempio,
il ciclo iterativo for(;;), all’interno del quale deve essere richiamata, come
ultima istruzione la funzione:
int rt_task_wait_period(NULL)
la quale mette il task nello stato di attesa del prossimo periodo, a discrezione
dello scheduler di Xenomai.
All’interno del ciclo iterativo for(;;), per simulare l’uso della CPU da
parte del task, abbiamo utilizzato la semplice funzione:
void rt_timer_spin (RTIME ns)
che tiene occupata la CPU per un certo numero di cicli non compiendo alcuna
azione, ossia «brucia» cicli di CPU. Infatti, l’unico parametro richiesto dalla
routine è proprio il tempo di impiego della CPU espresso in nanosecondi o
in numero di cicli della cpu (clock ticks).
CAPITOLO 2. XENOMAI
51
E’ possibile cancellare un task, in modo tale che tutte le risorse del kernel assegnategli al momento della creazione vengano rilasciate chiamando la
funzione:
int rt_task_delete (RT_TASK * task)
E’ importante sottolineare l’uso della funzione rt_printf() al posto della printf() [16], Infatti, tutti i thread Xenomai vengono avviati in modalità
primaria (nucleo RT) ma, invocando una primitiva non RT, in questo caso
la printf(), il thread viene spostato nella coda dei processi di Linux, mantenendo la stessa priorità, cioè si ha un passaggio in modalità secondaria.
Così facendo però, il thread entra in competizione con i task di Linux, di
conseguenza l’ordine di esecuzione dei processi potrebbe essere diverso da
quello che ci aspettiamo.
Come test successivo abbiamo pensato di rendere meno uniforme e più
dinamica la creazione dei vari task, generando casualmente il numero di
processi creati (al massimo 10), il tempo computazionale (ci ) e il periodo
(ti ) di ogni task. Per far questo abbiamo utilizzato la coppia di funzioni note
srand() e rand().
Lo sviluppo di questi test è stato fatto sia in user-space sia in kernel-space,
evidenziando le seguenti differenze:
• nel contesto di esecuzione user-space non è possibile settare una C/T
globale pari a 1, poiché bisogna considerare delle latenze dovute al
cambio di contesto dei due task. L’occupazione massima della CPU
che si è raggiunta empiricamente è del 95%;
• in kernel-space ci si può spingere a un limite del 99%, oltre il quale si
provoca un crash del nano-kernel di Xenomai.
Risulta interessante notare che utilizzando lo script in user-space, l’utente
resta in grado di controllare i dispositivi di input come touchpad e tastiera, sebbene con notevoli rallentamenti. Nel caso del modulo kernel-space,
questo è impossibile poichè, come già visto, ci si può spingere verso limiti di
computazione più elevati, eliminando la possibilità di entrare in esecuzione
alle routine di controllo di tali dispositivi.
Capitolo 3
Cris AXIS ETRAX 100LX
3.1
3.1.1
La FOX Board
Panoramica
La FOX Board [12] utilizzata per il porting del sistema RT Xenomai, (Fig.
3.1) è una scheda madre prodotta dalla Acme Systems di ridotte dimensioni
e bassi consumi con un microprocessore ETRAX 100LX, a 100MIPS RISC
CPU progettato dalla Axis Communications.
La scheda possiede le seguenti caratteristiche tecniche:
Caratteristiche Hardware
• 1 microprocessore RISC con architettura Axis ETRAX 100LX MCM
4+16 (32 bit, 100 MHz di frequenza di clock) prodotto da Axis Communications;
• 4 MB di memoria flash e 16 MB di memoria RAM (per questo è anche
denominata "LX416");
• 1 porta Ethernet a 10/100 Mb/s;
• 2 porte USB 1.1;
• 1 porta seriale TTL a 3,3 Volt per consolle;
• 2 connettori di espansione con segnali per interfacce IDE, SCSI, porte
seriali, linee di I/O e bus I2C;
• alimentazione singola a 5 Volt, 280 mA (equivalente a circa 1 W di
potenza);
52
CAPITOLO 3. CRIS AXIS ETRAX 100LX
53
Figura 3.1: FOX Board
• dimensioni: 66x72x19 mm;
• peso: 37g;
• range temperature: 0 – 70 °C.
Caratteristiche Software
• Linux kernel 2.4 (upgrade al 2.6 disponibile);
• WEB server, FTP server, SSH, TELNET e PPP preinstallati;
• supporto ai convertitori usb/seriali basati su chip FTDI;
• supporto alle chiavi di memoria USB;
• ambiente di sviluppo (SDK) Open Source per sistemi Linux;
• compilatore GNU C1 con interfaccia web disponibile gratuitamente
su Internet per la compilazione di piccole applicazioni senza dover
installare l’SDK.
1
il gcc (GNU Compiler Collection, in origine GNU C Compiler) è un compilatore multitarget creato inizialmente dal fondatore della Free Software Foundation, Richard Matthew
Stallman, come parte del Progetto GNU. Le versioni recenti sono incluse nelle principali
distribuzioni del sistema operativo GNU/Linux, e di molti altri sistemi.
CAPITOLO 3. CRIS AXIS ETRAX 100LX
54
Questa scheda è adattabile a moltissimi utilizzi, che spaziano dal semplice lettore MP3 a veri e propri web server, ed è espandibile grazie ai componenti hardware aggiuntivi (quali per esempio display, espansioni di memoria,
antenne GSM/GPRS) messi in commercio da Acme Systems e da altre ditte.
La scheda è progettata espressamente per ospitare un sistema operativo
Linux. Infatti, viene fornita con una immagine del kernel Linux pronta per
essere eseguita. Come è noto, il kernel di questo sistema operativo offre un
elevato grado di adattabilità.
3.1.2
Accensione
Per funzionare la scheda necessita di una tensione continua pari a 5 Volt,
che gli può essere fornita collegando un alimentatore commerciale di tipo
PS5V1A al connettore J14 o collegando un alimentatore tipo quello dei floppy
disk al connettore J2 (Fig. 3.2).
Figura 3.2: Alimentazione attraverso il connettore J14 o J2
Una volta alimentata, i tre led presenti, DL1, DL2 e DL3 si illuminano
per un breve periodo di tempo, dopo di che rimane acceso solo il led verde.
Il significato dei tre led è il seguente:
• Led rosso DL1, può essere usato dall’utente per controllare le applicazioni software. Normalmente è spento. All’avvio viene utilizzato
dal kernel per indicare un errore sulla board. Tipicamente lampeggia
quando l’indirizzo MAC della scheda Ethernet non è configurato (que-
CAPITOLO 3. CRIS AXIS ETRAX 100LX
55
sta condizione si verifica solo dopo la riprogrammazione della memoria
flash con il comando boot_linux -F, cioè quando il kernel è stato
riprogrammato cancellando tutte le informazioni del «boot block»);
• Led giallo DL2, mostra il traffico LAN sul connettore ethernet;
• Led verde DL3, è collegato direttamente alla rete di alimentazione e
di conseguenza mostra lo stato acceso/spento della board.
3.1.3
Modalità di accesso alla FOX Board
La FOX Board ha un connettore femmina LAN RJ45 per la connessione alla
rete Ethernet 10/100 Mbit (J11). E’ possibile collegare direttamente un PC
attraverso un cavo Ethernet incrociato. La configurazione di default della
rete sulla FOX Board è:
• indirizzo IP: 192.168.0.90
• netmask: 255.255.255.0
• default gateway: 192.168.0.1
Quindi per vedere la scheda tramite TCP/IP dal nostro PC, bisogna che
questo sia configurato sullo stesso pool di indirizzi LAN, cioè l’indirizzo IP
del PC deve appartenere o al range da 192.168.0.2 a 192.168.0.89 o al range
da 192.168.0.91 a 192.168.0.254.
Vi sono principalmente tre modalità per utilizzare il sistema Linux presente sul sistema embedded.
3.1.3.1
Web access
La FOX Board ha preinstallato di default il web server BOA2 . Se il PC si
trova sulla stessa LAN della piattaforma embedded, come descritto sopra,
digitando il seguente URL: http://192.168.0.90 all’interno di un browser,
comparirà la schermata in figura (Fig. 3.3).
La home page predefinita si trova su /usr/html/index.html. Questo file
viene salvato in una cartella di sola lettura, quindi non è possibile modificarne
il contenuto. Per cambiare la home page predefinita o per aggiungerne altre,
è necessario utilizzare la cartella di lettura/scrittura /usr/html/local.
2
è un webserv http elementare ma efficace, in grado di offrire le funzionalità tipiche
di questo servizio. Si compone fondamentalmente di un demone, boa, e di un file di
configurazione, /etc/boa/boa.config.
CAPITOLO 3. CRIS AXIS ETRAX 100LX
56
Figura 3.3: Home page FOX Board
3.1.3.2
FTP access
Il sistema embedded ha un server FTP sempre attivo che opera sulla porta
21. Se la board è correttamente visibile sulla LAN, digitando il seguente
comando all’interno della shell:
ftp 192.168.0.90
verrà chiesto di inserire l’username e la password, che sono rispettivamente
root e pass (vedi Fig. 3.4).
Figura 3.4: ftp access
Una volta effettuato il login è possibile per esempio navigare all’interno
CAPITOLO 3. CRIS AXIS ETRAX 100LX
57
del file system della board o impostare la modalità binaria per il traferimento
dei file.
3.1.3.3
TELNET access
La FOX board ha un server Telnet sempre attivo e in ascolto sulla porta 23,
che consente l’accesso remoto da PC. Per l’accesso Telnet è possibile utilizzare il software gratuito PuTTY, il quale supporta anche sessioni SSH, che
a differenza del Telnet sono protette. Al fine di utilizzare Putty è necessario
deselezionare l’opzione «Return key sends Telnet New Line instead of M »
nel pannello di configurazione di PuTTY, come mostrato in Fig. 3.5.
Figura 3.5: Pannello di configurazione Telnet di PuTTY
3.2
Cris ETRAX
Il cuore della FOX Board è una CPU Axis ETRAX 100LX. Questo chip
è stato progettato e realizzato dalla Axis Communications per l’impiego in
sistemi embedded del sistema operativo Linux (LX sta per Linux).
CAPITOLO 3. CRIS AXIS ETRAX 100LX
58
La sua architettura è nota negli ambienti Linux come un’architettura
CRIS che è l’acronimo di Code Reduced Instruction Set. Mentre ETRAX è
un acronimo delle caratteristiche del chip: Ethernet, Token Ring, AXis.
Le informazioni seguenti (sottosezione 3.2.1) rappresentano una piccolissima parte del manuale «AXIS ETRAX 100LX Designer’s Reference»,
per maggiori dettagli e chiarimenti si consiglia di consultare direttamente
il documento [14].
3.2.1
CPU RISC
La CPU della ETRAX 100LX è una CPU RISC a 32-bit con istruzioni a
16-bit, compatibile con l’architettura CRIS. Funziona con una frequenza di
clock di 100 MHz e può raggiungere una velocità di picco pari a 100 MIPS.
3.2.1.1
Registri
Il processore contiene 14 General Registers a 32-bit (R0-R13), uno Stack
Pointer a 32-bit (R14 o SP) e un Program Counter a 32-bit (R15 o PC)
(Fig. 3.6).
L’architettura del processore comprende anche 16 Special Registers (P0P15), 10 dei quali sono definiti (Fig. 3.7).
Figura 3.6: General Registers
CAPITOLO 3. CRIS AXIS ETRAX 100LX
59
Figura 3.7: Special Registers
3.2.1.2
Flag e Condition Code
Il Condition Code Register (CCR) e la sua estensione a 32 bit, il Dword
Condition Code Register (DCCR), contengono 11 diversi flag, mentre i bit
rimanenti sono sempre a zero (vedi Fig. 3.8).
3.2.1.3
Organizzazione dei dati in memoria
I tipi di dati supportati dalla CRIS sono:
• byte, intero 8 bit;
• word, intero 16 bit;
• dword, intero o indirizzo 32 bit.
Ogni locazione indirizzabile contiene dati di un byte. Questi sono archiviati in memoria con il byte meno significativo all’indirizzo più basso (little
indian). La CPU CRIS nella ETRAX 100LX ha una bus dati a 32-bit.
La conversione da 32 bit a 16 bit, se necessaria, viene eseguita mediante
l’interfaccia del bus.
Se i dati superano i 32-bit, la CPU dividerà l’accesso ai dati in due accessi
separati. Perciò, l’uso di parole non allineate e dword possono degradare le
prestazioni.
CAPITOLO 3. CRIS AXIS ETRAX 100LX
60
Figura 3.8: CCR e DCCR
Le figure seguenti mostrano un esempio di organizzazione dei dati con
un bus a 16 bit Fig. 3.9 e con un bus a 32 bit Fig. 3.10.
Figura 3.9: Esempio di organizzazione dei dai con un bus a 16 bit
3.2.1.4
Formato delle istruzioni
L’istruzione base ha la lunghezza di una dato word, cioè 16-bit. Le istruzioni
devono essere 16 bit allineati.
Quando la CPU recupera 32 bit, contenenti due istruzioni allineate a 16
bit, salva i due byte superiori in un registro interno di prefetch. Dopodiché, la
CAPITOLO 3. CRIS AXIS ETRAX 100LX
61
Figura 3.10: Esempio di organizzazione dei dati con un bus a 32 bit
CPU effettuerà una lettura per ogni seconda istruzione durante l’esecuzione
del codice consecutivo.
La maggior parte delle istruzioni hanno la fisionomia generale in Figura
3.11.
Figura 3.11: Fisionomia generale di un’istruzione
3.2.2
Gestione degli interrupt su Linux/CRIS
E’ molto importante ai fini del porting della patch Adeos, capire come il
kernel Linux sull’architettura CRIS gestisca le interruzioni [17].
Si ricordi che la funzione principale del livello di astrazione hardware
(HAL) è quella di prendere gli interrupt dall’hardware e se necessario inoltrarli a Linux o al gestore real-time.
3.2.2.1
Interrupt Paths
Nella Fig. 3.12, vengono mostrati i percorsi di gestione dei vari interrupt del
kernel Linux su architettura CRIS.
Nella maggior parte dei casi un interrupt causa l’esecuzione della routine
IRQxx_interrupt (queste routine vengono inserite nel vettore degli interrupt
ogni volta che un gestore dell’interrupt «xx» viene installato, per esempio,
da un driver hardware).
La prima istruzione della routine consiste nel salvare i registri e disabilitare gli interrupt (poichè non vengono disabilitati automaticamente). Do-
CAPITOLO 3. CRIS AXIS ETRAX 100LX
Figura 3.12: Interrupt handling paths in the Linux kernel on CRIS
62
CAPITOLO 3. CRIS AXIS ETRAX 100LX
63
podiché l’interrupt xx viene mascherato. Se questo non fosse mascherato e
il sistema potesse ricevere nuovi interrupt. prima che quello gestito venga
riconosciuto, si entrerebbe in un loop infinito.
Viene chiamata poi la routine-C, do_IRQ, passandogli come parametro
il numero dell’interrupt. Questa funzione esegue il gestore dell’interruzione,
inoltre dovrebbe anche riconoscere l’interrupt. Normalmente le interruzioni
vengono disattivate durante l’esecuzione dell’handler, ma questo dipende
da come il gestore è stato installato. La ricezione degli interrupt potrebbe
rimanere attiva mentre si esegue l’handler. Questa soluzione è possibile se
l’interrupt è stato precedentemente mascherato.
Quando il gestore ha terminato, la routine IRQxx_interrupt continua e
infine smaschera l’interrupt.
Il percorso appena descritto è il più importante da capire, ma è anche
necessario vedere cosa accade dopo. Alcune routine meritano una spiegazione:
• ret_from_intr, controlla se il processore era in user o in kernel (supervisor) mode quando si è verificato l’interrupt. User-mode significa che
era in esecuzione un’applicazione in user-space e kernel-mode che era
in esecuzione qualche codice del kernel, come un driver. In user-mode
si ha un percorso più lungo;
• _ret_with_reschedule, controlla se il processo corrente ha qualche signal pendente o è necessaria una schedulazione. In modo tale che venga
inoltrata la routine appropriata;
• _Rexit, è responsabile del ripristino dei registri precedentemente salvati e del ritorno al codice interrotto (dal processore quando si è verificato
l’interrupt). Attraverso questa routine, tutti i gestori delle interruzioni
si concludono «con il ritorno».
3.2.2.2
Casi speciali
• IRQ 0: Interruzioni hardware (hwbreakpoint) usate solo per il debug;
• IRQ 1: Interruzioni dal watchdog. Se il watchdog è abilitato nella
configurazione del kernel, la routine IRQ1_interrupt visualizza le informazioni di debug e in seguito il chip viene resettato. La routine
può essere chiamata anche per il reset del chip da software, dato che il
watchdog è usato per questo;
CAPITOLO 3. CRIS AXIS ETRAX 100LX
64
• IRQ 2: Per gli interrupt del timer viene usata la routine speciale
IRQ2_interrupt. Questa funziona è simile alla routine IRQxx_interrupt,
solamente che gli interrupt non vengono mascherati. Questo perchè
do_IRQ gestisce anche le interruzioni software pendenti (chiamando
do_softirq) dopo l’attuale gestore di interrupt. do_softirq consente gli
interrupt durante l’esecuzione dei gestori: un nuovo interrupt del timer
può essere processato solo se non è mascherato. L’interrupt del timer
è cruciale e dovrebbe essere sempre processato il più presto possibile, in quanto il watchdog deve essere resettato e l’orologio di sistema
aggiornato;
• IRQ 14: il bus-fault della MMU (Memory Management Unit) si verifica per esempio quando un’istruzione cerca di accedere a un indirizzo
di memoria per il quale la traduzione da indirizzo virtuale a fisico non
è memorizzata nella TLB. Quando si verifica un bus-fault viene chiamata la ruotine mmu_bus_fault. Questa salva i registri e disabilità gli
interrupt come la procedura generale. Viene poi chiamata la routine-C
handle_mmu_bus_fault, che si suppone risolva il bus-fault, qualunque esso sia. Al ritorno viene inoltrata ret_from_intr, ma invece di
_Rexit è usata la funzione speciale _RBFexit. Essa riprista i registri
e riavvia l’istruzione che ha causato il fault;
• IRQ 15: Le interruzioni multiple si verificano quando ci sono diversi
interrupt attivi allo stesso tempo. La routine multiple_interrupt, inizialmente, salva i registri e disabilita gli interrupt; in seguito processa
il primo interrupt in attesa, chiamando la routine IRQxx_interrupt
con una scorciatoia. La scorciatoia può essere percorsa dato che i registri sono stati salvati. L’interrupt viene poi trattato in modo normale.
Se vi sono altri interrupt attivi, verrà chiamata di nuovo la routine
multiple_interrupt.
3.3
3.3.1
Software Development Kit (SDK)
I sistemi embedded e la cross compilazione
I sistemi embedded, a causa delle limitate risorse di cui dispongono, non
possono ospitare ambienti di sviluppo. Non è possibile eseguire su di essi
nessun IDE, editor di testo, compilatori e debugger. Tuttavia è necessario
scrivere applicativi per questi sistemi.
Il problema viene risolto utilizzando una macchina host opportunamente
configurata per generare il codice oggetto, che sarà poi trasferito ed eseguito
sulla macchina target, il sistema embedded.
CAPITOLO 3. CRIS AXIS ETRAX 100LX
65
Il processo relativo alla costruzione di un programma su di un sistema
host, per poter poi essere eseguito su di un sistema target, è chiamato cross
compilazione, il cui elemento fondamentale è il cross compilatore.
Il gcc è stato portato praticamente su tutti i principali sistemi: per ognuno di essi è stato configurato per produrre dei binari ottimizzati per quella
particolare architettura. Il gcc è il cross compilatore ottimale da utilizzare
per costruire una cross toolchain.
La costruzione di un cross compilatore e di una cross toolchain non sono operazioni semplici. Per effettuare una cross compilazione non è sufficiente disporre di un cross compilatore, configurato e ottimizzato per una
particolare architettura hardware.
Sono necessarie anche una serie di utility, che a loro volta devono essere
costruite, ottimizzate e configurate per poter contribuire alla cross compilazione per quella particolare architettura. Il cross compilatore richiede il
supporto delle librerie C e di altri eseguibili, come il linker, l’assembler e il
debugger.
L’insieme dei tool, delle librerie e dei programmi usati per la cross compilazione si chiama cross platform toolchain, o toolchain in breve. Tutte le
macchine atte alla compilazione di codice dispongono di una toolchain.
Se gli eseguibili prodotti sulla macchina host dovessero girare su una
macchina target, dotata di una architettura simile all’host, allora la toolchain
è detta nativa.
Se macchina host e macchina target hanno differenti architetture, allora
la toolchain è detta cross platform.
La cross toolchain può essere costruita a mano o si possono utilizzare
dei tool (crosstoll, buildroot, crossdev) che cercano di automatizzare tutto il
processo di costruzione della toolchain.
Il principale problema che si può incontrare impiegando questi tool è che
potrebbero non funzionare e fallire la costruzione della cross toolchain.
Questa eventualità può accadere soprattutto se si cerca di utilizzare una
versione del compilatore gcc, dei binutils, delle librerie C o del kernel che
ancora non è supportata dal tool. In questi casi l’unica alternativa è quella
di costruire a mano la toolchain. La costruzione della toolchain può rivelarsi
un compito più o meno complicato a seconda dei pacchetti software che si
utilizzeranno.
CAPITOLO 3. CRIS AXIS ETRAX 100LX
66
Per quanto riguarda la CPU a nostra disposizione sulla FOX Board, è
disponibile gratuitamente, sul sito della Axis, un Software Development Kit
(SDK) ufficiale. Inoltre, sono fruibili una serie di ambienti di sviluppo alternativi completamente open source e scaricabili dal sito della Acme Systems
realizzati da utenti appassionati. Per esempio:
• phrozen sdk fatto da John Crispin. Questo SDK storico è stato usato dalla Acme Systems per costruire immagini di default del kernel.
Quando Acme Systems ha sviluppato la prima versione della sua FOX
Board LX, Axis Communications ha rilasciato il suo Axis SDK versione 2.01 basato sulle versioni del kernel 2.4.31 e 2.6.15. A partire da
questa versione, John Crispin, un utente appassionato di FOX Board,
ha rilasciato la sua Phrozen SDK basata su quella dell’Axis SDK, ma
con un sacco di nuovi driver, utility, altri miglioramenti è un menuconfig più facile da usare. Successivamente Crispin non ha proseguito lo
sviluppo di questo ambiente.
Linux kernel 2.6.15 o 2.4.31. Librerie glibc o uclibc.
• cris OS fatto da Claudio Mignani. Questo ambiente si basa sul porting di OpenWRT, originariamente realizzato da John Crispin e ora
fortemente migliorato da Claudio Mignanti. I principali vantaggi di
questo ambiente sono che l’intero file system è sia leggibile sia scrivibile, e l’installazione del pacchetto è molto facile grazie al repository
software on-line in formato binario fatto da Mignani.
Linux kernel 2.6.25.16. Librerie uclibc.
• foXServe SDK realizzato da Davide Cantaluppi. Questo ambiente si
basa sul phrozen sdk migliorato con web server Apache, PHP, WebDAV
e SQLite.
Linux kernel 2.6.15. Libreria glibc. FoXServe funziona solo sulla FOX
Board LX832.
Per quanto riguarda il nostro lavoro di tesi abbiamo scelto il Software
Development Kit ufficiale della Axis [13]. Questo, è costituito da tre parti
principali: il compilatore, il debugger e la distribuzione software (vedi Fig.
3.13).
3.3.2
Installazione e test del cross compilatore gcc-cris
Il primo passo per poter utilizzare l’ambiente di sviluppo per applicazioni
è quello di installare il cross compilatore. L’architettura CRIS, utilizzata
dall’Axis ETRAX 100LX, è supportata dal GNU Compiler Collection (GCC)
CAPITOLO 3. CRIS AXIS ETRAX 100LX
67
Figura 3.13: Schema a blocchi SDK
distribuito dalla Free Software Foundation (FSF). L’assembler, il linker e
altri strumenti CRIS sono inclusi come parte del sorgente GNU Binutils.
Analogamente per la libreria Newlib.
Vi sono due modi per installare il compilatore. Il primo, da noi adottato,
è quello di scaricarare direttamente, dal sito della Axis, il pacchetto binario
pre-compilato cris-dist-1.64-1.i386.deb e installarlo. Mentre la seconda soluzione, più complicata, consiste nel compilare prima il codice sorgente e poi
installare i file binari ottenuti.
Il compilatore viene installato di default nella directory: /usr/local/cris.
E’ importante sottolineare il fatto che l’installazione presenta due compilatori, uno per crisv10 (relativo alla ETRAX 100LX) e uno per crisv32 (relativo alla ETRAX FX). Di conseguenza è necessario selezionare il compilatore
giusto in fase di cross-compilazione.
Per essere certi di utilizzare quello appropriato, lanciando il comando:
/<path to compiler>/bin/gcc-cris --version
dove <path to compiler> di default corrisponde a: /usr/local/cris. Si dovrebbe ottenere la schermata in figura 3.14, per quanto riguarda il compilatore crisv10.
Figura 3.14: Versione gcc-cris
Al fine di verificare il funzionamento del compilatore gcc-cris, abbiamo
scritto una semplicissima applicazione in C, prima compilata ed eseguita sul
CAPITOLO 3. CRIS AXIS ETRAX 100LX
68
sistema host e poi cross-compilata sul sistema host ed eseguita sul sistema
target.
In pratica, abbiamo scritto il codice sorgente (Algoritmo 3.1) in un file
denominato hello.c salvato all’interno della directory v/hello.
Algoritmo 3.1 Codice sorgente applicazione hello.c
#i n c l u d e <s t d i o . h>
i n t main ( i n t argc , c h a r ∗ argv [ ] ) {
p r i n t f ( " H e l l o World ! \ n " ) ;
return 0;
}
Poi abbiamo compilato l’algoritmo per il sistema host con il seguente
comando:
gcc -o hello_host hello.c
Il quale ha prodotto, all’interno della stessa directory del file oggetto, il
file binario, hello_host, chiaramente eseguibile sul sistema host. Mandando in esecuzione il file ottenuto, sarà possibile visualizzare sullo schermo la
semplice stringa «Hello World!».
Quanto detto finora non è niente più che la classica compilazione di un’applicazione in C. Successivamente abbiamo poi cross-compilato lo stesso file
sorgente, hello.c per il sistema target.
Il comando che ci permette di ottenere il file eseguibile sul sistema embedded è il seguente:
gcc-cris -mlinux -o hello_target v/hello/hello.c
Ciò è fattibile nel caso in cui si abbia precedentemente aggiunto il percorso del compilatore gcc-cris alla variabile d’ambiente PATH con il comando:
export PATH=$PATH:/usr/local/cris/bin
Dal momento che il file binario, hello_target, non può essere eseguito sul
sistema host, come ci aspettavamo, lo abbiamo caricato sul sistema embedded per verificare che l’esecuzione produca gli stessi risultati. Ciò può essere
ottenuto tramite FTP o SCP. Naturalmente, il sistema embedded deve supportare il metodo di trasferimento scelto. Inoltre, il file eseguibile può essere
scritto solo su un file system scrivibile.
Utilizzando il caricamento sul sistema embedded tramite FTP bisogna
eseguire i seguenti comandi. Per prima cosa occorre connettersi alla board
CAPITOLO 3. CRIS AXIS ETRAX 100LX
69
digitando il comando:
ftp <ip address>
che come abbiamo visto richiedererà l’username e la password.
Successivamente bisogna assicurarsi di trasferire il file in modalità binaria
e non in modalità ASCII. Per far questo basta lanciare il comando:
ftp> binary
L’eseguibile può essere posizionato sia nella partizione flash riscrivibile
(/mnt/flash) sia nella partizione tmpfs (/tmp) nella RAM. Ovviamente, se
si inserisce l’eseguibile nella RAM, esso non sarà disponibile al riavvio del
sistema. Quindi, portiamoci nella directory /mnt/flash con il comando:
ftp> cd /mnt/flash
e carichiamo il file con:
put <file name>
Dal momento che il trasferimento di un file utilizzando FTP non rende eseguibile il file, è necessario impostare manualmente il bit execute. E’
possibile utilizzare il comando chmod dal client FTP:
ftp> chmod 755 <file name>
Infine ci disconnettiamo:
ftp> quit
Poichè la FOX Board possiede due porte USB 1.1, per il trasferimento del
file eseguibile, hello_target, possiamo utilizzare anche una semplice chiavetta
usb.
Una volta caricato il file sulla piattaforma di destinazione, per verificare
se funziona come previsto, ci connettiamo alla board tramite Telnet o ssh
con PuTTY e, dopo aver effettuato il login, ci spostiamo dentro la directory
/mnt/flash con il comando:
cd /mnt/flash
e lanciamo l’applicazione come visto per il sistema host:
./hello_target
Ciò che otterremo sarà di nuovo la stringa «Hello World!».
3.3.3
Distribuzione software
La distribuzione software è il sottoinsieme dell’ambiente di sviluppo utilizzato dalla Axis, che comprende tutto il codice sorgente e i file del sistema di
compilazione, necessari a produrre un’immagine del firmware di Linux per
l’Axis ETRAX.
CAPITOLO 3. CRIS AXIS ETRAX 100LX
70
Il sistema di compilazione permette una configurazione semplice e flessibile delle opzioni hardware e delle applicazioni software. Inoltre, è disponibile
un certo numero di configurazioni, predefinite per i sistemi più comunemente usati. Le caratteristiche del firmware di destinazione (Linux 2.6) sono:
pieno supporto al MMU, libreria standard glibc o uClibc, librerie condivise,
thread Linux e POSIX e varie applicazioni. Il kernel Linux include, inoltre,
driver per le interfacce della ETRAX come Ethernet, porte seriali e parallele,
general purpose I/O, USB e IDE.
Un elenco completo delle librerie e delle applicazioni incluse nel Software
Development Kit è reperibile all’indirizzo Internet http://developer.axis.
com/wiki/doku.php?id=axis:sw-list.
Il passo successivo, dopo aver installato il cross compilatore, per avere
un SDK «completo» (senza Debugger) è quello di installare la distribuzione
software (la versione più recente è la 2.20). Prima di far questo però bisogna
verificare che tutti i requisiti richiesti dalla distribuzione siano soddisfatti.
Elenco requisiti:
• OS: qualsiasi distribuzione Linux abbastanza nuova dovrebbe funzionare;
• interfaccia di rete Ethernet;
• accesso root;
• compilatore C gcc;
• GNU make versione 3.80 o 3.81;
• GNU wget;
• CRISS cross-compilatore;
• awk (o gawk);
• bc;
• byacc (o yacc se byacc è un link a esso);
• lex o flex;
• perl;
• sed;
• tar;
CAPITOLO 3. CRIS AXIS ETRAX 100LX
71
• zlib (per esempio zlib1g e zlib1g-dev);
• md5sum;
• curses o ncurses (per esempio libncurses5 e libncurses5-dev);
• bison;
• which.
La distribuzione software è composta da un pacchetto di installazione e
da un insieme di pacchetti distribuiti. Può essere installata in due modi:
installazione normale e installazione tarball. L’unica differenza tra le due
procedure riguarda la modalità di accesso ai pacchetti distribuiti, in quanto
entrambe richiedono il pacchetto di installazione.
L’installazione normale necessita di una connessione Internet sul computer dove si installa la distribuzione software. Mentre, con l’installazione
tarball si scaricano tutti i tarball supplementari (pacchetti .tar) che riguardano i pacchetti distribuiti per essere poi utilizzati su un sistema target. Noi
abbiamo optato per l’installazione normale.
In pratica, durante il processo di installazione vengono scaricati tutti i
pacchetti software richiesti. Questo riduce la quntità di spazio occupato dalla
distribuzione software installata, nonché la quantità di contenuto scaricato.
Ovviamente, sul computer è necessaria una connessiona a Internet.
Come prima cosa, bisogna scaricare il pacchetto di installazione devboardR2_20.tar.gz dal sito della Axis. Il pacchetto contiene, tra le altre cose, lo
script di configurazione per l’installazione della distribuzione.
Dopo aver scaricato il file è necessario estrarlo con il comando:
tar xvfz devboard-R2_20.tar.gz
L’estrazione crea una directory devboard-R2_20. A questo punto bisogna
entrare nella directory appena creata ed eseguire lo script di configurazione.
Questo scaricherà e istallerà gli strumenti e il codice sorgente necessario per
configurare la distribuzione software.
cd devboard-R2_20
./configure
Lo script di configurazione chiederà la tipologia del prodotto per il quale
si vuole costruire il firmware. La selezione che si effettua determina i file di
configurazione che verranno utilizzati. Nel nostro caso abbiamo selezionato
la tipologia di prodotto «fox_416». In alternativa, è possibile impostare il
CAPITOLO 3. CRIS AXIS ETRAX 100LX
72
tipo di prodotto settando la variabile DEV_BOARD_PRODUCT=<product
name> prima di eseguire lo script di configurazione.
Successivamente viene chiesto se si desidera personalizzare il firmware o
mantenere la configurazione predefinita per la tipologia di prodotto selezionata. E’ consigliabile configurare la distribuzione del software, dal momento che la configurazione predefinita include solo un sottoinsieme di tutti i
pacchetti disponibili. Immettendo y, la selezione dei pacchetti può essere
configurata prima che questi vengano scaricati, risparmiando tempo e spazio
sul disco.
Questa domanda viene fatta solo la prima volta che si installa la distribuzione software, anche se questa può essere configurata successivamente.
3.3.4
Configurazione e compilazione del firmware
Per selezionare quali pacchetti vogliamo includere o togliere nel firmware,
una volta recati all’interno della directory devboard-r2_20, bisogna eseguire
il comando:
make menuconfig
Ciò che otteniamo nella shell è un menù grafico suddiviso per categorie,
in modo da rendere la selezione dei pacchetti abbastanza semplice e logica.
Nel nostro caso, oltre alla configurazione di default, abbiamo abilitato il
supporto dropbear ssh 3 e il supporto a utelnetd 4 .
In alternativa a questo comando possono essere eseguiti make xconfig o
make config.
E’ importante salvare la configurazione prima di uscire dall’utility.
Fatto ciò, bisogna eseguire lo script ./configure per installare i pacchetti
richiesti dalla configurazione. Questi genererà il Makefile di primo livello che
viene utilizzato per compilare l’immagine del firmware.
Ora basta lanciare il comando make dopodiché occorre attendere (richiede
abbastanza tempo) che il processo di creazione dell’immagine del firmware
termini.
3
Secure SHell, è un protocollo di rete che permette di stabilire una sessione remota
cifrata tramite interfaccie a riga di comando con un altro host di una rete informatica. E’
il protocollo che ha sostituito l’analogo ma insicuro Telnet.
4
è un protocollo di rete utilizzato su Internet. L’obiettivo del procollo Telnet è fornire
un supporto per le comunicazioni sufficientemente generalizzato, bidirezionale e orientato
ai byte (8 bit). E’ solitamente utilizzato per fornire all’utente sessioni di login remoto di
tipo riga di comando tra host su Internet.
CAPITOLO 3. CRIS AXIS ETRAX 100LX
73
Per quanto riguarda la versione 2.20 della distribuzione software, questa
produrrà una immagine basata sulla versione 2.6.26 del kernel Linux.
3.3.5
Scrittura del firmware sulla board
Poichè sulla piattaforma embedded a nostra disposizione vi era un kernel
Linux 2.4, mentre con la distribuzione software 2.20 si ottiene un’immagine
del kernel Linux 2.6.26, abbiamo aggiornato la board con questo firmware
compilato precedentemente.
Come si può notare dalle caratteristiche tecniche del sistema embedded,
sulla board vi è una memoria FLASH in cui è possibile archiviare tutti i file
di programma necessari per il corretto funzionamento del sistema stesso. In
questa memoria permanente viene salvato il sistema operativo Linux e tutte
le applicazioni e i dati di cui necessita la board. In pratica corrisponde al
disco rigido di un PC.
E’ importante ricordare che non c’è bisogno di ricompilare una nuova
immagine del firmware per avere la FOX Board con le nostre applicazioni.
E’ sufficiente, invece, nella maggior parte dei casi, trasferire semplicemente
le nostre applicazioni tramite FTP o SSH direttamente nel file system della
scheda in una zona riscrivibile della memoria flash (/mnt/flash).
Uno dei metodi di reflashing della scheda, chiamato «local network flashing», si basa su un piccolo codice ROM all’interno del processore AXIS
che viene eseguito all’accensione quando il jumper J8 è chiuso.
Questo codice carica nella memoria cache interna i pacchetti che gli vengono inviati dal PC attraverso la LAN. L’Axis eseguirà progressivamente i
frammenti di codice contenuti in questi pacchetti e di conseguenza inizierà
a interagire con il PC per scaricare la procedura di programmazione scelta
per la memoria flash.
Tale metodo funziona solo su una rete locale e non supporta alcun routing
IP. Inoltre richiede che il PC e la FOX Board siano sulla stessa rete locale
(vedi 3.1.3).
All’interno della directory principale SDK ( v/devboard-R2_01/ ) occorre digitare:
.
init_env
uno script che viene creato quando si lancia il comando make. Lo scopo di
questo file è quello di settare tutte le variabili d’ambiente necessarie alla di-
CAPITOLO 3. CRIS AXIS ETRAX 100LX
74
stribuzione software. Come regola si dovrebbe sempre richiamare il seguente
script prima di iniziare a lavorare con l’SDK in una nuova shell.
Dopo init_env, bisogna lanciare il comando:
boot_linux -F -i <image_filename>
...
In seguito otterremo un messaggio come quello in Figura 3.15.
Figura 3.15: Network flashing
In questo momento si deve spegnere la FOX Board, chiudere il jumper
J8 (Ethernet flashing) sulla scheda (vedi Fig. 3.16)
Figura 3.16: Ethernet flashing
e riaccenderla di nuovo (così facendo la FOX Board dovrebbe essere sulla
stessa LAN del PC avendo lanciato il comando boot_linux).
Se la procedura risulta corretta, vedremo il processo di trasferimento del
file immagine nella memoria FLASH della scheda con messaggi come quelli
in Figura 3.17.
Durante la programmazione il LED rosso (DL1) della scheda rimane
sempre acceso.
Al termine della fase di programmazione della FOX Board essa si riavvia
automaticamente. Bisogna tuttavia ricordarsi di rimuovere il jumper J8
altrimenti la scheda carica ancora i pacchetti dalla rete e non si avvia.
CAPITOLO 3. CRIS AXIS ETRAX 100LX
75
Figura 3.17: Ethernet flashing
Alcune opzioni utilizzabili con il comando boot_linux:
• -d «dispositivo», interfaccia di reta da utilizzare, eth0 di default;
• -f, salva l’intera immagine del firmware sulla memoria flash tranne che
la partizione di recupero;
• -F, salva l’intera immagine del firmware sulla flash. Inoltre questa
operazione sovrascrive i parametri memorizzati nella partizione di recupero, come il numero di serie e l’indirizzo MAC;
• -h, visualizza informazioni e opzioni aggiuntive non mostrate in questo
elenco;
• -i «image», il percorso e il nome dell’immagine da usare;
• -i, visualizza i risultati del comando «etraxboot» invece di eseguirlo.
Capitolo 4
Porting
4.1
Approccio al problema del porting
In linea di massima, per effettuare il porting di Linux con Xenomai basta
scaricare il file della patch Adeos pipeline relativo all’architettura di destinazione e applicarlo a un kernel Linux vanilla scaricabile da Internet, la cui
versione sia compatibile con quella della patch. A volte è necessario effettuare
qualche ulteriore «piccolo» adattamento.
Dopodiché bisogna cross compilare opportunamente il kernel patchato e
installarlo sul rispettivo sistema embedded.
Quanto al sistema target utilizzato nella tesi, le cose si complicano notevolmente poiché l’architettura CRIS della CPU contenuta nella ETRAX
100LX non è supportata dalla patch Adeos, vale a dire che la patch della
pipeline non esiste per la nostra architettura.
Consapevoli di questo abbiamo optato per la seguente soluzione: abbiamo
scaricato una patch Adeos relativa a un’architettura supportata, come la
ARM cercando di modificarla e adattarla all’architettura Cris.
Tenuto conto che la distribuzione software dell’ambiente di sviluppo da
noi utilizzato e descritto nel capitolo precedente produce un’immagine del
kernel Linux relativo alla versione 2.6.26, abbiamo scaricato dal sito http:
//download.gna.org/adeos/patches/v2.6/arm/older/ la patch per ARM
adeos-ipipe-2.6.26-arm-1.12-00.patch rilasciata il 13 Gennaio 2009.
76
CAPITOLO 4. PORTING
4.2
77
Adeos patch per architettura CRIS
Da una prima analisi generale della patch Adeos per ARM «scaricata», abbiamo osservato che questa interessava, tra modifiche e file completamente
nuovi, più o meno 145 file. Di conseguenza, al fine di rendere il lavoro più
comprensibile, abbiamo diviso la patch in parti più piccole, ognuna riferita
a una categoria di file.
Inizialmente abbiamo isolato in una patch indipendente tutte le modifiche
che andavano ad agire solamente sui file del kernel Linux 2.6.26.
Questo sottoinsieme di modifiche della patch originale lo abbiamo chiamato: adeos-ipipe-2.6.26-generic-1.12.patch (per l’elenco completo dei file modificati vedi sezione 4.2), mentre la parte restante è stata rinominata: adeos-ipipe-2.6.26-cris-specific-1.0.patch, per distinguerla dalla patch
completa originale .
Abbiamo poi testato se le modifiche della patch generic venivano applicate regolarmente sui file del kernel 2.6.26 presente all’interno dell’ambiente
di sviluppo fornito dalla Axis. A tal fine, attraverso la shell, ci siamo spostati all’interno della directory contenente i sorgenti del kernel (v/devboardr2_20/os/ ), utilizzando il terminale, e abbiamo applicato la patch con il
comando «omonimo».
Prima di applicare veramente le modifiche ai file conviene eseguire il
comando patch con l’opzione --dry-run, il quale permette di «simulare»
l’applicazione della patch in modo da rendersi conto se le modifiche saranno
applicate o no e, in caso negativo, conoscere quali file creano problemi.
La sequenza di comandi per fare quanto detto è:
cd <linux_dir>
patch -p1 --dry-run < v/adeos-ipipe-2.6.26-generic-1.12.patch
Poiché nel nostro caso la simulazione è andata a buon fine, come del resto
ci aspettavamo essendo la versione della patch Adeos e la versione del kernel compatibili, abbiamo applicato la patch ai file del kernel, rilanciando il
comando patch senza l’opzione --dry-run dal comando precedente:
patch -p1 < v/adeos-ipipe-2.6.26-generic-1.12.patch
Abbiamo quindi racchiuso tutti i file della patch specific, le cui modifiche riguardavano istruzioni assembly1 , in una patch denominata adeos-ipipe1
o linguaggio assemblatore è, tra i linguaggi di programmazione, quello più vicino al
linguaggio macchina vero e proprio (pur essendo differente rispetto a quest’ultimo). Erroneamente viene spesso chiamato «assembler» anche se quest’ultimo identifica il programma
CAPITOLO 4. PORTING
78
2.6.26-cris-asm-1.0.patch. L’elenco dei file modificati da quest’ultima patch
è:
• /arch/arm/boot/compressed/head.S
• /arch/arm/kernel/entry-armv.S
• /arch/arm/kernel/entry-common.S
• /arch/arm/kernel/entry-header.S
• /arch/arm/kernel/ipipe-mcount.S
• /arch/arm/mm/proc-arm920.S
• /arch/arm/mm/proc-arm926.S
• /arch/arm/mm/proc-xscale.S
• /arch/arm/vfp/vfphw.S
• /include/asm-arm/arch-integrator/entry-macro.S
Quest’ultima patch, chiamata per intenderci «asm», non è stata adattata all’architettura CRIS in quanto il tempo a nostra disposizione è risultato insufficiente al fine di acquisire l’esperienza necessaria per interpretare e
modificare il codice assemly in essa contenuto.
Abbiamo infine «scremato» ulteriormente la patch adeos-ipipe-2.6.26cris-specific-1.0.patch, poiché vi erano alcune modifiche su file riguardanti
particolari architetture della famiglia ARM, per esempio mach-at91, machimx, mach-integrator, mach-pxa, mach-sa1100 che non influenzavano l’architettura CRIS.
La parte della patch che compredeva queste modifiche è stata archiviata, per un eventuale consultazione futura, anche se poteva benissimo essere
cancellata. L’elenco dei file modificati è riportato nella sezione 4.2.
Ricapitolando, la patch Adeos scaricata inizialmente per architettura
ARM, adeos-ipipe-2.6.26-arm-1.12-00.patch, è stata scomposta in tre parti
fondamentali più una quarta patch non considerata.
Le tre patch principali sono:
1. adeos-ipipe-2.6.26-generic-1.12.patch
che converte il linguaggio assembly in linguaggio macchina.
CAPITOLO 4. PORTING
79
2. adeos-ipipe-2.6.26-cris-asm-1.0.patch
3. adeos-ipipe-2.6.26-cris-specific-1.0.patch
Da un’ultima analisi della patch specific, abbiamo visto che c’erano dei file
completamente nuovi, come per esempio /arch/arm/kernel/ipipe.c, il quale
implementa la pipeline della patch Adeos su cui si basa tutto il sistema
Xenomai, utilizzando le funzioni definite nel file /include/asm-arm/ipipe.c.
Il file /include/asm-arm/irqflags.h sostituisce la funzione raw_local_irq_save(x)
che salva lo stato corrente dell’interrupt attivo e disabilita le richieste di altri
interrupt, con local_irq_save_hw_notrace(x). Le funzioni raw_local_irq_enable()
e raw_local_irq_disable() che attivano e disattivano le richieste di interrupt, vengono sostituite rispettivamente con local_irq_enable_hw_notrace()
e local_irq_disable_hw_notrace(), la funzione raw_local_save_flags(x) con
local_save_flags_hw(x).
Tutte le funzioni sovrascritte sono anche state
definite.
Una serie di modifiche va a influenzare file di gestione della memoria, come per esempio /arch/arm/mm/fault.c, /arch/arm/mm/flush.c, /include/asmarm/cacheflush.h e /include/asm-arm/memory.h.
Infine c’erano ancora alcuni file specifici di ARM non riguardanti la nostra
architettura CRIS e sono stati dunque tralasciati, tipo: /arch/arm/mm/alignment.c, /arch/arm/mm/context.c, /arch/arm/mm/copypage-v4mc.c, /arch/arm/mm/copypagexscale.c e b/arch/arm/mm/fault-armv.c.
File modificati dalla patch generic
• /Makefile
• /include/asm-generic/cmpxchg-local.h
• /include/linux/hardirq.h
• /include/linux/ipipe.h
• /include/linux/ipipe_base.h
• /include/linux/ipipe_compat.h
• /include/linux/ipipe_percpu.h
• /include/linux/ipipe_tickdev.h
CAPITOLO 4. PORTING
• /include/linux/ipipe_trace.h
• /include/linux/irq.h
• /include/linux/kernel.h
• /include/linux/linkage.h
• /include/linux/mm.h
• /include/linux/preempt.h
• /include/linux/sched.h
• /include/linux/spinlock.h
• /include/linux/spinlock_types.h
• /init/Kconfig
• /init/main.c
• /kernel/Makefile
• /kernel/exit.c
• /kernel/fork.c
• /kernel/ipipe/Kconfig
• /kernel/ipipe/Kconfig.debug
• /kernel/ipipe/Makefile
• /kernel/ipipe/core.c
• /kernel/ipipe/tracer.c
• /kernel/irq/chip.c
• /kernel/irq/handle.c
• /kernel/lockdep.c
• /kernel/panic.c
• /kernel/power/disk.c
• /kernel/printk.c
• /kernel/sched.c
• /kernel/signal.c
80
CAPITOLO 4. PORTING
• /kernel/spinlock.c
• /kernel/time/tick-common.c
• /kernel/time/tick-sched.c
• /kernel/timer.c
• /incluce/linux/Kconfig.debug
• /incluce/linux/bust_spinlocks.c
• /incluce/linux/ioremap.c
• /incluce/linux/smp_processor_id.c
• /incluce/linux/spinlock_debug.c
• /mm/memory.c
• /mm/mlock.c
• /mm/vmalloc.c
File cancellati dalla patch specific
• /arch/arm/mach-at91/Kconfig
• /arch/arm/mach-at91/Makefile
• /arch/arm/mach-at91/at91_ipipe_time.c
• /arch/arm/mach-at91/at91rm9200.c
• /arch/arm/mach-at91/at91sam9260.c
• /arch/arm/mach-at91/at91sam9261.c
• /arch/arm/mach-at91/at91sam9263.c
• /arch/arm/mach-at91/at91sam9rl.c
• /arch/arm/mach-at91/gpio.c
• /arch/arm/mach-at91/irq.c
• /arch/arm/mach-imx/irq.c
• /arch/arm/mach-imx/time.c
81
CAPITOLO 4. PORTING
• /arch/arm/mach-integrator/core.c
• /arch/arm/mach-integrator/integrator_cp.c
• /arch/arm/mach-ixp4xx/common.c
• /arch/arm/mach-pxa/gpio.c
• /arch/arm/mach-pxa/irq.c
• /arch/arm/mach-pxa/leds-idp.c
• /arch/arm/mach-pxa/leds-lubbock.c
• /arch/arm/mach-pxa/leds-mainstone.c
• /arch/arm/mach-pxa/leds-trizeps4.c
• /arch/arm/mach-pxa/time.c
• /arch/arm/mach-s3c2440/irq.c
• /arch/arm/mach-sa1100/irq.c
• /arch/arm/mach-sa1100/leds-assabet.c
• /arch/arm/mach-sa1100/leds-badge4.c
• /arch/arm/mach-sa1100/leds-cerf.c
• /arch/arm/mach-sa1100/leds-hackkit.c
• /arch/arm/mach-sa1100/leds-lart.c
• /arch/arm/mach-sa1100/leds-simpad.c
• /arch/arm/mach-sa1100/time.c
82
Capitolo 5
Conclusioni
Il lavoro svolto in questa tesi ha messo in evidenza diversi aspetti importanti
riguardo il funzionamento del sistema «ibrido» Xenomai, il quale non costituisce un sistema operativo real-time vero e proprio, ma cerca di combinare
le caratteristiche funzionali di tipo real-time con i vantaggi, in termini di
costi, flessibilità, sviluppo e supporto, offerti dal sistema operativo Linux,
anche nei sistemi embedded.
Dai test svolti sul sistema Xenomai abbiamo potuto verificare come si
comporta lo scheduler RT del sistema per quanto riguarda la gestione dei
processi e i vari passaggi tra il dominio RT e il dominio Linux. Ciò costituisce
una nozione indispensabile per lo sviluppo di eventuali applicazioni, i cui
processi debbono essere eseguiti con stringenti vincoli temporali.
In merito al porting della patch Adeos, «la quale costituisce il nucleo fondamentale del sistema Xenomai», su architettura CRIS, abbiamo realizzato
tre patch. La prima chiamata generic, dato che le modifiche agivano solo
sui file del kernel Linux 2.6.26. La seconda definita asm poichè interessava
solo i file assembly e l’ultima denominata specific, in quanto apportava le
modifiche necessarie della Adeos sui file specifici dell’architettura.
La realizzazione di queste tre patch si è basata sullo studio della patch
Adeos per architettura ARM, poichè la nostra non è supportata da Xenomai.
Come già accennato precedentemente non siamo riusciti a terminare il
porting del sistema Xenomai sulla FOX Board ETRAX 100LX, in quanto
l’implementazione della parte in assembly della patch Adeos richiede molta
esperienza e una cospicua quantità di tempo.
Il lavoro svolto costituisce comunque un buon punto di partenza per
83
CAPITOLO 5. CONCLUSIONI
84
chiunque si affaccia al mondo dei sistemi real-time e più in particolare al
sistema ibrido Xenomai.
Ringraziamenti
Giunto al termine di questo percorso universitario, desidero ringraziare ed
esprimere la mia riconoscenza nei confronti di tutte le persone che, direttamente o indirettamente, mi sono state vicine e mi hanno incoraggiato nei
miei studi e nella stesura di questa tesi.
Il primo pensiero va, ovviamente, a tutta la mia famiglia: i miei genitori,
mio fratello Alberto e la compagna Michela, zia Maria e zio Pino, mio cugino
Giorgio e i miei nonni, in particolare nonno Claudio. Senza il loro aiuto
morale ed economico non avrei mai raggiunto questa meta.
Ringrazio vivamente il Prof. Dragoni che ha reso possibile questo lavoro
di tesi e Andrea Claudi che mi ha seguito e consigliato durante il tirocinio.
Inoltre desidero ringraziare gli amici che ho conosciuto e con cui ho trascorso
quest’ultimo periodo universitario, Andrea, Francesco, Nicola e Marco e tutti
gli altri all’interno del DIIGA.
Voglio ringraziare il Gotha (F. Bertin e G. Quaresima) per avermi aiutato
nella revisione sintattica e grammaticale della tesi.
Infine ringrazio tutti gli amici con cui ho trascorso parte della mia vita,
che hanno reso più sereni e spensierati i momenti di sconforto.
Bibliografia
[1]
Linux
Manual
schedsetscheduler
Page,
[2]
Albero rosso-nero, http://it.wikipedia.org/wiki/RB-Albero
[3]
Embedded Market Study 2010, http://www.techonline.com/
learning/webinar/224000228
[4]
Barbanov M., Yodaiken V., “RealTime Linux”, 1996
[5]
M. Galassi, J. Davies, J. Theiler, B. Gough, G. Jungman, M.
Booth, F. Rossi, “GNU Scientific Library Reference Manual”, Network Theory Ltd, 2nd edition, 2003, disponibile online: http:
//www.gnu.org/software/gsl/manual
[6]
Bovet D. P., Cesati M., «Understanding the Linux Kernel»,
http://linux.die.net/man/2/
O’Reilly 3rd Edition
[7]
Corbet J., Rubini A., Kroah-Hartman G., «Linux Device
Drivers», O’Reilly 3rd Edition
[8]
Xenomai Webpage, http://www.xenomai.org
[9]
A. Sambi, RTAI. disponibile su: http://www-lia.deis.unibo.it/
Courses/SistRT/1b-RTAI.pdf
[10]
Linux Kernel Archives, http://www.kernel.org/pub/linux/
kernel
[11]
Kevin Dankwardt, 2002 «Real-Time and Linux, part 1», Embedded
Linux Journal
[12]
FOX Board LX832, http://foxlx.acmesystems.it/?id=14
[13]
Documentazione SDK Axis Communications, http://www.
axis.com/products/dev_sdk/index.htm
86
[14]
AXIS ETRAX 100LX «Designer’s Reference», http://www.
axis.com/products/dev_sdk/index.htm
[15]
AXIS ETRAX 100LX «Programmer’s Manual», http://www.
axis.com/products/dev_sdk/index.htm
[16]
Giuseppe Lipari, «Real Time Linux and The Xenomai system»
[17]
Martin P. Andersson, Jens-Henrik Lindskov, «Real-Time Linux in an Embedded Environment», A Port and Evaluation of
RTAI on the CRIS Architecture
[18]
Lucconi F., «Porting su architettura ARM Marvell 88F6281
ed analisi comparativa delle patch real-time RTAI e Xenomai
per il kernel Linux»
[19]
Baldini A., «Progettazione, sviluppo e time analysis di
una applicazione real-time per image-processing in ambiente
Xenomai»