Introduciamo con questo una serie di articoli dedicati ai Sistemi Operativi.

Chi segue InformaticaLab da un pò avrà sicuramente letto la precedente introduzione al funzionamento dei sistemi operativi: rileggerla male non vi fà 🙂

Sistema Operativo: Che cos’è e a che serve?

Un Sistema Operativo è un pezzo di software che permette di controllare l’esecuzione dei programmi applicativi e funge da interfaccia fra l’hardware e gli altri software.

Un Sistema Operativo si pone 3 obbiettivi:

  1. Convenienza: Deve permettere l’uso del PC in maniera più conveniente,
  2. Efficenza: Un SO deve saper sfruttare al meglio tutto l’hardware disponibile,
  3. Abilità di evloversi: Deve essere creato in modo da poter essere scalabile ed aggiornabile in modo da facilitare test e aggiunta di nuove funzioni – senza interrompere il servizio offerto.

I programmi sono insiemi di istruzioni che vengono trasformate in linguaggio macchina e poi date in pasto al nostro processore: grazie al SO che ci mette a disposizione tutta un insieme di servizi di sistema, un programmatore può scrivere del codice che può essere eseguito su macchine che utilizzano lo stesso SO (Infatti non possiamo usare programmi di Linux su Windows e viceversa).

Quindi il SO agisce come un’interfaccia fra il programma e l’hardware, e generalmente offre questi servizi:

  • Ambiente di sviluppo di programmi: Come già detto, il SO mette a disposizione un insieme di servizi come Editors e Debuggers per aiutare il programmatore nel processo di coding.
  • Esecuzione di Programmi: Per poter eseguire un programma, ci sono tutta una serie di passaggi da seguire a cui pensa il SO sollevando il programmatore dalla gestione di questa parte. Ad esempio, il caricamento in memoria dei dati e delle Istruzioni.
  • Accesso ai dispositivi I/O: Ogni dispositivo di I/O ha un insieme differente di istruzioni per gestire Input/Output: ancora, il SO funge da interfaccia rendendo la vita ai programmatori molto più semplice, generalizzando le letture e le scritture.
  • Accesso di Sistema: Per i sistemi pubblici o condivisi, il Sistema controlla l’accesso ai rispettivi contenuti e protegge da accessi non autorizzati.
  • Controllo Errori: Esistono moltissimi tipi di errori che possono accadere durante l’ esecuzione del SO.  Il SO non solo deve accorgersene, ma deve anche, di volta in volta, sapere come affrontare l’ errore.
  • Statistiche Utente: Un buon Sistema Operativo deve inoltre conoscere affondo i/il utente/i in modo da ottimizzare la gestione dell’ hardware. Veniva usato sopratutto una volta nei sistemi multiutente per sapere quante risorse erano state utilizzate da un utente e quanto doveva quindi pagare.

Breve storia dell’ evoluzione dei SO

All’inizio c’era il processing seriale: i programmi, scritti in linguaggio macchina, venivano caricati in memoria centrale attraverso un dispositivo di I/O. Se c’era un’errore nel programma, un’ apposita spia si accendeva: altrimenti il risultato veniva stampato con la stampante.

I problemi principali di questa metodica sono:

  1. Scheduling: Ogni utente che utilizzava il computer aveva blocchi temporali multipli di 30 minuti. Quindi se aveva bisogno del computer per 45 minuti, il computer rimaneva inutilizzato.
  2. Tempo di installazione: Per installare un programma, chiamato all’ epoca Job,  poteva essere necessario caricare il compilatore in memoria, salvare il programma compilato, caricare e linkare le funzioni del programma con quelle più comuni. Un errore? Si ricomincia da capo.

Visto che negli anni 60, periodo di cui stiamo parlando, i computer non erano molto economici ogni perdita di tempo in cui non veniva utilizzato erano un sacco di soldi sprecati: per questo motivo la mitica IBM crea il primo SO: un Simple Batch System che sfrutta il concetto di monitor.

Il monitor funziona in questo modo: esso risiedeva nella memoria centrale, e veniva programmato tramite JCL (Job Control Language). Esso comprendeva un’insieme di funzioni quali: gestione degli interrupts (eccezioni /errori), Drivers dei dispositivi, Processing dei Job ed un interprete per il JCL.

Il monitor legge un programma alla volta dai dispositivi di I/O, e questi erano programmati in modo da tornare al monitor (tramite un branch – istruzione assembly di salto) una volta che erano stati eseguiti ed il risultato di ogni Job veniva inviato alla stampante come output del programma.

Un esempio di programma in JCL è:

$JOB
$FTN
ISTRUZIONI FORTRAN
$LOAD
$RUN
DATA
$END

Le istruzioni JCL sono denotate dal segno del dollaro davanti al nome della funzione:

  • FTN: indica che ciò che segue è una lista di istruzioni in Fortran, che vengono lette e compilate dal monitor (che carica gli appositi strumenti)
  • LOAD: indica di caricare il risultato di FTN, ovvero un oggetto in linguaggio macchina, in memoria principale;
  • RUN: Una volta caricato l’oggetto in memoria, il Monitor gli “passa” il comando della CPU
  • Data: Fra $RUN e $END sono presenti i dati utilizzati dal programma in Fortran.

Alcune caratteristiche utili sono implementate anche via Hardware:

  • Protezione della Memoria: Un programma non deve scrivere nell’area di memoria dove risiede il SO. Se cerca di farlo, il processore se ne deve accorgere e viene inviato un errore facendo tornare il controllo al monitor.
  • Timer: Viene utilizzato un timer per evitare che un singolo Job monopolizzi la macchina: scaduto il tempo, il controllo torna al monitor.
  • Istruzioni Privilegiate: Ci sono alcune istruzioni che non possono essere eseguite dai job. Ad esempio, quelle di I/O: questo per evitare che vada a leggere per esempio altri job. Per fare ciò, deve utilizzare l’interfaccia offerta dal SO.
  • Interrupts( Eccezioni):  Questa funzione, che rende il SO più dinamico, non veniva implementata dai vecchi SO.

Per gestire le istruzioni privilegiate e la protezione della memoria, quello che normalmente si fà è eseguire i Jobs in modalità User Mode, mentre le istruzioni del monitor in modalità Kernel mode.

In questo modo il monitor poteva gestire tutte le aree di memoria ed eseguire le istruzioni che voleva, anche quelle precluse ai normali Jobs.

Uniprogramming e Multiprogramming

Da sempre la velocità di computazione supera di gran lunga la capacità di lettura dai dispositivi di I/O. Questo vuol dire che se viene eseguito un programma per volta e questo richiede spesso dei dati presenti su un dispositivo di I/O, la cpu starà con le braccia conserte per un sacco di tempo!

Per questo motivo è stata pensata la Multiprogrammazione (o più comunemente, Multitasking). In questo modo, mentre il Job A aspetta una risposta da un Dispositivo di I/O, un Job B può essere eseguito.

Quando A riceverà la risposta che aspettava, la priorità tornerà a lui che continuerà ad essere esegutio.

Questo sistema permette di migliorare di moltissimo l’utilizzo delle risorse del computer.

Time-Sharing System

Questo sistema è stato creato al MIT per bisogno: al giorno d’oggi abbiamo più dispositivi per persona. Una volta, non era così. Come abbiamo visto mentre parlavamo delle funzioni di accounting, una volta un sistema era condiviso da più utenti.

Per permettere a tutti di usufruire dello stesso terminale,  quei geniacci hanno inventato il CTSS (Compatible Time- Sharing System un sistema utilizzato anche nei moderni sistemi operativi (chiaramente in maniera più intelligente).

In pratica quello che faceva era dividere in slice temporali l’utilizzo della cpu: ad ogni colpo di clock, il controllo tornava all’ OS che assegnava la CPU ad un nuovo utente.

Non è solo questa una delle innovazioni: esso infatti faceva un gioco molto intelligente. Divideva i Jobs a “pezzi” e sovrascriveva in memoria solo le parti di cui aveva bisogno.

Ad esempio, con uno spazio di 100 words (escludendo il SO) se un Job A  presente in memoria occupava 60 Words, e doveva essere caricato un Job B da 50 words, venivano sostituite solo 10 words di A, in modo da velocizzare di molto il passaggio da un programma all’altro.

Questa tecnica però, ha aggiunto alcuni livelli di difficoltà nella creazione del SO: ad esempio trovandosi in memoria più di un programma infatti, bisogna che ci sia una protezione in modo che non si sovrascrivino a vincenda, e che quindi ogni processo possa accedere alle sue aree di memoria.