Estratto del documento

Introduzione ai sistemi operativi

Perché studiamo i sistemi operativi?

Studiamo i sistemi operativi perché, essendo caratterizzati da un’architettura complessa, aiutano a comprendere la struttura articolata di un qualsiasi software in generale. D’altronde lo studio di questi è utile per scrivere programmi più efficienti, ottimizzando il codice e riuscendo a fare una programmazione di sistema (di basso livello), come ad esempio modificare un sistema operativo, lavorare ad un driver, aggiungere una funzionalità.

La programmazione concorrente nasce storicamente proprio con i sistemi operativi ed è stata poi riportata anche in altri contesti. Questa tipologia di programmazione si basa sulla realizzazione di programmi che possono essere eseguiti contemporaneamente sulla stessa CPU sfruttando i meccanismi del sistema operativo. Tutti i dispositivi che noi oggi utilizziamo sono multitasking: l’idea è sviluppare programmi fatti da flussi di esecuzione concorrenti nell’uso della medesima CPU (importante notare che è scorretto l’utilizzo di programmazione parallela, dato che questa viene utilizzata quando si dispone di CPU multi-core, con più unità di calcolo).

Ad esempio, quando usiamo Word, c’è il correttore ortografico, che corregge le parole mentre le scriviamo: un programma del genere si realizza attraverso tecniche di programmazione concorrente, è un insieme di task che girano insieme all’editore e leggono gli stessi dati che noi contemporaneamente scriviamo. I meccanismi per fare ciò sono offerti dal sistema operativo.

Definizioni

Un sistema operativo è un software, scritto pertanto in linguaggio di programmazione. Per la precisione è un insieme di moduli software interposti tra hardware e programmi di alto livello. Il sistema operativo definisce una macchina virtuale (o estesa) e quindi un insieme di astrazioni, che semplificano lo sviluppo delle applicazioni: ad esempio il sistema operativo fornisce il concetto di file, invece di quello di “insieme di byte memorizzati in un disco”.

Nella programmazione utilizziamo cout per visualizzare a video, quindi deve accadere che il testo scritto sul programma deve essere visualizzato sullo schermo, ed è proprio il sistema operativo che gestisce la comunicazione tra CPU, memoria e schermo stesso, quindi in effetti semplifica lo sviluppo del programma. In assenza del sistema operativo, avremmo dovuto programmare tutta la periferica. Quindi il sistema operativo offre una serie di astrazioni, per esempio il programmatore non si preoccupa della effettiva memoria RAM fisica disponibile. Il SO ci dà l’illusione di una CPU veloce, dà un accesso uniforme ad elementi eterogenei. Infatti, se consideriamo ad esempio le librerie di fstream, queste ci danno la possibilità di scrivere su un file, che sia memorizzato sul disco o su un dispositivo esterno come la pen-drive, allo stesso modo (utilizziamo fstream o fopen).

Il sistema operativo realizza politiche di gestione delle risorse hardware definendo i criteri con cui assegnare una risorsa a fronte di più richieste concorrenti. In un certo senso il sistema operativo fa da arbitro: un altro esempio è l’hard disk, da cui vengono caricate tutte le app. Il disco è sicuramente uno, conteso da tante applicazioni, motivo per cui il sistema operativo gestisce la risorsa.

Nella rappresentazione, si capisce come il sistema operativo sia uno strato che si interfaccia tra hardware, caratterizzato da CPU, memoria, dischi, terminali e così via, e librerie standard. L’utente si interfaccia con il sistema operativo utilizzando primitive semplici (open, close, read, write), ovvero mediante un’interfaccia detta system call.

Quando si esegue un servizio del sistema operativo, tutto il sistema, compreso l’hardware, esegue in una modalità privilegiata, detta kernel. Il sistema può in poche parole “fare qualcosa in più”, come interrogare i controllori delle periferiche o in generale operazioni che si possono effettuare solo delegando il sistema operativo. Negli altri casi la modalità di esecuzione del sistema si dice utente.

Storia

L’evoluzione che si è avuta nel corso degli anni è dovuta principalmente al costo dell’hardware, che è diminuito, al minore spazio che questo occupa e all’expertise degli utenti: anni fa, chiunque utilizzasse una macchina ne conosceva ogni singolo aspetto, al contrario di quanto accade oggi.

Single-user system

Il sistema operativo non esisteva, era essenzialmente caratterizzato da un loader e un insieme di librerie di procedure comuni, quindi c’era un minimo riuso di codice assembler. Il tempo in cui il processore era effettivamente occupato a fare calcoli era basso rispetto al tempo impiegato per stampare risultati, accenderlo e così via. Il problema era che l’IO era molto lento e predominante rispetto ad una CPU, che veniva utilizzata per l’1% del suo tempo.

Batch systems

Nasce l’esigenza di sistemi efficienti che porta alla luce i sistemi batch, in cui i calcoli da fare sono raggruppati in lotti (batch): un lotto viene caricato in memoria (supporto magnetico), mentre un altro viene eseguito. Le risorse erano sicuramente sfruttate meglio rispetto ai sistemi precedenti.

In questo caso il sistema operativo era costituito da loader, sequenziatore e processore di output, il programma che mandava in output un programma e poi lo terminava (questo concetto esiste ancora oggi, quando ad esempio stampo un file e chiudo Adobe, mentre la stampante stampa): spooling. Questo dimostra che, senza modificare l’hardware, ma con interventi software, si poteva utilizzare in maniera più efficiente il primo.

Tuttavia, se da un lato la macchina era meglio sfruttata grazie all’assenza di interazione con l’utente e le operazioni di I/O erano più veloci, lo svantaggio di questi sistemi era che gli utenti potevano attendere ore o giorni per avere i risultati dei loro lavori. Questi sistemi erano infatti monoprogrammati: sulla CPU si eseguiva un programma alla volta.

In generale i nostri programmi possono essere classificati come CPU bound (elevato utilizzo della CPU), e IO bound, programmi interattivi che fanno utilizzo di schermo, disco e rete. Le periferiche sono molto più lente, di un ordine di grandezza, rispetto alla CPU: in tutto questo tempo di attesa si potrebbero inserire altre operazioni. Questo concetto è alla base dei batch multi-programmati.

Al posto di caricare un solo lavoro, se ne caricano di più, lasciando un po’ di spazio al sistema operativo che iniziamo ad essere un programma. In questo modo quando il job1 di arresta per un’operazione di I/O, viene eseguito il job2. Ancora una volta l’hardware non è cambiato, ma vengono introdotte innovazioni come le interrupts. Nella foto a destra, si vede che quando il programma a è posto in attesa di un’operazione input/output, si può eseguire il programma b. In questo modo si riducono i tempi di attesa, si sfrutta la CPU e le risorse hardware al meglio.

Questo è ancora oggi il modo di gestire supercalcolatori (array di calcolatori) scientifici, sottomettendovi il job che verrà eseguito non appena arrivi il turno. I sistemi batch multi-programmati non hanno il concetto di avere un utente davanti, per cui sottomesso un lavoro, si aspetta il risultato. Oggi nei sistemi critici il risultato viene notificato mediante una mail.

In un sistema interattivo, con presenza di terminalisti, con una logica di programmazione batch, se uno dei terminalisti richiede un’operazione che prevede molti calcoli, pertanto un’operazione CPU bound, gli altri terminalisti non possono più utilizzare la macchina.

Time sharing system

Nascono allora questi sistemi: i job vengono eseguiti, ma il tempo di esecuzione è limitato. Quindi dopo il tempo stabilito vengono interrotti e in qualche modo i terminalisti hanno l’illusione di avere una macchina asservita a sé stessi. Ci sono più programmi in esecuzione, tuttavia la durata della run è limitata (time slice). Il sistema operativo dispone di una funzione di scheduling, algoritmo che decide chi interviene dopo l’interruzione di un programma. Al tempo stesso, chi scrive il programma non sa che effettivamente viene interrotto, astrazione che esiste ancora oggi.

La differenza tra batch e time è che i primi massimizzano l’uso del processore e presentano un’interfaccia JCL (Job Control Language), mentre i secondi vogliono minimizzare il tempo di risposta e portano il concetto di shell, comandi lanciati dal terminale. La gestione di memoria e protezione sono meccanismi hardware fondamentali per l’esecuzione del SO e abilitare multitasking e time sharing.

Richiami

Un sistema di calcolo è strutturato secondo il modello di VN. In figura sono evidenziati i principali componenti dell’architettura di un sistema mono-elaboratore: l’unità centrale, la memoria centrale, i vari dispositivi di ingresso/uscita. Tali componenti sono tra loro interconnessi tramite un bus di sistema.

Il processore esegue, una dopo l’altra, le istruzioni di un programma contenuto nella memoria centrale. Una caratteristica importante del processore è costituita dall’insieme dei registri interni, che si distinguono in registri generali o registri programmabili e in registri di stato e controllo.

I registri generali sono quelli utilizzati per contenere dati ed indirizzi di locazioni di memoria durante l’esecuzione di un programma (ad esempio accumulatori). Un particolare registro programmabile è il registro Stack Pointer, che è destinato a contenere l’indirizzo dell’ultimo dato (top) inserito nella pila utilizzato dal programma (stack). È un registro fondamentale perché al suo interno vengono allocate le variabili automatiche o le variabili scambiate tra un programma e sottoprogramma.

I registri di stato e controllo non sono visibili al programmatore e sono il Program Counter e il Program Status Word (PS). Il PC è destinato a contenere l’indirizzo in memoria della prossima istruzione da eseguire. È tramite questo registro che il processore implementa il suo ciclo di esecuzione delle istruzioni di un programma composto dalle seguenti fasi:

  • Fetch (prelievo) durante la quale il processore trasferisce in un registro interno (Instruction Register) l’istruzione da eseguire prelevandola dalla locazione di memoria il cui indirizzo è in PC e incrementa il contenuto del PC;
  • Decode;
  • Execute durante la quale l’istruzione viene eseguita, prelevando eventuali operandi dalla memoria o inserendovi eventualmente il risultato.

Il PS ha il compito di mantenere alcune informazioni sullo stato interno del processore, come per esempio i bit che condizionano le modalità di funzionamento del processore (kernel/user mode), il bit di carry (riporto di un’operazione aritmetica), o ancora la Interruption Mask, mediante le quale è possibile gestire il sistema delle interruzioni.

La memoria

La CPU, oltre a comunicare con i registri, parla anche con la memoria centrale. In generale, la memoria di un sistema di elaborazione si distingue in memoria volatile (RAM – Random Access Memory), memoria non volatile (ROM – Read Only Memory) e memoria di massa esterna. La memoria ROM contiene informazioni non modificabili, di solito essa contiene il programma di inizializzazione del sistema (bootstrap).

La memoria RAM è la memoria principale ed è utilizzata per contenere i programmi in esecuzione ed i loro dati, nonché il codice del sistema operativo e i suoi dati. Poiché l’accesso alla memoria RAM può rallentare la velocità con cui il processore esegue un programma, i processori di fascia alta implementano uno o più livelli di memoria cache, che è una memoria molto costosa e veloce, inserita tra il processore ed il bus di sistema. La cache contiene una “immagine” ridotta del contenuto della memoria principale. Ogni volta che il processore esegue un accesso in memoria, come prima cosa controlla se la locazione riferita si trova nella cache. Se la risposta è positiva (cache hit), alla cache si accede direttamente senza eseguire l’accesso in memoria, riducendo quindi la durata dell’operazione. Se invece la locazione riferita non è presente nella cache (cache miss), si effettua un accesso in memoria.

Il meccanismo di funzionamento della memoria cache sfrutta il principio di località dei riferimenti: secondo questo principio i riferimenti alla memoria da parte di un programma tendono ad essere vicini fra loro. Ad esempio, se consideriamo un programma che, durante la sua esecuzione esegue un ciclo while per un certo numero di volte, durante il quale il processore accederà allo stesso gruppo di istruzioni che fanno parte del ciclo. Quindi molto probabilmente, dopo la prima esecuzione del ciclo tutte le istruzioni si troveranno nella cache, quindi i cicli successivi saranno molto più veloci ed efficienti.

Per questo motivo, le locazioni di memoria non vengono memorizzate nella cache singolarmente ma in blocchi di locazioni contigue. “Il primo 90% del codice occupa il primo 90% del tempo di sviluppo. Il restante 10% del codice occupa il restante 90% del tempo di sviluppo”.

Ogni elemento della cache, atto a contenere un blocco di locazioni, è caratterizzato da tre elementi: un bit di validità (V), un campo detto tag, con cui viene identificato il blocco all’interno della cache e che è costituito da una parte dell’indirizzo fisico di tutte le locazioni del blocco (parte in comune a tutti gli indirizzi di quelle locazioni) e il contenuto delle locazioni del blocco. Ad esempio, se il blocco è composto da 32 locazioni, i loro indirizzi si differenziano per i valori relativi ai 5 bit meno significativi dell’indirizzo mentre i restanti bit più significativi dell’indirizzo sono ovviamente uguali per tutte le locazioni del blocco e costituiscono il campo tag.

Il bit di validità denota col suo valore la validità dei contenuti dell’elemento. Per esempio, all’inizio dell’esecuzione di un programma, quando ancora non è stato fatto nessun riferimento alla memoria, tutti gli elementi della cache sono invalidi e vengono caricati, e resi validi, ad ogni nuovo riferimento. Ovviamente, quando la cache è piena (tutti gli elementi sono validi) ed è necessario memorizzare un ulteriore blocco di locazioni, ne viene scelto uno il cui contenuto viene rimpiazzato.

All’atto dell’accesso ad una locazione di memoria è quindi necessario verificare per prima cosa, se tale locazione appartiene ad un blocco già presente in cache, e cioè se la porzione del suo indirizzo, corrispondente al tag, coincide col campo tag di un elemento della cache il cui bit di validità sia 1. Tale verifica viene normalmente effettuata in parallelo su tutti gli elementi della cache (accesso associativo).

Le differenti memorie di un processore sono organizzate secondo una gerarchia. Man mano che ci allontaniamo dal processore le dimensioni delle unità di memoria crescono, ma diminuiscono le velocità di accesso oltre che il costo di bit di tali unità. La memoria più veloce è costituita dai registri interni del processore, cui si può accedere in un solo ciclo di clock (orologio che invia un segnale periodico al processore, un’onda caratterizzata da periodo e frequenza, che nei moderni processori spazia dai MHz ai GHz).

Subito dopo, in termini di velocità viene la cache. Esistono anche più livelli di cache. Segue poi la memoria RAM che è più lenta, ma arriva fino a qualche Gb. Infine, ci sono i dischi magnetici (arrivano fino al terabyte, cioè 1012 byte) e i nastri magnetici che vengono utilizzati per memorizzare archivi di dati. Esistono la cache istruzioni (in sola lettura) e la cache dati (in lettura e scrittura).

È importante notare che la presenza di dati nella cache porta alcuni problemi, poiché i dati sono in lettura e scrittura e sono copie, quindi nella memoria centrale è sempre presente il dato. Quando la cache viene svuotata e il dato deve essere riportato in memoria, ci deve essere coerenza tra le due. Se qualcosa è stato sbagliato, si fanno continui trasferimenti inutili tra cache e memoria.

Dispositivi di ingresso/uscita

I dispositivi di ingresso/uscita costituiscono i componenti del sistema di elaborazione preposti al trasferimento di dati fra il sistema (unità centrale di elaborazione e memoria centrale) e l’ambiente esterno. Ogni dispositivo è collegato al bus del sistema tramite un particolare circuito detto controllore (dispositivo hardware), contenente una serie di registri ciascuno dei quali è individuato da un proprio indirizzo che è utilizzato dal processore per accedere al registro stesso. Eseguendo apposite istruzioni il processore è in grado di operare sui registri del controllore di un dispositivo e attivare quindi il dispositivo per effettuare un trasferimento di dati.

Le periferiche possono essere gestite con una delle due tecniche:

  • Memory-mapped: l’unità di controllo utilizza le stesse istruzioni per leggere e scrivere in memoria anche per accedere ai dispositivi di I/O. Il vantaggio è che non si richiede l’utilizzo di hardware aggiuntivo.
  • I/O mapped: vengono utilizzate istruzioni specifiche per l’input/output. I dispositivi hanno uno spazio di indirizzi separato da quello della memoria ed un segnale sul control bus serve all’unità di controllo per specificare se si tratta di un accesso in memoria o ad un dispositivo periferico.

Meccanismo d’interruzione

L’aspetto che maggiormente caratterizza il funzionamento di un dispositivo periferico è legato alla differenza di velocità con cui opera rispetto alla velocità con cui la CPU esegue le proprie istruzioni, velocità spesso inferiore di diversi ordini di grandezza.

Anteprima
Vedrai una selezione di 10 pagine su 222
Sistemi operativi - Teoria e Pratica Pag. 1 Sistemi operativi - Teoria e Pratica Pag. 2
Anteprima di 10 pagg. su 222.
Scarica il documento per vederlo tutto.
Sistemi operativi - Teoria e Pratica Pag. 6
Anteprima di 10 pagg. su 222.
Scarica il documento per vederlo tutto.
Sistemi operativi - Teoria e Pratica Pag. 11
Anteprima di 10 pagg. su 222.
Scarica il documento per vederlo tutto.
Sistemi operativi - Teoria e Pratica Pag. 16
Anteprima di 10 pagg. su 222.
Scarica il documento per vederlo tutto.
Sistemi operativi - Teoria e Pratica Pag. 21
Anteprima di 10 pagg. su 222.
Scarica il documento per vederlo tutto.
Sistemi operativi - Teoria e Pratica Pag. 26
Anteprima di 10 pagg. su 222.
Scarica il documento per vederlo tutto.
Sistemi operativi - Teoria e Pratica Pag. 31
Anteprima di 10 pagg. su 222.
Scarica il documento per vederlo tutto.
Sistemi operativi - Teoria e Pratica Pag. 36
Anteprima di 10 pagg. su 222.
Scarica il documento per vederlo tutto.
Sistemi operativi - Teoria e Pratica Pag. 41
1 su 222
D/illustrazione/soddisfatti o rimborsati
Acquista con carta o PayPal
Scarica i documenti tutte le volte che vuoi
Dettagli
SSD
Ingegneria industriale e dell'informazione ING-INF/05 Sistemi di elaborazione delle informazioni

I contenuti di questa pagina costituiscono rielaborazioni personali del Publisher alexxandras di informazioni apprese con la frequenza delle lezioni di Sistemi operativi e studio autonomo di eventuali libri di riferimento in preparazione dell'esame finale o della tesi. Non devono intendersi come materiale ufficiale dell'università Università degli studi di Napoli Federico II o del prof Cinque Marcello.
Appunti correlati Invia appunti e guadagna

Domande e risposte

Hai bisogno di aiuto?
Chiedi alla community