Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Il primo insieme di servizi che analizziamo sono quelli utili agli utenti:
▪ Interfaccia con l'utente. Quasi tutti i sistemi operativi si interfacciano con l'utente e dunque forniscono un'interfaccia che può essere di
varie tipologie; alcuni hanno una interfaccia a riga di comando CLI che consiste appunto in stringhe che codificano i comandi, e
chiaramente un metodo per inserirle; altri hanno un'interfaccia batch che prevede invece che i comandi siano codificati in file che
vengono poi eseguiti; infine la maggior parte dei sistemi mettono a disposizione un'interfaccia grafica GUI che appunto prevede
finestre e un puntatore per comandare le operazioni. Spesso alcuni sistemi danno la possibilità di avere anche più tipologie di
interfacce, lasciando scegliere talvolta all'utente quale utilizzare.
▪ Esecuzione di un programma. Il sistema svolge un servizio fondamentale, che come abbiamo già ampiamente visto è quello di
permettere l'esecuzione di un programma, caricandolo in memoria e interrompendolo in modo normale o anomalo.
▪ Operazioni di I/O. Il sistema operativo offre mezzi adeguati per gestire i dispositivi, senza permettere, per motivi di efficienza e
protezione, il controllo di questi da parte dell'utente.
▪ Gestione del file system. Il sistema permette l'utilizzo e la gestione di file e directory, del loro accesso, della loro creazione e
cancellazione, della ricerca e talvolta permette all'utente persino di scegliere il file system per avere funzionalità e prestazioni
specifiche.
▪ Comunicazioni. Molti sono i casi in cui i processi necessitano di comunicare tra di loro. La comunicazione può avvenire in due modalità:
attraverso lo scambio di messaggi, che implica appunto la creazione di un canale di comunicazione in cui i processi si scambiano i
messaggi, oppure attraverso la memoria condivisa, che permette invece la comunicazione tramite lettura e scrittura di informazioni in
un’area di memoria condivisa ai processi che comunicano.
▪ Rilevamento degli errori. Il sistema fornisce un servizio, come abbiamo potuto vedere, di protezione e sicurezza che necessita di un
meccanismo per la rilevazione degli errori. Deve infatti saper poi intraprendere l'azione giusta, dopo aver rilevato l'errore; talvolta
l'unica azione possibile è l'arresto di sistema, altre volte invece si riesce a provocare solo la terminazione di un processo o restituirgli un
messaggio di errore in modo tale che possa cercare di risolvere l'errore.
Il secondo gruppo di servizi che analizziamo sono invece quelli utili al sistema:
▪ Allocazione delle risorse. Il sistema si occupa della distribuzione delle risorse necessarie ai processi e agli utenti, che possono essere
molteplici ed essere attivi contemporaneamente.
▪ Accounting delle risorse. Si tiene meticolosamente traccia degli utenti che utilizzano il calcolatore, e di quali risorse stanno
impiegando, allo scopo di contabilizzare le risorse o anche per redigere statistiche.
▪ Protezione e sicurezza. Come abbiamo già detto, la protezione assicura che l'accesso alle risorse del calcolatore sia controllato, mentre
la sicurezza di assicura che non accedano al sistema utenti non riconosciuti, assicura i dispositivi, assicura che non ci siano accessi
illegali.
2.2 User Interface del sistema operativo
sabato 27 luglio 2019 13:14
Abbiamo visto che la maggior parte dei sistemi operativi sono interattivi con un utente, hanno bisogno spesso dell'intervento utente e
dunque forniscono un'interfaccia di comunicazione con esso. In generale le due tipologie di interfacce più diffuse e famose sono proprio la CLI
e la GUI che adesso analizzeremo in dettaglio.
❖ Comunicazione
Restano da analizzare le chiamate di sistema relative alla comunicazione dei processi. Esistono due tipologie di comunicazione che adesso
analizzeremo.
Si definisce comunicazione a scambio di messaggi quel modello di comunicazione per cui, tramite una mailbox, i processi si scambiano i
messaggi in modo diretto o indiretto. È necessario innanzitutto creare la connessione e chiuderla alla fine della comunicazione e ciò è
possibile prima identificando i processi in gioco con get_host_id() per sapere il nome macchina e get_process_id() per sapere invece
l'identificatore di processo; fatto ciò tali identificatori vengono passati alle chiamate open() e close() oppure a open_connection() e
close_connection() a seconda del sistema. Generalmente un processo deve anche accettare la comunicazione tramite la chiamata
accept_connection(). Lo scambio di messaggi, dunque l'invio e la ricezione avvengono tramite le chiamate di sistema write_message() e
read_message().
Si definisce comunicazione a memoria condivisa quel modello in cui i processi condividono appunto aree di memoria. Si creano e si accede
alla memoria tramite le chiamate shared_memory_create() e shared_memory_attach(). Il sistema operativo deve poi avere dei meccanismi
per far sì che i processi non abbiano accesso in contemporanea alla memoria condivisa, scrivendo e leggendo contemporaneamente.
Questi modelli di memoria sono assai diffusi e spesso sono addirittura presenti contemporaneamente; lo scambio di messaggi è utile in fatti
per lo scambio di piccole quantità di dati, mentre nel caso di grosse quantità è sicuramente più conveniente il modello a memoria condivisa;
il primo modello però è più semplice, il secondo è complesso perché deve assicurare protezione e sincronizzazione dei processi.
2.5 Programmi di sistema
lunedì 29 luglio 2019 15:10
Abbiamo visto che, nella gerarchia di un calcolatore, abbiamo alla base l'hardware, poi salendo abbiamo il sistema operativo e poi al di sopra i
programmi di sistema insieme ai programmi applicativi.
I programmi di sistema o utilità di sistema sono sostanzialmente programmi forniti dal sistema stesso che sono talvolta utili ad esso stesso;
alcuni sono semplici chiamate di sistema mentre altri sono più complessi. Possiamo classificare i programmi di sistema nelle seguenti
categorie:
• Gestione dei file. Sono programmi che effettuano operazioni (copia, crea, cancella ecc.) su file e directory.
• Informazioni di stato. Sono programmi che richiedono di indicare data, ora, spazio disponibile in memoria, numero utenti ecc. al
sistema.
• Modifica dei file. Sono programmi che vengono chiamati editor che permettono di creare e modificare il contenuto dei file
memorizzati su dischi o altri dispositivi.
• Ambienti di supporto alla programmazione. Comprendono compilatori, assemblatori, debugger, interpreti, talvolta forniti con il
sistema stesso, talvolta scaricabili.
• Caricamento ed esecuzione di programmi. Comprendono caricatori (loader) e linker.
• Comunicazioni. Offrono meccanismi per cui si possono creare collegamenti virtuali tra processi, utenti e calcolatori diversi, tipo
browsers ecc.
• Servizi di background. Alcuni programmi, una volta che vengono lanciati, restano in esecuzione lungo tutta l'esecuzione del sistema
operativo e terminano con lui, anziché terminare in modo classico; tali processi sono chiamati servizi, sottosistemi o demoni. Un
sistema ha tipicamente decine di demoni.
Tipicamente un utente si fa un’immagine del sistema influenzata dalle applicazioni e dai programmi di sistema, non di certo dalle chiamate di
sistema messe a disposizione; molto spesso però all'apparenza due sistemi operativi sono diversi ma in realtà mettono a disposizione le stesse
chiamate di sistema.
2.6 Progettazione e realizzazione di un sistema operativo
lunedì 29 luglio 2019 15:23
❖ Scopi della progettazione
Nel momento in cui inizia la progettazione di un sistema operativo, la prima domanda che ci si pone riguarda il capire gli obiettivi e le
specifiche del sistema stesso. Da una parte infatti, al livello più alto, la progettazione è influenzata dalla scelta dell'architettura fisica e dal tipo
di sistema. Ad un altro livello gli obiettivi sono un po' più blandi da specificare, ma in generale si dividono in due gruppi, ovvero in obiettivi
utenti e obiettivi sistema.
Per quanto riguarda gli obiettivi per gli utenti ci sono alcuni obiettivi ovvi, come la facilità di utilizzo, la facilità nell'imparare a usarlo, la
sicurezza, la velocità e l'affidabilità, eppure non sono obiettivi facili da identificare perché tali caratteristiche sono soggettive da utente ad
utente. Bene o male le stesse caratteristiche sono richieste anche da chi utilizza per progettare, creare e operare con il sistema, quindi gli
obiettivi di sistema, ed anche qui purtroppo non sono obiettivi precisi perché sono vaghi ed interpretabili in vari modi. Non esiste dunque
una soluzione unica alla progettazione del sistema per quanto riguarda gli utilizzatori e non a caso esistono numerosi sistemi operativi in
commercio per coprire i più svariati usi e utenti.
❖ Meccanismi e politiche
Creare un sistema operativo richiede molta creatività e non esistono linee guida precise per capire come fare; in compenso esistono però dei
principi generali forniti nel campo del software engineering.
Fondamentale è la distinzione tra due concetti che sono meccanismi e politiche: i meccanismi determinano come eseguire qualcosa, mentre
le politiche indicano e stabiliscono che cosa si debba fare. Analizziamo ad esempio il timer di sistema: è un meccanismo che assicura la
protezione del sistema e il tempo da impostare nel timer riguarda invece le politiche.
La differenza ulteriore tra meccanismi e politiche sta nel fatto che le politiche sono soggette a cambiamenti nel tempo e di luogo, e nel caso
peggiore tali cambiamenti portano anche al dover cambiare i meccanismi. In generale sarebbe ottimale disporre di meccanismi generali, in
modo tale che il dover cambiare politica consiste nel dover modificare solo qualcosa, ridefinendo solo alcuni parametri.
I sistemi a microkernel portano all'esasperazione la divisione tra meccanismi e politiche, dal momento che forniscono un insieme di
funzionalità di base (quindi meccanismi di base) che sono quasi completamente indipendenti dalle politiche, e tali consentono poi l'aggiunta
di moduli ulteriori aggiungendo allo stesso tempo meccanismi nuovi e criteri più complessi.
I sistemi come Windows invece si hanno meccanismi e criteri fissati a priori e propri al sistema.
❖ Realizzazione
È difficile fare affermazioni su come un sistema operativo venga realizzato, dal momento che la sua realizzazione implica tempo e soprattutto
implica la scrittura di moltissimi programmi da parte di moltissime persone.
Soffermiamoci quindi sulla sua programmazione che ovviamente è il modo in cui viene realizzato.
Inizialmente i sistemi operativi venivano scritti in linguaggio assembler; al giorno d'oggi vi sono ancora sistemi scritti in tale linguaggio ma la
maggior parte è scritta in linguaggi di alto livello come il C o il C++. Un sistema operativo attuale però non è scritto in realtà in un solo
linguaggio, quindi si utilizza per la maggior parte linguaggi di alto livello, ma ci sono ancora piccole parti di codice, relative magari a procedure
critiche o a più stretto contatto con l'hardware, che sono scritte in assembler.
I vantaggi dell'uso di linguaggi di alto livello sono ovvi, infatti basta considerare che innanzitutto linguaggi più comprensibili e vicini al
programmatore, sono più veloci, più compatti, più facili da capire e da mettere a punto anche in un secondo momento; consente un
maggiore porting da una macchina ad un'altra, infatti se consideriamo l'MS-DOS che fu scritto in assembler, per l'Intel 8088, tale è disponibile
quindi solo per la famiglia di CPU Intel; il sistema operativo Linux invece, scritto in C, è disponibile invece su più CPU.
Gli svantaggi dell'uso di linguaggi di alto livello invece sono una minore velocità di esecuzione e una maggiore occupazione di spazio in
memoria, ma tali svantaggi, grazie agli hardware attuali, sono stati ormai superati; ormai gli algoritmi migliori, le strutture dati che forniscono
i linguaggi di alto livello sono sicuramente più vantaggiosi di una buona scrittura in assembler.
Una volta scritto il sistema e verificato il funzionamento, è possibile procedere poi al debugging del sistema, ovvero all'individuazione di
procedure che possono portare a colli di bottiglia, sostituendole magari con procedure più precise scritte in basso livello.
2.7 Struttura del sistema operativo
lunedì 29 luglio 2019 16:29
❖ Struttura monolitica
Molti sistemi non hanno una struttura ben precisa; sono nati come sistemi piccoli, semplici e solo dopo poi si sono accresciuti, superando lo
scopo originale.
Un primo sistema che possiamo considerare è sempre MS-DOS, che fu progettato da poche persone che non si sarebbero mai immaginate la
sua grandissima diffusione. Tale sistema non fu suddiviso in moduli, è appunto un unico blocco, perché il suo scopo era quello di occupare il
minimo spazio in memoria. Non vi è infatti alcuna separazione tra interfacce e livelli di funzionalità, le applicazioni infatti accedono
direttamente alle routine di sistema, scrivendo magari direttamente su disco e I/O, rendendo, come abbiamo già visto, il sistema vulnerabile
a errori e attacchi sia interni che esterni. Da un certo punto di vista, visto che fu progettato sull'Intel 8088, non ci si poteva aspettare altro,
infatti come abbiamo già visto l'hardware sottostante e disponibile è chiaramente rilevante nella struttura del sistema operativo; tale
processore non aveva infatti la distinzione tra modalità kernel e modalità utente, e dunque non lasciava altra scelta ai progettisti di lasciar
libero accesso, incondizionato, all'hardware.
Anche il primo sistema Unix non era strutturato, sempre a causa delle limitazioni hardware. Consisteva in due parti separate, ovvero il kernel
e i programmi di sistema, e a sua volta il kernel era suddiviso in interfacce e driver, aggiunti ed espansi poi con il corso della sua evoluzione;
sembrerebbe un sistema parzialmente stratificato, ma tutto sommato in realtà è un ammasso di funzionalità combinate comunque su un solo
livello.
Tale struttura rendeva inevitabilmente non solo difficile la sua realizzazione ma anche la sua manutenzione; l'unico vantaggio che poteva
apportare era nelle prestazioni.
❖ Metodo stratificato
Grazie alla presenza poi di hardware appropriato, fu possibile suddividere il sistema in più moduli, più facilmente gestibili e più piccoli, ancor
di più del primo sistema Unix. Stratificare ha permesso di avere un controllo più stretto sul calcolatore e sulle applicazioni che lo utilizzano.
Seguendo un approccio top-down si parte, in tali sistemi, dall'individuare le funzionalità complessive e le sue caratteristiche, e si procede
dividendole in componenti distinte, facendo anche attenzione all'incapsulamento.
Vi sono vari modi per modulare un sistema operativo e quello che analizzeremo ora è il metodo stratificato; secondo tale metodo il sistema è
suddiviso in un certo numero di livelli o strati, dove il più basso è quello relativo all'hardware fino ad arrivare all'n-esimo livello che è
l'interfaccia con l'utente.
Lo strato del sistema è chiaramente un oggetto astratto, che incapsula dati e operazioni che trattano tali dati. Uno strato del sistema ha
strutture dati e ha routine che possono essere richiamate solo dagli strati di livello più alto; dunque a sua volta tale strato può richiamare solo
le routine degli strati inferiori.
Il vantaggio principale che fornisce tale struttura è che è di facile progettazione e debugging, infatti per come sono composti gli strati e per il
fatto che si possono utilizzare solo funzioni degli strati inferiori, fa sì che ogni qual volta si progetti uno strato si può dare per certo che gli altri
siano corretti e ci si può concentrare solo sullo strato che si sta progettando; questo vuol dire anche che se si incontra un errore questo non
può essere che relativo allo strato che si sta considerando. Ogni strato si realizza soltanto partendo da quello che è messo a disposizione dagli
strati inferiori, senza entrare nel merito di come ciò è stato realizzato, per questo vi è incapsulamento poiché ogni strato nasconde agli altri
ciò che contiene.
Lo svantaggio di tale struttura invece risiede nel fatto che bisogna ben capire come fare tali strati, cosa comprendere in tali strati, come
dividere insomma il sistema. Un ulteriore problema sta nel fatto che tale modello tende ad essere meno efficiente rispetto agli altri, perché
magari per effettuare una particolare operazione sono necessarie chiamate di sistema dal livello sottostante, che richiama a sua volta
funzioni, e quindi chiamate di sistema, del livello ancora più sottostante e così via, facendo risultare una chiamata di sistema che richiede
molto più tempo di una di un sistema non stratificato. Tali problemi hanno arrestato abbastanza lo sviluppo di sistemi con tale struttura,
infatti attualmente si progettano sistemi basati su pochi strati con più funzioni
❖ Microkernel
Il primo sistema operativo Unix, abbiamo visto, è cresciuto sempre più, allontanandosi un po' dalla sua struttura tipicamente monolitica.
Negli anni '80 un gruppo di ricercatori universitari progettò un sistema operativo chiamato Mach, il quale aveva un kernel strutturato in
moduli, a microkernel. L'orientamento a microkernel consiste nel rimuovere dal kernel tutti i componenti non essenziali, realizzandoli come
programmi di sistema o utente. In questo modo il kernel risulta di dimensioni molto ridotte.
Non c'è un'opinione comune su quali servizi debbano rimanere nel kernel e quali no. Tuttavia, in generale un microkernel fornisce i servizi
minimi di gestione dei processi, della memoria e di comunicazione.
Il microkernel deve quindi poi fornire un meccanismo di comunicazione tra i programmi client e i vari servizi in esecuzione nello spazio
utente. La comunicazione avviene tramite lo scambio di messaggi; in questo modo se si vuole accedere a un file, un programma client deve
interagire con un programma server e ciò non avviene mai direttamente ma sempre tramite il microkernel.
Il vantaggio principale dei sistemi a microkernel è che rendono il sistema operativo facilmente estensibile, infatti i nuovi sistemi si
aggiungono allo spazio utente e non modificano in alcun modo il kernel. Quest'ultimo se deve essere modificato subirà cambiamenti molto
ridotti e dunque ciò rende il sistema anche più portabile su altre piattaforme. Offre maggiore sicurezza ed affidabilità, perché se un servizio è
compromesso il kernel ne resta immune.
Lo svantaggio di tali sistemi è che possono incappare in cali di prestazione dovuti al sovraccarico dell'esecuzione di troppi processi sistema in
modalità utente. Per questo motivo al tempo della progettazione di Windows XP si era ormai passati ad un'architettura più monolitica che a
microkernel.
❖ Moduli
Il miglior approccio attualmente disponibile è quello a moduli, che consiste in moduli kernel caricabili dinamicamente. Il kernel è infatti
costituito da componenti di base alla quale si integrano dinamicamente altre funzionalità, o all'avvio o all'occorrenza. Questa strategia è
ormai utilizzata dalle implementazioni moderne di Unix, quali Linux, Solaris, Mac OSX e anche da Windows.
Il kernel deve fornire solo i servizi principali mentre poi gli altri servizi si implementano in modo dinamico, durante l'esecuzione del kernel.
L'aggiunta infatti di nuove funzionalità nel kernel è molto più dispendiosa dell'aggiunta di moduli, in quanto la prima richiederebbe di
compilare il kernel ogni volta che si fa un cambiamento.
Si ha quindi tutto sommato un sistema che ricorda quello stratificato, perché ogni sezione del kernel ha interfacce ben protette e nascoste
agli altri, ma è sicuramente più flessibile in quanto ogni modulo può richiamare qualsiasi altro modulo; si ha anche la somiglianza con un
sistema microkernel visto che si ha un modulo principale dove ci sono solo funzionalità di base e poi le altre sono al di fuori, ma è
sicuramente più efficiente perché i moduli non devono invocare funzioni di trasmissione di messaggi per la comunicazione.
❖ Sistemi ibridi
Abbiamo infine un'ultima modalità che sostanzialmente è quella che adottano quasi tutti i sistemi operativi, ovvero possiamo considerarli
quasi tutti dei sistemi ibridi, e questo perché quasi nessun sistema operativo ha una struttura ben precisa e definita; vanno infatti a
combinare strutture diverse.
I sistemi come Linux e Solaris ad esempio sono monolitici (perché il sistema operativo ha un unico spazio di indirizzamento per aumentare le
prestazioni) ma allo stesso tempo sono modulari.
I sistemi come Windows invece sono monolitici in gran parte, sempre per le prestazioni, ma conservano comunque un orientamento a
microkernel e addirittura a moduli, come accennato prima.
Soffermiamoci adesso su tre esempi, approfondendoli nel dettaglio, ovvero Mac OS X e, per quanto riguarda i dispositivi mobili, iOS e
Android.
Mac OS X ha una struttura ibrida; è sicuramente un sistema stratificato
dove abbiamo, allo strato più alto, Aqua che è l'interfaccia grafica con
l'utente, poi proseguendo a scendere abbiamo un insieme di ambienti
applicativi e servizi, tra cui Cocoa che è un ambiente che definisce un API
per l'Objective-C che si utilizza nella programmazione di applicazioni per
tale sistema.
Proseguendo ancora c'è il kernel che è costituito dal microkernel Mach e
dal kernel BSD; il primo si occupa della gestione della memoria, delle
chiamate a procedure remote, dalla comunicazione dei processi e allo
scheduling dei thread, mentre il secondo mette a disposizione
un'interfaccia a riga di comando con gli ambienti applicativi, fornisce servizi
legati al file system e alla rete e un'implementazione dell'API POSIX.
Infine, come si può vedere il kernel fornisce anche un kit di strumenti per lo sviluppo dei driver dei dispositivi, l'I/O kit, e moduli caricabili
dinamicamente che nel gergo del Mac vengono chiamate estensioni del kernel.
In sostanza quindi in Mac OS X abbiamo una struttura stratificata, a microkernel e a moduli.