Sistemi operativi
Servizi di un sistema operativo
Il sistema operativo è un ambiente che permette l'esecuzione di programmi e deve offrire dei servizi. I servizi specifici variano a seconda del sistema operativo, però esistono dei servizi generali che tutti i sistemi operativi devono offrire.
- Interfaccia con l’utente (UI): Tutti i sistemi operativi devono avere una interfaccia per consentire all’utente di poter utilizzare il sistema. Ci sono vari tipi di interfaccia utente:
- Interfaccia da riga di comando (CLI);
- Interfaccia batch (a lotti);
- Interfaccia grafica (GUI).
- Esecuzione di programmi: Deve essere in grado di eseguire un programma. Un programma deve poter terminare in maniera corretta o anomala (indicando l’errore);
- Gestione dell’I/O: Un programma in esecuzione può richiedere l’utilizzo dell’I/O per poter leggere o scrivere da un file. Per motivi di efficienza e di protezione un utente non può gestire direttamente l’I/O, perciò il sistema operativo deve fornire degli strumenti per poterlo gestire;
- Gestione del file system: Un programma oltre a leggere e scrivere da un file potrebbe voler creare file e cartelle, cercare un file nelle cartelle e ottenere informazioni riguardanti i file. Alcuni sistemi operativi gestiscono i permessi di accesso ai file sulla base della proprietà del file interessato;
- Comunicazione: Più processi possono necessitare di comunicare tra di loro. I due processi possono trovarsi sulla stessa macchina o su macchine diverse collegate in rete. La comunicazione può avvenire tramite memoria condivisa, cioè uno spazio di memoria nel quale entrambi i processi hanno accesso e possono leggere o scrivere, o tramite scambio di messaggi.
- Rilevamento di errori: Il sistema operativo deve essere sempre in grado di riconoscere errori che si possono verificare nella CPU, nei dispositivi di memoria, di I/O e in un programma utente. Il sistema operativo deve saper gestire l’errore. Talvolta è costretto a forzare la chiusura del programma, altre volte restituisce un messaggio d’errore al programma in modo che possa risolvere il problema da solo.
Un secondo gruppo di funzioni del sistema operativo non riguarda solamente gli utenti ma assicura il funzionamento efficiente del sistema stesso.
- Allocazione delle risorse: Se sono in esecuzione più processi o più utenti sono attivi, il sistema operativo provvede all’assegnamento delle risorse. Alcune di queste risorse, come i cicli di CPU, la memoria centrale e la memoria del file system, possono avere dispositivi di controllo molto specifici. Altre risorse, come i dispositivi di I/O, possono avere routine più generali.
- Accounting dell’uso delle risorse: Vogliamo mantenere traccia di quanti utenti usano il calcolatore, segnalando quali e quante risorse impiegano. Questi dati possono essere utilizzati per addebitare un costo all’utilizzatore, oppure per migliorare i servizi di calcolo.
- Protezione e sicurezza: Quando più processi sono in esecuzione concorrente non devono influenzarsi o interferire con il sistema operativo. È importante proteggere il sistema dall’accesso ad estranei. La sicurezza comincia dall’identificazione tramite username e password e si estende fino alla protezione dei file salvati sul disco da accessi illegali e provvede al loro rilevamento. Se un sistema deve essere protetto e sicuro al suo interno devono esistere precauzioni ovunque.
Controllo dei processi
Un programma in esecuzione deve potersi fermare sia normalmente (end()) che in modo anomalo (abort()). Sia che un programma venga concluso normalmente sia in modo anomalo deve restituire il controllo all’interprete dei comandi che legge il comando successivo. Quando si presenta un errore alcuni programmi lo gestiscono e continuano l’esecuzione, altri terminano e altri richiedono un input per decidere il da farsi.
La shell ha il compito di creare processi (create()) e di eseguire programmi (execute()). L’interprete dei comandi esegue un programma in seguito a una richiesta impartita. Quando si crea un nuovo processo si specificano informazioni come la priorità e il tempo massimo di esecuzione (get_process_attribute() e set_process_attribute()). Una volta creati i processi si attende la loro terminazione (wait_time()) o, più preferibilmente, si attende un determinato evento per la chiusura (wait_event()).
Alcuni processi possono condividere dei dati, a tale scopo ci sono chiamate di sistema che consentono ad un processo di bloccare i dati (lock(), acquire_lock() e release_lock()).
I sistemi monoprogrammati, come MS-DOS, erano sistemi che possedevano un interprete dei comandi ed eseguivano un solo programma alla volta. Non erano multitasking ma permettevano di fare più cose contemporaneamente con la system call terminateAndStayResident(), che terminava il processo ma non lo eliminava dalla memoria, e quando si premeva una combinazione di tasti si riapriva.
MS-DOS carica il programma in memoria (load()) e lo esegue (execute()), utilizzando anche la memoria in cui risiede lo stesso interprete dei comandi. Quando l’esecuzione termina, una piccola parte dell’interprete, che non è stata sovrascritta riprende l’esecuzione e come prima cosa ricarica l’interprete in memoria, dopodiché verrà eseguito un altro programma partendo dal valore restituito dalla terminazione del programma precedente.
Nei sistemi più recenti invece l’interprete dei comandi gestisce più processi (sistema multitasking). Per avviare un nuovo processo la shell (cioè l’interprete dei comandi) esegue la chiamata di sistema fork(), carica il programma in memoria con exec() ed esegue il programma. A seconda di come è stato impartito il comando la shell attende la fine del programma o lo esegue in background richiedendo subito un altro input.
Completato il proprio compito il processo esegue la chiamata di sistema exit() per terminare la propria esecuzione. L’interprete dei comandi esegue i programmi un po' per volta, dando l’impressione che li stia facendo tutti insieme, questo perché esegue i compiti molto velocemente. Quando la CPU passa dall’esecuzione di un pezzetto di programma ad un altro ha bisogno di effettuare un context switch, cioè deve salvare l’indirizzo del programma che sta eseguendo per poi ritornarci quando ha completato. Deve essere inoltre capace di fare attach e dispatch, e di scegliere il processo da eseguire.
Questo tipo di sistema ha come effetto collaterale una brutta gestione della memoria, perché viene allocata e liberata automaticamente la memoria necessaria creando dei buchi di memoria non sempre riutilizzabili.
Esistono chiamate di sistema che ci consentono di ottenere l’ora e la data attuale del sistema: time() e date(), e possono essere utilizzate per calcolare il tempo di esecuzione di un programma o di permanenza di un valore in una locazione.
Strutture del sistema operativo
Quando fu scritto MS-DOS non si immaginava avesse riscontrato una così grande evoluzione. A quei tempi le architetture non prevedevano l’esecuzione di programmi in user mode e kernel mode per cui potevano capitare errori dovuti alla scrittura in zone di memoria dedicate al funzionamento del sistema operativo.
Il sistema operativo può essere pensato come tanti strati sovrapposti che comunicano tra di loro. Funzioni di alto livello fanno utilizzo di funzioni di livello sempre più basso fino ad arrivare al codice assembly. Il sistema UNIX è parzialmente stratificato. Il kernel è posto tra le chiamate del sistema e l’hardware. Questa struttura monolitica rendeva difficile l’implementazione e la manutenzione ma aveva dei vantaggi in termini di prestazioni.
Prima i sistemi operativi venivano scritti in assembly, oggi vengono scritti in linguaggio di alto livello come C o di altissimo livello come C++. Scrivere un sistema operativo in C lo rende più facilmente modificabile anche se si perdono un po' di prestazioni. Scriverlo in assembly non è cosa semplice, è difficile da migliorare perché è poco leggibile, bisognerebbe ragionarci molto per capire cosa fa un’istruzione. La cosa migliore è scriverlo in C o C++ e una volta completato e verificato che funzioni si procede alla correzione di operazioni troppo lente magari riscrivendole in assembly. Così facendo il sistema rimane facilmente leggibile e le istruzioni troppo lente diventano più veloci.
Oggi i sistemi operativi sono programmati su un modello a strati. Per scrivere un sistema operativo si parte dalle istruzioni a basso livello, si testa che funzionino tutte e si va avanti creando uno strato di metodi che utilizza i metodi dello strato inferiore. In questo modo se un metodo non funziona sappiamo che non funziona nello strato più esterno che stiamo testando perché quelli sottostanti sono stati testati e funzionano.
Ogni strato del sistema operativo presenta delle strutture dati e dei metodi nascosti al livello che sta al di sotto. Ogni strato può richiamare funzioni dello strato al di sotto di lui ma non al di sopra. Questa gestione del sistema operativo però lo rende più lento rispetto alla stratificazione parziale di UNIX. Per esempio, un’operazione di I/O da parte dell’utente richiede varie chiamate a livelli sempre più bassi fino ad arrivare all’hardware di I/O posto al centro del sistema.
Negli ultimi anni questi problemi hanno causato una piccola battuta d’arresto allo sviluppo dei sistemi operativi, per cui si preferisce creare meno strati con più funzioni per renderli un po' più veloci.
Microkernel
Con il passare del tempo il sistema operativo UNIX si è esteso ed è diventato pesante e ingestibile. Occorreva un nuovo sistema di gestione. Verso la metà degli anni 80 un gruppo di ricercatori sviluppò un sistema operativo (Mach) basato su microkernel. Lo scopo era quello di rimuovere tutti i componenti non essenziali dal kernel per alleggerirlo. Lo scopo fu raggiunto e ne conseguì un kernel molto più leggero.
Non c’è una regola che decide cosa va nel microkernel, tuttavia in generale un microkernel offre i servizi minimi di gestione dei processi, della memoria e di comunicazione. Lo scopo del microkernel è quello di mettere in comunicazione i processi che si trovano nello spazio utente. Grazie a questo sistema il microkernel può essere modificato lasciando invariato il sistema operativo, perché il sistema operativo utilizza il microkernel ma non è a conoscenza di come è strutturato, il microkernel fa quello che il sistema operativo gli dice prendendo dei parametri.
È per questo che i sistemi operativi basati su microkernel possono essere installati su più macchine. Il primo a farlo fu Steve Jobs che lo inserì nei suoi Mac quando furono create le nuove CPU a 64 bit e riuscendo ad adattare il suo sistema operativo a nuove architetture.
Ci sono altri tipi di sistemi che prevedono il caricamento di moduli. Questa implementazione è usata nei sistemi più moderni di LINUX, Solaris, Mac OS X e Windows. La struttura a moduli è composta di un modulo centrale nel quale ci sono le istruzioni alla base, come nel microkernel, e i moduli circostanti possono richiamare le funzioni base che sono al centro o altre funzioni presenti in altri moduli.
La struttura di Solaris si incentra su un kernel dotato dei seguenti sette tipi di moduli caricabili:
- Classi di scheduling
- File system
- Chiamate di sistema caricabili
- Format eseguibili
- Moduli STREAMS
- Varie
- Driver dei dispositivi e del bus
Il sistema operativo Mac OS X è un sistema stratificato, ecco come si compone:
Gli strati superiori comprendono l’interfaccia grafica e i servizi applicativi. Più sotto c’è il microkernel e il BSD. Il primo cura la gestione della memoria, le chiamate di procedure remote, la comunicazione tra processi e lo scheduling dei thread. Il secondo mette a disposizione un’interfaccia BSD a riga di comando, servizi legati a filesystem e alla rete e un’implementazione delle API POSIX.
Il sistema iOS che gira su iPhone e iPad ha lo stesso kernel del Mac ma non esegue le stesse applicazioni. Ecco la sua struttura:
- Cocoa touch
- Servizi multimediali
- Servizi di base
- Nucleo
Cocoa touch è un API per Objective-C che fornisce diversi framework per lo sviluppo di applicazioni che girano su dispositivi iOS, e fornisce supporto hardware per le caratteristiche peculiari dei dispositivi mobili, come il touch screen. Lo strato dei servizi multimediali fornisce servizi per la grafica, l’audio e il video. Lo strato dei servizi di base fornisce una varietà di funzioni tra cui il supporto per il cloud computing e i database. Lo strato inferiore rappresenta il nucleo del sistema operativo.
Il sistema Android invece è stato progettato dalla Open Handset Alliance (guidata principalmente da Google) ed è pensato per girare su una grossa quantità di dispositivi mobili. Alla base ha un kernel Linux opportunamente modificato per far girare una macchina virtuale Java pensata appositamente per dispositivi con una scarsa capacità di calcolo (dispositivi mobili) e possiede delle librerie per il web kit development, per database SQLite e altro.
I programmatori per Android programmano in Java alcuni senza sapere che sotto c’è un kernel Linux, e i programmi vengono prima compilati in bytecode Java e poi eseguiti sulla macchina virtuale Dalvik (così si chiama la macchina virtuale per Java pensata per dispositivi Android).
Hardware di input/output
Esistono 3 modi per prendere l’input:
- Input programmato (polling), che chiede in continuazione se ci sono dati disponibili per l’input o l’output;
- Input tramite interrupt, che legge l’input quando si verifica una condizione di interrupt come per esempio una scanf;
- DMA, Direct Memory Access, che è pensato per le periferiche che devono trasferire grosse quantità di dati come l’adapter video, la scheda di rete ecc.
Il controllo del sottosistema di gestione dei dispositivi di I/O è un aspetto molto importante, soprattutto considerando che esistono moltissimi tipi di periferiche I/O. Ultimamente si sta adottando uno standard universale di I/O in modo da rendere più compatibili i driver con altri dispositivi, ma è anche vero che stanno nascendo sempre più dispositivi di I/O, e questo rende più difficile questa standardizzazione.
I driver dei dispositivi offrono al sistema operativo un’interfaccia uniforme per l’accesso ai dispositivi, così come le chiamate a sistema offrono un’interfaccia uniforme tra le applicazioni e il sistema operativo.
I calcolatori fanno funzionare un gran numero di dispositivi, dispositivi di memorizzazione, di trasmissione, interfacce uomo-macchina (mouse, monitor, mouse) ecc. Altri dispositivi sono più specializzati, come i dispositivi di pilotaggio di un aeroplano.
Un dispositivo comunica con un sistema elaborativo inviando segnali attraverso cavi, l’etere o tramite un punto di connessione (porta seriale). Se più dispositivi condividono un insieme di fili all’ora questo insieme di fili è detto BUS. Quando un dispositivo A è collegato con un filo al dispositivo B che è collegato a un dispositivo C che a sua volta è collegato a una porta di un calcolatore, si ottiene il cosiddetto collegamento in daisy chain, che di solito funziona come un BUS.
I BUS differiscono tra di loro per formato dei segnali, velocità, throughput e metodo di connessione. Nella parte in alto a destra dell’immagine ci sono quattro dischi collegati a un BUS SCSI (Small Computer System Interface) con il relativo controllore. Un controllore è un insieme di componenti elettronici che può far funzionare una porta, un BUS o un dispositivo. Un controllore di porta seriale è un dispositivo integrato nella CPU che controlla lo stato dei fili della porta seriale.
Alcuni controllori non sono semplici, come il controllore SCSI, e perciò sono dotati di un controllore hardware che possiede una propria unità di elaborazione e dei propri registri. Osservando un HD si può notare sul retro una scheda, quello è il suo controllore e gli permette di eseguire operazioni come il prelievo anticipato (prefetching), la gestione del buffer e della cache e la rilevazione di settori difettosi.
L’unità di elaborazione fornisce comandi e dati per portare a termine le istruzioni di I/O. La comunicazione tra l’unità di elaborazione e il controllore avviene tramite la lettura o la scrittura, da parte dell’unità di elaborazione, di bit nei registri del controllore. Un modo in cui questa comunicazione può avvenire è tramite l’esecuzione di particolari istruzioni di I/O che specificano il trasferimento di un byte o una parola a un indirizzo di porta I/O. L’istruzione attiva le linee di comunicazione con quella periferica e legge o scrive.
In alternativa il controllore può supportare l’I/O memory mapped, cioè gli indirizzi di controllo del dispositivo sono mappati in un sottoinsieme degli indirizzi della CPU che esegue le richieste di I/O utilizzando le ordinarie istruzioni I/O. Alcuni sistemi utilizzano entrambe le tecniche.
Il controllore sta sulla mobo, o in alcuni HD è incorporato, mentre l’unità di elaborazione si trova sull’HD. Unità di elaborazione e controllore comunicano tramite speciali operazioni di I/O.
Processi
Un processo è un'istanza di un programma eseguibile in esecuzione. Non bisogna confondersi tra processo e programma. Un programma è un’entità passiva, un file eseguibile salvato in memoria, un processo è un’entità attiva, un programma che viene eseguito. Quando un programma viene eseguito diventa un processo. Un programma può essere aperto più volte diventando diversi processi. Anche se il programma è lo stesso
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
-
Sistemi operativi - Appunti
-
Appunti Sistemi Operativi - Seminari sistemi UAV , sistemi operativi NuttX-ROS
-
Sistemi operativi - Appunti
-
Appunti Sistemi operativi