Tesi Andrea Primativo
-
Upload
andrea-primativo -
Category
Documents
-
view
23 -
download
0
Transcript of Tesi Andrea Primativo
Facoltà di Ingegneria
Corso di Studi in Ingegneria Informatica
Elaborato finale in Sistemi Real Time
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Anno Accademico 2011/2012
Candidato:
Andrea Primativo
matr. N46/246
Indice
Introduzione 4
Capitolo 1. Architettura di Sistema 6
1.1 Microkernel 71.1.1 Scheduling 71.1.2 Timer 9
1.1.3 Interrupt 9
1.2 Process Manager 121.2.1 Process Management 121.2.2 Memory Management 13
1.3 Adaptive Partitioning 141.3.1 Adaptive partitioning thread scheduler 151.3.2 Critical thread 16
1.4 High Availability 17
1.4.1 HA Client-side Library 181.4.2 HA Manager 18
1.5 Inter Process Communication 191.5.1 Message-Passing 19
1.6 Character I/O 21
1.7 Filesystem 23
Capitolo 2. QNX Momentics IDE & tools 24
2.1 Comunicazione Host-Target 25 2.2 System Profiler Tool 26
III
2.3 Application Profiler Tool 27
Capitolo 3. Esempio di utilizzo di Momentics IDE 31
3.1 Creazione Processi 31 3.2 Monitoraggio Processi 35
Conclusioni e sviluppi futuri 41
Bibliografia 43
IV
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Introduzione
Sono sempre di più i campi in cui sono necessarie prestazioni real time. Ciò è dovuto sia
alla nascita di nuove esigenze applicative come il voip o lo streaming, sia all’enorme
evoluzione dell’hardware, in termini di costo e dimensioni, che ha consentito un
processo di automazione e digitalizzazione in svariati ambiti.
La necessità di base è quella di ottenere un servizio che sia garantito in termini di tempo
e prevedibilità.
In un passato non troppo lontano lo sviluppo di un sistema realtime prevedeva molto
spesso soluzioni “ad hoc”, ad esempio era di uso comune far interagire le applicazioni
direttamente con l’hardware in modo da eliminare tutte le fonti di non determinismo
introdotte ad esempio dal sistema operativo.
Al giorno d’oggi un tale approccio non solo è sconsigliabile in quanto contrario ad ogni
principio dell’ ingegneria del software, ma è praticamente irrealizzabile data l’elevata
complessità raggiunta degli odierni sistemi.
Si è reso quindi necessario l’utilizzo di sistemi operativi opportunamente progettati per
poter operare in un ambiente realtime, i cosiddetti RTOS (Real Time Operating
System).
Tali sistemi non solo sono in grado di garantire tempi di risposta molto bassi ma, cosa
più importante, anche un elevato grado di prevedibilità e determinismo.
Esistono ormai molti RTOS, sia open-source sia commerciali che, sebbene offrano lo
stesso servizio, sono spesso profondamente diversi tra loro rendendone ognuno più
adatto a talune applicazioni anzichè altre.
La scelta dell’RTOS da utilizzare in un determinato sistema è molto delicata, infatti ci
sono molti aspetti da considerare, non ultimi le certificazioni (a quali standard è
conforme), o i tools che tale sistema mette a disposizione.
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Insomma una buona scelta può rappresentare un valore aggiunto per il sistema nel suo
complesso, mentre una scelta non adatta può comportare il fallimento dell’intero
progetto.
In questo elaborato analizzeremo l’RTOS QNX, che si propone come soluzione
altamente scalabile e flessibile e allo stesso tempo affidabile, adatta ad una vasta gamma
di applicazioni.
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Capitolo 1
Architettura di sistema
QNX è un prodotto della Quantum Software System, una società canadese fondata da
due studenti dell’università di Waterloo nel 1982, anno del primo rilascio di QNX. In
pochi anni tale RTOS si guadagnò la fama di sistema altamente robusto e affidabile e
infatti è stato usato in molte applicazioni industriali.
Verso la fine degli anni ’80 il kernel venne completamente riscritto decidendo di
utilizzare lo standard POSIX. Il risultato fu QNX 4.
Neutrino è invece l’attuale versione del kernel QNX, scritto verso la fine degli anni ’90
e costantemente in aggiornamento, offre supporto nativo ad architetture SMP ed è
completamente conforme allo standard POSIX.
La scelta caratterizzante di QNX è quella di adottare un’architettura a microkernel che
sia molto flessibile in modo da potersi adattare semplicemente alle più disparate
esigenze e che sia contemporaneamente robusta. Infatti solo le funzioni fondamentali
sono implementate a livello kernel, mentre il resto delle funzionalità offerte (File
System, Device Drivers, Networking, ecc.) sono implementate in moduli separati che
girano a livello utente.
Un aspetto molto importante di QNX è la vasta gamma di certificazioni che gli sono
riconosciute, infatti oltre ad offrire completa aderenza allo standard POSIX è in
possesso delle seguenti certificazioni:
ISO 9001:2008
IEC 62304 Software Life-Cycle for Medical Device
IEC 61508 Safety Integrity Level 3
Common Criteria ISO/IEC 15408 Evaluation Assurance Level (EAL) 4+
POSIX PSE52 Realtime Controller 1003.13-2003
OpenGL ES
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
E’ anche grazie a queste certificazioni che QNX è utilizzato in numerosi ambienti safety
critical, soprattutto in campo medico. Infatti esiste in commercio un’estensione del
kernel QNX ottimizzato per apparecchiature mediche: QNX Neutrino RTOS for
medical device.
Inoltre QNX Neutrino è molto diffuso nei sistemi di controllo industriale, nel settore
automotive, in sistemi per il controllo del traffico, nel settore aerospaziale e addirittura
in alcuni sistemi di controllo di reattori nucleari.
Infine il BlackBarry Tablet OS, il sistema operativo utilizzato dal tablet PlayBook, è
basato su QNX Neutrino.
1.1 Microkernel
Il microkernel implementato da QNX
(modulo procnto) è scritto quasi
completamente in C e implementa i servizi
fondamentali per la gestione dei thread e
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
per garantire il funzionamento realtime.
Sostanzialmente i servizi offerti da tale microkernel sono lo scheduling, lo scambio di
messaggi, fondamentale per permettere la comunicazione tra i vari moduli aggiuntivi, la
gestione delle interruzioni e la gestione dei timer.
L’intera struttura del microkernel è realizzata in modo da porre un limite superiore al
tempo di esecuzione di una sequenza di istruzioni durante la quale le interruzioni sono
disabilitate, garantendo così un certo grado di prevedibilità.
1.1.1 Scheduling
Il thread è l’unità di schedulazione in QNX e quindi un processo deve contenere almeno
un thread.
Lo scheduler di QNX è uno scheduler basato su priorità, in particolare ci sono 256
livelli di priorità e lo scheduler sceglie sempre il thread “pronto” che ha la priorità
maggiore.
Esiste una coda separata per ogni livello di priorità, dove i thread pronti attendono di
essere schedulati. Solitamente ogni coda è gestita in maniera FIFO, tuttavia possono
esserci delle eccezioni, ad esempio quando un thread che si risveglia da una receive
bloccante con un messaggio per lui, esso è messo in testa alla coda relativa alla sua
priorità e non alla fine.
Per quanto riguarda le politiche di scheduling QNX ne implementa tre:
FIFO
Round Robin
Sporadic Scheduler
La politica Sporadic Scheduler risulta di particolare rilievo. Essa infatti è
un’implementazione del concetto di sporadic server, e infatti è consigliata proprio per
gestire richieste aperiodiche. E’ addirittura necessario adottare tale politica per le
richieste aperiodiche nel caso sia stato effettuato un test di schedulabilità sui task
periodici che girano nel sistema, in modo da non compromettere la fattibilità dello
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
scheduling.
Inoltre essa può essere utilizzata per gestire task hard realtime, in quanto con tale
politica è assicurato che un thread abbia a disposizione un certo tempo di calcolo
all’interno del suo periodo (deadline) e ciò equivale a garantire (nei limiti della
fattibilità, che va comunque verificata in fase di progetto) che un task termini entro la
propria deadline.
Analizziamone il comportamento: ad ogni thread viene associata una capacità iniziale
C, un periodo di replenishment T e una Low priority.
Un thread esegue per un tempo C alla sua priorità nominale. Quando C si esaurisce la
sua priorità viene abbassata a Low priority. Dopo un tempo T la capacità C si “ricarica”
e la sua priorità è riportata a livello nominale.
Inoltre c’è da dire che il numero di replenishment è limitato in un dato periodo in modo
da non sovraccaricare il sistema a seguito ad esempio di overrun di attivazione di task
aperiodici.
Si noti che ogni thread nel sistema può eseguire usando una qualsiasi delle politiche
implementate e, infatti, tramite le primitive pthread_getschedparam(),
pthread_setschedparam() e pthread_setschedprio() si possono conoscere e cambiare
sia la politica di scheduling di un thread sia la sua priorità.
Si noti che le politiche RR e FIFO sono usate solo tra thread di pari priorità. In OGNI
CASO quando un thread a priorità maggiore diventa pronto prelaziona l’eventuale
thread a priorità minore che sta eseguendo.
1.1.2 Timer
Un aspetto di rilievo del microkernel QNX è la possibilità di usufruire del set di
funzionalità di timing definite dallo standard POSIX.
Di grande rilievo è il timer “cyclical”, ossia un timer che genera interruzioni
periodicamente.
E’ evidente come tale timer sia fondamentale nella gestione di task periodici, può essere
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
infatti utilizzato per svegliare un task che dopo aver eseguito le sue operazioni torni a
bloccarsi fino al periodo successivo.
1.1.3 Interrupt
Uno dei tanti aspetti importanti in un sistema realtime è legato alle interruzioni, in
particolare i tempi di latenza legati ad esse.
I tempi di latenza di interesse sono due:
Latenza di interrupt, ossia il tempo che intercorre dall’istante in cui l’hardware
segnala l’interruzione all’istante in cui la prima linea di codice dell’interrupt handler è
eseguita. E’ necessario che questo tempo sia il più piccolo possibile. QNX si preoccupa
di renderlo tale, infatti le interruzioni sono quasi sempre abilitate e la latenza di interrupt
in questo caso è estremamente ridotta. Tuttavia alcune sezioni di codice impongono la
disabilitazione delle interrupt ritardando quindi l’ esecuzione dell’ handler; in questo
caso la latenza di interrupt nel caso peggiore è legata al tempo di esecuzione della più
lunga sezione di codice da eseguire ad interrupt disabilitate, tale tempo di esecuzione in
QNX è molto piccolo[1].
Latenza di scheduling, ossia il tempo che intercorre tra l’ultima istruzione
dell’interrupt handler e la prima istruzione del driver. Ciò equivale solitamente ad un
cambio di contesto e, sebbene sia un tempo più grande rispetto alla latenza di interrupt,
anche in questo caso QNX assicura che è un tempo molto ridotto, si pensi infatti che un
contex switch relativo a un processo in QNX Neutrino è più veloce di un contex switch
relativo a un thread in un sistema Unix [1].
Ovviamente QNX Neutrino offre pieno supporto alle interrupts innestate.
1.2 Process Manager
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Il Process Manager è un’entità che, assieme al microkernel, è fondamentale per
l’esecuzione dei processi. Entrambe queste unità sono contenute nello stesso modulo
(procnto), essenziale per il runtime.
Le principali responsabilità del process manager riguardano la gestione dei processi
(process managment) e la gestione della memoria (memory managment).
Analizziamo questi due aspetti.
1.2.1 Process Managment
Per quanto riguarda la gestione dei processi la principale responsabilità del process
manager è la creazione dinamica di nuovi processi.
Per tale scopo sono rese disponibili diverse funzioni:
posix_spawn(), definita dallo standard POSIX tale funzione permette la creazione di
un processo specificando il programma che sarà eseguito da tale processo.
E’ l’equivalente di una fork() seguita da una exec(). Tuttavia la posix_spawn()
risulta molto più efficiente di tale combinazione in quanto non ha necessità di
ricopiare lo spazio di indirizzamento del processo padre (cosa che avviene tramite la
fork() ).
spawn(), tale funzione effettua sostanzialmente le stesse operazioni della
posix_spawn(), ma è implementata direttamente da QNX Neutrino e permette di
controllare dei parametri quali “scheduling policy” e “priority” che con la
posix_spawn() non sono accessibili.
fork().
vfork().
exec().
Sebbene siano disponibili anche le funzioni fork(), vfork() ed exec(), come si è detto,
risulta più conveniente usare una spawn() quando si vuole creare un nuovo processo
che dovrà eseguire un programma differente da quello del padre. Tuttavia tali funzioni
sono presenti soprattutto per una questione di portabilità (verso sistemi che non
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
supportano la posix_spawn() ).
1.2.2 Memory Managment
Un aspetto caratteristico di QNX è proprio la gestione della memori in quanto, a
differenza della maggior parte degli RTOS, esso fornisce protezione della memoria
anche a runtime, tramite l’utilizzo di una MMU (che ormai viene fornita in molti
processori embedded).
L’utilizzo di una MMU comporta sicuramente degli svantaggi in termini di overhead e
prevedibilità, tuttavia l’avanzamento della tecnologia smussa tale aspetto, rendendo i
vantaggi di un ambiente protetto (dal punto di vista della memoria) di gran lunga
superiori agli svantaggi. In sostanza implementare protezione della memoria a runtime
permette di raggiungere livelli di robustezza ben superiori rispetto al caso di memoria
non protetta, al costo di un piccolo peggioramento delle performances.
Nello specifico il modello a protezione completa offerto da QNX prevede la rilocazione
del codice di un processo in uno spazio di indirizzamento virtuale privato che può
variare da 2 a 3.5 GB utilizzando appunto una MMU per la mappatura degli indirizzi
logici negli effettivi indirizzi fisici.
La memoria è gestita con la tecnica della paginazione su domanda e, nei sistemi che lo
supportano, la dimensione delle pagine può essere variabile, il che può comportare dei
vantaggi: l’uso di pagine di dimensione elevata permette di diminuire le entry di TLB
(Translation Lookaside Buffer) ottenendo quindi una minore probabilità di TLB miss e
aumentando di conseguenza le performances.
Essendo QNX Neutrino “fully compliant” allo standard POSIX, esso prevede un
meccanismo di memory locking utile per evitare le latenze dovute al caricamento on-
demand di una pagina non presente in memoria.
Un ultimo aspetto da considerare per quanto riguarda la gestione della memoria è la
frammentazione. QNX Neutrino adotta un algoritmo di allocazione della memoria
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
fisica che cerca di ridurre al minimo tale fenomeno, tuttavia la frammentazione è
inevitabile in un ambiente dove i processi possono essere caricati dinamicamente e
quindi il memory manager implementa un algoritmo di deframmentazione.
Essendo comunque la procedura di deframmentazione onerosa in termini di overhead
c’è la possibilità di disabilitarla (di default è abilitata).
1.3 Adaptive Partitioning
Adaptive partitioning è un meccanismo di scheduling unico presentato da QNX.
Esso prevede di dividere i task presenti nel sistema in partizioni (insieme di task) ed
assicurare ad ogni partizione una percentuale minima di risorse (tempo di CPU).
Questo meccanismo è davvero interessante in quanto in condizioni di carico normali si
comporta come uno scheduler hard realtime, mentre in condizioni di carico elevate
impone dei limiti sul tempo di CPU di cui una partizione può usufruire, garantendo
comunque che la minima percentuale necessaria sia rispettata[2]. In altre parole offre
una implicita gestione dei sovraccarichi.
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
1.3.1 Adaptive partitioning thread scheduler
Adaptive partitioning thread scheduler è un thread scheduler opzionale che garantisce a
gruppi di thread (partizione) una percentuale minima di utilizzo della cpu (budget).
Tale scheduler si comporta diversamente a seconda dello stato del sistema, in
particolare si possono distinguere i seguenti casi:
Underload: ogni partizione utilizza una percentuale di utilizzo minore del budget
nominale. In questo caso il thread scheduler semplicemente si comporta come lo
scheduler QNX Neutrino standard scegliendo il task da eseguire come il task a
priorità maggiore tra tutti quelli presenti nel sistema (tra tutte le partizioni).
Free Time: questa condizione si verifica quando una partizione non utilizza per
niente tempo di CPU. In questo scenario lo scheduler può “cedere” il tempo
inutilizzato ad un’ altra partizione che lo richiede, in particolare tale tempo sarà
assegnato alla partizione in cui risiede il thread a priorità maggiore. Ovviamente
questo comportamento potrebbe non essere sempre desiderabile e quindi è possibile
imporre che il tempo in eccesso non sia dato al thread più prioritario ma sia diviso
equamente tra le partizioni in rapporto al loro budget nominale.
Full Load: è lo stato in cui si trova il sistema quando tutte le partizioni utilizzano
interamente il loro budget. In questo caso lo scheduler divide il tempo di CPU tra le
partizioni in rapporto ai loro budget nominali. Ovviamente quando una partizione
richiede più tempo di quanto gli sia permesso, tale tempo non gli è concesso.
Si noti che in questo caso lo scheduler potrebbe non rispettare la politica di
scheduling adottata in maniera globale: se infatti una partizione ha esaurito il suo
budget, anche se ci fosse un thread pronto ad elevata priorità in tale partizione, esso
non sarebbe eseguito, lasciando spazio, ad esempio, ad un thread a più bassa priorità
che si trovi in una partizione che abbia ancora budget disponibile.
1.3.1 Critical threads
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Si è visto che quando una partizione utilizza tutto il suo budget non gli è più permesso
eseguire (per un certo tempo). Evidentemente questo potrebbe non essere accettabile se
all’interno di quella partizione c’è un thread hard realtime fondamentale per il sistema
che si vorrebbe venisse eseguito nonostante la sua partizione sia “out-of-budget”.
Fortunatamente tale situazione è prevista. In particolare è possibile definire un thread
come critico. Tale thread potrà eseguire anche se la partizione cui appartiene ha
terminato il suo budget nominale, a patto che tale partizione disponga di un budget
“critico”.
Ricapitolando per garantire a thread hard realtime ad elevata priorità di eseguire, anche
in condizioni di carico elevato, nel rispetto delle loro deadline sono introdotti i concetti
di thread critico e budget critico.
E’ ancora possibile che un thread hard realtime non riesca ad eseguire e a non terminare
quindi entro la propria deadline?
Ovviamente questo può succedere se la partizione utilizza anche tutto il suo critical
budget.
QNX assume che tale condizione derivi da un errore di design. Infatti un errore
nell’assegnazione dei budget o un test di schedulabilità sbagliato possono essere la
causa di una deadline miss.
In un tale scenario la partizione è in bankruptcy. Neutrino permette di scegliere tra una
serie di comportamenti che il sistema dovrà adottare quando si verifica una bankruptcy:
Default: semplicemente non permette più alla partizione che ha esaurito il suo
critical budget di eseguire fino a quando non gli sarà assegnato nuovo budget
Force a reboot: Il sistema viene riavviato. Utile più che altro in fase di testing.
Notify: Notifica tramite l’invio di un segnale che una partizione è in stato di
bankruptcy.
Cancel: setta a zero il budget della partizione in oggetto. Il budget rimarrà tale fino a
quando non verrà cambiato manualmente (da shell ad esempio).
Concludendo l’adaptive partitioning thread scheduler è utile sotto molti punti di vista.
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Può essere usato, infatti, per garantire un comportamento predicibile anche in situazioni
di overload, inoltre può essere utile per “limitare” applicazioni non sicure (di terze parti
ad esempio) in modo da evitare che monopolizzino la CPU [2].
Infine offre un’ottima gestione dell’ hard realtime grazie al meccanismo dei “critical
thread”.
1.4 High Availability
Il termine High Availability (HA) sta ad indicare la capacità di un sistema di rimanere
attivo per un prolungato intervallo di tempo senza interruzioni[6].
Affinché un sistema supporti HA è necessario che sia basato su un’opportuna
architettura, sia hardware che software.
Dal punto di vista software QNX Neutrino si propone, tra l’altro, come un sistema
operativo a supporto dell’ HA. In effetti un’architettura a microkernel è di per se una
soluzione molto robusta in quanto non solo separa le applicazioni utente da quelle
kernel, ma “protegge” il kernel anche dai task di sistema stessi: si pensi ad esempio al
fallimento del modulo che si occupa del filesystem. In un’ architettura monolitica ciò
comporterebbe il fallimento dell’intero kernel mentre in una ambiente a microkernel ciò
non sarebbe vero, il resto del sistema infatti continuerebbe a lavorare mentre potrebbero
essere intraprese opportune contromisure per provvedere al fallimento di quel
determinato modulo.
Tuttavia QNX Neutrino non si basa solo sulla sua robusta architettura ma offre un
supporto esplicito all’High Availability mediante due specifici moduli:
HA client-side library
HA Manager
1.4.1 HA client-side library
HA client-side library è una libreria che reimplementa svariate funzioni di I/O della
standard C library in un’ottica HA. In particolare è pensata per garantire un
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
“meccanismo automatico e trasparente per il recovery” [6].
Ad esempio un client in comunicazione con un server può decidere di rendere tale
connessione HA; così facendo se la connessione dovesse cadere a causa ad esempio di
una momentanea mancanza di connettività la connessione verrebbe ristabilita in maniera
completamente trasparente tramite delle routine standard definite dalla libreria HA o
comunque tramite routine programmate dall’utente stesso.
1.4.2 HA Manager
L’HA Manager (HAM) offre dei meccanismi per monitorare i componenti (servizi o
processi) del sistema ed effettuare, in caso di fallimento di uno di tali componenti, un
graduale ripristino della sua normale attività. Ovviamente (HAM) provvede a tener
traccia di tutti gli eventi salienti avvenuti durante la vita dell’intero sistema.
A questo punto potrebbe sorgere spontanea la domanda: “Cosa accade se è l’HAM
stesso a fallire?”.
La risposta in effetti è molto semplice: HAM non è soggetto a fallimenti interni! Ciò è
ottenuto grazie ad una soluzione tanto semplice quanto efficace: associato ad un HAM
c’è sempre un “Guardian”, ossia un processo sempre pronto a occupare il posto dell’
HAM nel caso in cui quest’ultimo dovesse fallire. Dato che i due processi (HAM e
Guardian) condividono i dati tramite memoria condivisa il Guardian può sempre
riprendere dallo stato in cui l’HAM si è interrotto.
Si noti che il Guardian prima di prendere il posto dell’ HAM crea un nuovo processo
Guardian in modo che non ci sia mai un HAM senza un Guardian.
1.5 Inter Process Communication
La comunicazione tra processi è un aspetto particolarmente importante in QNX
Neutrino in quanto non solo le varie forme di IPC devono essere utilizzate dai processi
applicativi, ma perché anche la comunicazione tra i processi di sistema e il microkernel
avviene tramite scambio di messaggi.
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
QNX Neutrino mette a disposizione vari tipi di IPC, tra cui le classiche shared memory,
le code di messaggi POSIX, Pipes e FIFOs, che sono disponibili tramite processi esterni
(tranne shared memory che è implementata nel contesto del Process Manager).
Inoltre altri due tipi di IPC più basilari sono implementati direttamente all’interno del
nucleo: signaling e message-passing. In questo elaborato ci soffermeremo solamente sul
message-passing in quanto rappresenta la base della comunicazione col micrkernel.
1.5.1 Message-passing
In sostanza tale tecnica è basata su tre primitive fondamentali:
MsgSend()
MsgReceive()
MsgReply()
Il nome di tali primitive è auto esplicante, tuttavia c’è da dire che mentre la MsgSend() e
la MsgReceive() sono bloccanti, la MsgReply() è non bloccante.
In realtà non necessariamente la MsgReceive() comporta un bloccaggio: consideriamo
un semplice scenario dove un thread client invia un messaggio ad un thread server.
Se il server effettua la MsgReceive() dopo che il client ha invocato la MsgSend() allora
il server non subirà alcun bloccaggio; potrà quindi elaborare il messaggio e rispondere
con una MsgReplay(). Per quanto riguarda il client, esso resterà sempre bloccato dopo
aver inviato una MsgSend() in attesa di una MsgReplay(); tuttavia c’è da fare una
distinzione: se la MsgSend() è effettuata prima della MsgReceive() il client sarà bloccato
nello stato SEND_BLOCKED, in seguito alla MsgReceive(), sebbene il client continui a
rimanere bloccato il suo stato cambia e diventa REPLY_BLOCKED. Diventerà infine
READY quando il server effettuerà la MsgReply().
Se, al contrario, la MsgSend() è invocata dopo la MsgReceive() il client si bloccherà, ma
direttamente con stato REPLY_BLOCKED, fino a diventare READY in seguito ad una
MsgReplay().
In realtà in QNX uno scambio di messaggi non avviene logicamente tra due thread bensì
tramite un canale. Quindi un server che desidera ricevere messaggi creerà un canale al
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
quale uno o più client possono “attaccarsi”.
Per quanto riguarda le prestazioni della tecnica del message-passing esse sono
praticamente comparabili con la velocità della memoria fisica, in quanto l’invio di un
messaggio altro non comporta che la copia del messaggio dallo spazio di indirizzamento
del sender a quello del receiver.
Onde evitare il fenomeno dell’inversione di priorità QNX Neutrino associa alla tecnica
del message-passing il meccanismo di priority inheritance. Infatti il receiver di un
messaggio eseguira, per il tempo necessario all’elaborazione del messaggio (in pratica
fino all’invio della MsgReply() ), alla priorità del thread sender.
Un aspetto molto importante e caratteristico del message-passing implementato da
Neutrino è che esso può facilmente essere esteso in manienra del tutto trasparente a
target che non sono fisicamente sullo stesso host. Infatti tramite il framework Qnet,
ossia un insieme di librerie per il networking nativo, è possibile virtualizzare un network
in modo del tutto trasparente facendo comunicare due host tramite lo scambio di
messaggi.
1.6 Character I/O
Elevate prestazioni per quanto riguarda le comunicazioni con dispositivi di I/O a
“carattere” sono desiderabili in un sistema realtime, infatti l’utilizzo di tali dispositivi
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
potrebbe far degenerare le prestazioni dell’intero sistema (in termini di prestazioni
realtime) se non opportunamente migliorato.
QNX Neutrino adotta una tecnica per la gestione del character I/O simile a quella dei
sistemi UNIX, infatti ogni dispositivo è mappato in un apposito file (solitamente nella
directory /dev).
E’ previsto un apposito modulo, char-io, per la gestione di tutti i dispositivi di I/O.
Come si può immaginare questo modulo esegue al di fuori del kernel e implementa le
classiche API POSIX per la comunicazione con i dispositivi a carattere, ma non solo;
infatti sono presenti funzioni non previste dallo standard che possono tornare utili in un
sistema realtime al fine di migliorarne le prestazioni.
Sostanzialmente il modulo char-io può essere visto come un processo gestore in
quanto si pone tra le applicazioni software e i driver relativi ai vari device, riducendo al
minimo le responsabilità di questi ultimi.
Tale implementazione per la gestione dei file cerca ridurre al minimo l’overhead
massimizzando il throughput; in particolare ciò è possibile grazie a queste due regole:
In seguito ad una richiesta di lettura, l’interrupt handler mette il dato letto in
una coda. Ora il driver verrà schedulato soltanto se c’è una richiesta di lettura
E tale richiesta può essere effettivamente soddisfatta.
Quando un’operazione di lettura va buon fine, sarà direttamente il driver a
comunicare il risultato all’applicazione. In questo modo il dato è copiato una
volta sola.
Sebbene non si sia scesi nello specifico del sottosistema dedicato ai device, si intuisce
che anch’esso è stato opportunamente ottimizzato per garantire prestazioni adeguate ad
un sistema realtime, mantenendo tuttavia un’architettura semplice e leggera.
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
1.7 Filesystem
In QNX Neutrino è possibile usufruire di una vasta gamma di filesystem diversi. In
questo modo si aggiunge un ulteriore grado di elasticità al sistema, che può utilizzare un
filesystem anziché un altro a seconda delle esigenze. Ancora, è possibile che più
filesystem siano presenti contemporaneamente nel sistema e che anzi collaborino tra di
loro senza che ciò comporti un grosso overhead. Essendo, ancora una volta, il/i
filesystem eseguiti al di fuori del kernel, essi possono essere aggiunti o rimossi a
runtime senza alcun bisogno di riavviare l’intero sistema.
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
In tabella sono riportati i vari filesystem supportati da QNX Neutrino.
Embedded Disk Network Other
Flash Devices
NOR Flash
NAND Flash
RAMTemporary storage
POSIX – QNX Filesystem
Ext2 - Linux
FAT 12, 16, 32 – Windows/DOS
NTFS (read only) - Windows
ISO9660, Joliet – CD-ROM Devices
HFS+ (read only) - Apple Mac OSX
UDF - CDs and DVDs
NFS Unix connectivity
CIFSMicrosoft connectivity
Compression
Add compression / decompression to any file system
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Capitolo 2
QNX Momentics IDE & Tools
Qnx Momentics è un IDE, basato su Eclipse, messo a disposizione degli sviluppatori
dalla Quantum Software System.
Esso rappresenta uno strumento imprescindibile non solo per la creazione di software
per sistemi che utilizzano QNX Neutrino, ma anche per la manutenzione e il
monitoraggio di tali sistemi. Infatti il kit di sviluppo incorpora diversi tool per il
profiling sia del sistema che dei singoli processi in modo da tenere sempre sotto
controllo l’ambiente di runtime. Tale operazione di monitoraggio è possibile grazie al
servizio di comunicazione tra il target e il sistema host dove gira l’ IDE che, sia chiaro,
non necessariamente (anzi quasi mai) si trovano sulla stessa macchina.
Sebbene Momentics rappresenti un ambiente integrato che agevola lo sviluppo del
software, in questa trattazione si vuole dare maggiore rilievo ad altri aspetti della suite
di sviluppo quali: la comunicazione tra sistema host e target, il System Profiler Tool, l’
Application Profiler Tool. Infatti tali componenti sottolineano il fatto che QNX è un
sistema operativo specializzato nel gestire sistemi integrati che svolgono funzioni
critiche e quindi devono essere monitorati con particolare cura e soprattutto in maniera
remota.
2.1 Comunicazione Host-Target
Per sistemi Windows e Linux la comunicazione tra l’IDE e il target può avvenire sia
tramite IP sia tramite connessione seriale.
Per quanto riguarda la comunicazione IP è ovviamente necessario che i due end-point
(Host e Target) si trovino sulla stessa rete. Affinche un’effettiva comunicazione possa
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
avvenire il Target deve opportunamente essere configurato per gestire la connessione IP
eseguendo il tool qconn. E’ interessante notare che due target che sono connessi
tramite Qnet possono entrambi comunicare con l’IDE utilizzando lo stack TCP/IP di
uno qualsiasi di essi. Una volta connessi è possibile per l’IDE comunicare con il sistema
target. In particolare a questo punto è disponibile il pannello “system information” che
fornisce in primo luogo una veduta d’insieme del sistema evidenziando l’utilizzo totale
di memoria e i processi in esecuzione. Inoltre per ogni processo è possibile analizzare
nello specifico il suo utilizzo di memoria, ossia della sua memoria virtuale,
evidenziando le varie aree: stack, heap, area dati e memorie condivise.
Infine un altro strumento interessante è il target filesystem navigator che ci permette di
navigare all’ interno del filesystem del sistema target.
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
2.2 System Profiler Tool
Il System Profiler è un tool che permette di collezionare informazioni sul sistema target
e riportarle all’IDE. Tale tool si basa sulla estensione del microkernel Neutrino
denominata “instrumented microkernel” (proncto-instr); infatti tale modulo
utilizza dei meccanismi che permettono il profiling dell’intero sistema, il tutto senza
ovviamente compromettere le prestazioni. Sostanzialmente si può dire che System
Profiler fa uso delle informazioni ottenute dall’instrumented microkernel e le riporta
all’IDE per analizzarle.
Il System Profiler Editor mette a disposizione diversi pannelli di presentazione. Di
particolare interesse è il “summary pane”, che ci da informazioni sull’ utilizzo di cpu
sia in generale che per ogni processo.
Più nello specifico il grafico “system activity” divide il tempo di utilizzo della CPU
nelle seguenti categorie indicando quanto tempo, in percentuale, è stato utilizzato per
ogni categoria:
Idle: tempo di esecuzione del thread idle
Interrupt: tempo speso servendo interrupts
System: tempo speso gestendo kernel calls
User: tempo utilizzato da thread non idle meno il tempo utilizzato per kernel calls e
interrupt handler
La vista “process&thread activity” ci mostra invece l’utilizzo di CPU per ogni
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
processo o thread in esecuzione nel sistema specificando per ognuno di essi:
Tempo di esecuzione
Tempo di attesa nello stato READY
Tempo di bloccaggio
Numero di kernel calls
Numero di messaggi scambiati
Inoltre fornisce un grafico dell’utilizzo generale della CPU.
A questo punto si potrebbe essere interessati a sapere per esempio la causa di un
eventuale picco di utilizzo evidenziato dal grafico, in particolare se esso è attribuibile ad
un determinato processo. In questo caso può risultare utile il pannello “CPU usage” che
ci mostra graficamente in maniera molto semplice l’utilizzo di CPU associato ad ogni
processo. E’ anche possibile evidenziare più processi contemporaneamente.
Un ulteriore utilizzo che può essere fatto del system profiler è, ad esempio l’analisi dei
tempi di latenza delle interrupt. Un’analisi del genere è coadiuvata dal pannello
“timeline”. Qui infatti sono riportati tutti gli eventi “loggati” dall’ instrumented
microkernel ordinati temporalmente e divisi per processo.
Infine c’è da dire che il file di log generato dall’intrumented microkernel può arrivare a
contenere milioni di eventi di interesse, e può risultare quindi particolarmente difficile
trovarne uno in particolare. Fortunatamente il tool System Profiler mette a disposizione
funzioni di filtering che permettono di “navigare” più agevolmente all’ interno del file
di log concentrandosi solamente su eventi di particolare interesse.
2.3 Application Profiler Tool
L’Application Profiler tool permette di monitorare le prestazioni di una particolare
applicazione senza la necessità di “seguirla” linea per linea, e può essere quindi
utilizzata per migliorarne le prestazioni individuando ad esempio quelle aree di codice
che incidono negativamente sull’efficienza. Questo tool offre diversi tipi di profiling:
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Statistical profiling: in questa modalità il tool campiona ogni millisecondo il
processo durante la sua esecuzione registrando l’indirizzo dell’ istruzione
attualmente eseguita. In questo modo riesce a farsi un’idea “statistica” di quali
aree di codice impegnino di più la CPU. Ovviamente tale analisi, per sua natura,
può risultare affetta da errori statistici. Si noti che questo tipo di analisi non
comporta alcuna aggiunta di codice particolare utile a raccogliere informazioni.
Instrumented profiling: questa modalità di profiling prevede invece l’aggiunta
di codice supplementare (inserito direttamente dal compilatore), tuttavia permette
un’analisi più precisa che riesce a riportare anche informazioni “call-pair”, ossia
riesce a capire quale funzione chiama quale altra funzione.
Postmortem profiling: le informazioni ricavate da un’applicazione
“instrumented” sono raccolte in un file .out che una volta importato nell’ IDE
può fornire una rappresentazione grafica di tali informazioni. Come si intuisce
dal nome grazie a tale file è possibile un’analisi postmortem, tuttavia non
permette di ottenere tutte le informazioni ottenibili da un’analisi a runtime.
Per analizzare i dati ottenuti da un profiling, sia postmortem che a runtime, è possibile
utilizzare la “application profiler view”, mostrata nelle immagini in basso. In questo
pannello è possibile selezionare un item (es. un file .c) appartenente all’applicazione da
analizzare e a questo punto avremo una schermata (riquadro in basso a destra della
prima immagine) dove al codice sorgente è correlato un grafico (le linee colorate
affiancate alle linee di codice), in particolare:
Le barre verdi rappresentano la percentuale di tempo utilizzato nel contesto di una
funzione calcolata sulla percentuale totale associata al processo.
Le barre arancioni rappresentano la percentuale di tempo speso su quella
determinata linea di codice calcolata sulla percentuale totale associata al processo.
Le barre blu infine, rappresentano la percentuale di tempo speso su quella
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
determinata linea di codice calcolata questa volta sulla percentuale di utilizzo della
particolare funzione cui appartiene quella linea.
Per ogni dato item selezionato nella application profiler view è possibile aprire tre
ulteriori viste:
Sampling information: tale vista elenca tutte le funzioni presenti nell’item
selezionato nella application profiler view e per ognuna di esse riporta (riquadro in
basso a sinistra della prima immagine):
Tempo di CPU speso nel contesto di quella funzione
Numero di chiamate
Tempo medio di esecuzione per ogni chiamata
Thread Processor Usage: per ogni thread presente nell’item riporta il tempo di
esecuzione, sia in secondi che in percentuale al tempo di utilizzo totale del processo
(riquadro in alto a destro della prima immagine).
Call counts: questa vista include tre pannelli (seconda immagine):
Call pairs: per ogni funzione indica da chi è stata chiamata e per quante volte (es.
funzione: sort; chiamata da: main; numero di volte: 10).
Call graph: un grafico che visualizza la serie di chiamate a funzione.
Call pair details: per la funzione selezionata nel call graph indica da chi è stata
chiamata, per quante volte, e la lista delle funzioni chiamate dalla funzione in
oggetto.
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Capitolo 3
Esempio di utilizzo di Momentics IDE
In questo capitolo verrà mostrato un esempio di utilizzo dell’IDE. Verranno infatti
creati due semplici task periodici che agiranno rispettivamente da client e server
scambiandosi messaggi. Verrà dato rilievo alle primitive utilizzate per l’impostazione
del timer e a quelle relative allo scambio di messaggi. Si analizzerà poi il
comportamento dei due processi tramite i tool messi a disposizione dall’ IDE.
Nel seguente paragrafo saranno riportati soltanto i segmenti di codice di interesse.
3.1 Creazione Processi
Di seguito è riportato lo spezzone di codice relativo alla creazione del canale per la comunicazione:
Lato server
Lato client
Si noti per prima cosa che per la creazione e per l’attach non sono state utilizzata la
classica funzione ChannelCreate() e ConnectAttach(), bensì name_attach() e
name_open().
Queste ultime due funzioni in sostanza permettono di creare un canale e associargli un
nome ( name_attach() ) e di utilizzare tale nome per attaccarsi al canale (name_open() ),
rendendo l’uso di questa soluzione di gran lunga più adatto alla comunicazioni di
processi indipendenti tra loro. In questo caso ATTACH_POINT è una macro definita
if ((attach = name_attach(NULL, ATTACH_POINT, 0)) == NULL)
if( (msg_channel_id = name_open(ATTACH_POINT, 0)) == -1)return EXIT_FAILURE;
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
come “server_channel”, attach è una variabile di tipo puntatore a name_attach_t,
una struttura definita in sys/dispatch.h che conterrà, tra l’altro l’id del canale
creato tramite la name_attach().
Prima di analizzare il codice relativo all’inizializzazione del timer è opportuno
soffermarsi su un aspetto importante riguardante la gestione di quest’ultimo.
QNX permette di associare particolari tipi di eventi allo scadere del timer; in particolare
ogni volta che il timer scade possono accadere tre cose:
Viene consegnato al processo cui è associato il timer un PULSE, ossia un particolare
tipo di messaggio.
Viene consegnato al processo cui è associato il timer un SIGNAL, inteso nel senso
POSIX del termine.
Viene creato un nuovo thread.
Nel nostro caso si è scelta la prima opzione, quindi ogni volta che il timer scadrà il
processo riceverà un PULSE che lo risveglierà dal suo stato RECEIVE_BLOCKED.
Ovviamente per fare in modo che ciò accada sono necessarie delle impostazioni
preliminari, tra cui la creazione di un canale dedicato alla ricezione di PULSE:
int pulse_channel_id;int pulse_id =0;struct _pulse pulse;struct sigevent event;struct itimerspec timer;
pulse_channel_id = ChannelCreate(0);
event.sigev_notify = SIGEV_PULSE;event.sigev_coid = ConnectAttach(ND_LOCAL_NODE,0,pulse_channel_id, 0, 0 );event.sigev_priority = getprio(0);event.sigev_code = 1023;event.sigev_value.sival_ptr = (void*)pulse_id;
timer_id = TimerCreate(CLOCK_REALTIME, &event);
timer.it_value.tv_sec = 1;timer.it_value.tv_nsec =0;timer.it_interval.tv_sec = 0;timer.it_interval.tv_nsec = 500000000;
timer_settime(timer_id, 0, &timer, NULL);
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Quindi dopo aver opportunamente inizializzato la struttura event (in modo da settare
come notifica un PULSE) è possibile procedere alla creazione del timer tramite
TimerCreate(), che ha appunto in ingresso anche la struttura event.
A questo punto il timer esiste ma non è ancora avviato, ne tantomeno configurato. A
tale scopo è necessario inizializzare la variabile timer (di tipo struct
itimerspec). Tale struttura permette di impostare due parametri: il primo istante in
cui il timer scadrà (in termini relativi) e un intervallo che rappresenta il periodo dei
successivi istanti di tempo in cui scadrà il timer. Non necessariamente questi due valori
devono essere uguali. Si noti che i tempi sono espressi in secondi e nanosecondi.
Nell’esempio il timer scadrà una prima volta dopo 1 secondo e da quel momento in poi
ogni 0,5 secondi (500000000ns = 0,5s).
Infine è possibile far partire effettivamente il timer tramite l’istruzione timer_settime().
Ultimati tutti i settaggi i task possono svolgere i loro compiti all’interno di un ciclo
infinito (sono periodici); vediamo come viene svegliato un task all’inizio del suo
periodo:
Tramite la MsgReceivePulse(), un’ottimizzazione della MsgReceive() per i messaggi di
tipo PULSE, il processo rimarrà bloccato, una volta finito il suo ciclo di operazioni, fino
al ricevimento di un PULSE che sarà inviato proprio allo scadere del timer e cioè
all’inizio di ogni periodo.
Vediamo infine le primitive per lo scambio di messaggi:
Lato server:
pid = MsgReceivePulse(pulse_channel_id, &pulse,sizeof(pulse), NULL);
rcv_id=MsgReceive(attach->chid, &msg, sizeof(messaggio),NULL);
if(msg.header.type == _IO_CONNECT)MsgReply(rcv_id, EOK, NULL,0);
/* Elaborazione
*/
MsgReply(rcv_id, 0, &msg, sizeof(msg));
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Si noti l’invio di una MsgReply() particolare quando l’header del messaggio
è di tipo _IO_CONNECT. In questo caso infatti il messaggio è la richiesta di
connessione del client e quindi la MsgReply() contiene un apposito flag
(EOK) che conferma la connessione. Questa è un’accortezza che si rende
necessaria se si utilizzano le funzioni name_attach() e name_open() per
instaurare la connessione.
Lato client:
Con ciò si conclude l’analisi delle principali funzioni utilizzate per la creazione dei due
processi.
3.2 Monitoraggio Processi
Una volta mandati in esecuzione i due processi è possibile utilizzare i vari tool che si
hanno a disposizione per monitorarne l’esecuzione. E’ possibile per prima cosa avere
una panoramica dell’intero sistema tramite le varie viste del pannello system
information (Capitolo 2, Paragrafo 2.1). Tuttavia una vista interessante che non è stata
mostrata prima è la vista “malloc information” che ci da informazioni sulla memoria
MsgSend(msg_channel_id, (void*) &msg, sizeof(messaggio), (void*) &msg, sizeof(messaggio));
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
allocata dinamicamente ad un processo (Immagine 1 e 2).
I grafici mostrati nelle immagini sono relativi al processo client creato precedentemente,
infatti esso alloca ad ogni ciclo una quantità casuale di memoria.
Configurando opportunamente le due applicazioni al profiling tramite le opzioni di
compilazione dell’IDE è possibile sfruttare il System Profiler e l’ Application Profiler.
Le informazioni che si ottengono riguardo l’esecuzione dei processi e riguardo il
sistema sono riassunte in diversi pannelli.
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Vediamo ad esempio il system summary e il CPU activity che abbiamo trattato nel
secondo capitolo.
Si possono distinguere i due processi utilizzati per questo esempio denominati
“client_es” e “server_es” con i relativi tempi di esecuzione. Inoltre il grafico di destra
ci mostra la percentuale di utilizzo della CPU. Tuttavia saremmo interessati a conoscere
le percentuali di utilizzo dei nostri due task in particolare e non solo la percentuale
globale. Si è allora ricorsi al pannello “CPU Usage” riportato di seguito.
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
In rosso è evidenziata la percentuale di utilizzo del processo server, mentre in blu quella
del processo client. Sotto il grafico invece è riportata una tabella con i tempi di
esecuzione di ogni processo divisi per CPU (in questo caso una sola CPU).
Infine vorremmo essere in grado di analizzare il flusso di esecuzione dei due processi e
assicurarci che i periodi di attivazione siano rispettati. Ciò è possibile tramite il pannello
“Timeline”, in particolare quello riportato di seguito è stato ottenuto filtrando gli altri
processi presenti nel sistema e evidenziando soltanto i task dell’esempio e procnto (il
kernel).
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Le linee nere verticali rappresentano le istanze dei nostri task. Si nota subito che
l’esecuzione risulta periodica come previsto, in particolare il timer “sveglia” i processi
ogni mezzo secondo proprio come impostato in fase di progetto.
Zoomiamo ora su una singola istanza di esecuzione dei processi e analizziamone il
flusso:
Nel grafico le comunicazioni inter-processo sono evidenziate da delle frecce, inoltre i
segmenti in verde scuro indicano che il processo è in stato READY, quelli in verde
chiaro che il task è in esecuzione, mentre i segmenti in blue indicano che il task è in
stato bloccato (es. RECEIVE_BLOCKED o REPLY_BLOCKED).
Nel caso specifico vediamo che il primo ad attivarsi è il client, che richiederà un
servizio dal server tramite l’invio di un messaggio (freccia blu dal client al server), il
server dopo aver elaborato il messaggio invia la reply al client (freccia verde dal server
al client). Infine entrambi i task si bloccano in attesa del PULSE che indichi lo scadere
del timer.
Osserviamo che posizionando il mouse su un particolare evento si apre una label che ne
riporta le informazioni salienti, come ad esempio il timestamp in cui si è verificato
l’evento e il nome dell’evento. In effetti tali informazioni sono quelle contenute nel log
file, il quale è riportato anche in una finestra, riportata di seguito, al di sotto del grafico:
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Questa funzionalità offerta dall’IDE non solo può essere utile per analizzare il flusso di
esecuzione e verificare che i vincoli temporali siano rispettati, ma si potrebbe pensare di
utilizzarlo per calcolare statisticamente il tempo di calcolo di un dato processo. In tale
calcolo si dovrebbe tenere in conto però del tempo in cui effettivamente il processo in
oggetto esegue, ma anche del tempo di esecuzione di altri processi la cui esecuzione è
“imposta” dal processo in esame. Un esempio può essere proprio il processo client che
si è realizzato in questo capitolo, infatti il suo tempo di esecuzione non solo comprende
il tempo durante il quale il client utilizza la CPU, ma anche il tempo che impiega il
server ad offrire il servizio richiesto dal client.
In questo contesto può tornare utile un ulteriore pannello compreso nella vista
“Timeline” denominato “Client/Server CPU Statistics” riportato di seguito:
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Si nota per ogni processo il tempo totale di CPU, il “Self Time”, che rappresenta il
tempo effettivo di utilizzo di CPU per il dato processo, e l’ “Imposed Time” che
rappresenta il tempo richiesto ad un altro processo da parte del processo in esame.
In questo modo è anche possibile individuare processi che, seppur non consumino
direttamente tempo di CPU, impongono un grosso carico sul sistema.
La panoramica degli strumenti offerti dall’IDE si ferma qui. Chiaramente le funzionalità
offerte da quest’ambiente integrato sono molteplici e non si limitano a quelle mostrate
in questo semplice esempio.
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Conclusioni e sviluppi futuri
QNX nasce come un sistema operativo realtime che cerca di garantire elevate
prestazioni e grande robustezza ed affidabilità. In seguito all’analisi condotta si può
affermare che in effetti raggiunge questi obiettivi grazie a meccanismi intelligenti ed
eleganti. Un elemento di grande valore di questo sistema è sicuramente la sua
architettura che impiega un microkernel puro, che effettivamente fornisce poche
funzioni basilari. Questo aumenta notevolmente la robustezza del sistema a
malfunzionamenti degli altri componenti garantendo all’utente un elevato grado di
affidabilità. Ancora, grazie a questa struttura è possibile personalizzare il sistema in
modo da assicurare la massima flessibilità, in quanto caricando gli opportuni moduli è
possibile adattare il comportamento del sistema ad un particolare tipo di ambiente.
Un altro punto di forza di QNX è senza dubbio Momentics IDE che grazie ai vari tool
rappresenta un valore aggiunto di notevole interesse. Inoltre grazie alle tecniche di
cross-compilazione messe a disposizione da tale IDE e grazie al supporto di molteplici
architetture hardware, esso permette di sviluppare applicazioni che siano altamente
portabili. Abbiamo inoltre visto le varie tecniche di monitoraggio del sistema target,
funzione indispensabile, soprattutto quando si ha a che fare con sistemi critici.
Infine, aspetto non meno importante, la documentazione fornita è ampia ed esauriente e
permette a chiunque abbia dei concetti base del funzionamento di un sistema operativo
di capire con chiarezza i meccanismi adottati da QNX.
Concludendo credo di poter affermare che QNX si è mostrato effettivamente all’altezza
della sua fama e che il suo largo utilizzo in sistemi delle più svariate nature è
ampiamente giustificato dalla sua qualità.
Per quanto riguarda gli sviluppi futuri, sebbene QNX sia ampiamente affermato
soprattutto in ambito medico, sembra aprirsi un nuovo mercato in seguito
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
all’acquisizione della Quantum Software System da parte di Research In Motion,
azienda produttrice degli smartphone BlackBerry. Infatti proprio l’ultimo sistema
operativo del nuovo tablet BlackBerry è basato su QNX, quindi a quanto pare
quest’ultimo potrebbe a breve diventare un concorrente di sistemi per smartphone quali
Android e iOS, e visti gli eccellenti risultati ottenuti da QNX fino ad ora chissà se fra
qualche anno non sarà presente nella maggior parte dei nostri dispositivi mobili.
Analisi del sistema operativo QNX per lo sviluppo di applicazioni real time
Bibliografia
[1] “QNX Neutrino RTOS, System Architecture”
[2] “QNX Neutrino RTOS, Adaptive Partitioning User’s Guide”
[3] “QNX Neutrino RTOS, Programmer’s Guide”
[4] “QNX Neutrino RTOS, Library References”
[5] “QNX Momentics Tool Suite, Integreted Development Environment User’s Guide”
[6] “www.qnx.com”