Articolo introduttivo sulla gestione della memoria da parte dei Sistemi Operativi, argomento che verrà trattato più approfonditamente in seguito.

Fra tutte le cose già viste un Sistema Operativo, per venire incontro ai bisogni dell’utente, deve integrare una capacità di gestione della memoria molto flessibile.

Il Sistema Operativo cerca di soddisfare queste richieste di gestione soddisfando 5 responsabilità:

  1. Isolamento dei processi: Come già ribadito parecchie volte, un SO deve evitare che due processi possano sovrascriversi l’un l’altro (sia la parte costante di istruzioni, che la parte dinamica di dati).
  2. Allocazione e gestione automatiche: Un programma dovrebbe essere allocato dinamicamente in base alla gerarchia di memoria, asseconda della richiesta. Perciò, un programmatore non deve pensare a problemi di memoria a cui deve pensare il SO che assegnerà tanta memoria quanta richiesta da ogni processo.
  3. Supporto della programmazione modulare: I programmatori devono essere in grado di suddividere i loro programmi in moduli, e di alterare dinamicamente la loro dimensione.
  4. Protezione e controllo degli accessi: Anche questo concetto l’abbiamo già visto in precedenza: la condisione della memoria può creare parecchi problemi se non gestita in maniera corretta. Il SO deve permettere l’accesso a determinate aree di memoria solo a determinati utenti/processi.
  5. Archiviazione a lungo termine: Molte applicazioni richiedono di occupare dello spazio (salvare dati) che rimarrà allocato anche dopo che il computer venga spento.

Ogni SO riesce a venire incontro a queste responsabilità, sfruttando due concetti:

  • La memoria virtuale (Virtual Memory)
  • Il File System

Il File System implementa una archiviazione a lungo termine con informazioni archiviate sottoforma di oggetti chiamati Files. Questo tipo di dato è conveniente sia per il programmatore, che lo riesce a gestire facilmente, sia per il SO che invece può sviluppare i concetti di archiviazione,  Protezione e Controllo degli accessi.

Per quanto riguarda la memoria virtuale, essa permette ai programmi di puntare ad indirizzi logici che quindi non sono aree di memoria reali: questa facilitazione permette di avere virtualmente più processi in memoria (o almeno di fargli credere che sia così) in modo da non avere interruzioni nel passaggio fra un processo all’altro.

Visto che però, per ovvi motivi di spazio, non è possibile avere fisicamente in memoria due processi molto grandi è stato introdotto il concetto di paging: ovvero la suddivisione di un processo in “pagine” di larghezza fissa che vengono caricate da una memoria più lenta (tipo l’hard disk) in memoria principale a seconda del bisogno.

Utilizzando però questa memoria virtuale, il processore pure punta ad indirizzi di memoria logici: è stato reso necessario l’aggiunta di altro hardware (il TLD – Translation lookaside buffer) che modifica gli indirizzi da logici a reali.

La memoria virtuale, ci permette di raggiungere la condivisione e la protezione della memoria: un processo separato dagli altri, avrà un’area di indirizzamento (virtuale) tutta sua. Se due processi vogliono condividere dello spazio, avranno un’area di indirizzamento comune, ed infine se vogliono modificare files (o porzioni di files) questi vengono caricati sulla memoria virtuale a loro disposizione.

Protezione delle informazioni e Sicurezza

Con l’aumento dei Time -sharing Systems e con la nascita dei Networks, il problema della sicurezza è venuto alla ribalta.

Tutti i lavori riguardanti la sicurezza dei sistemi operativi, possono essere raggruppati in 4 categorie:

  • Disponibilità: Protezione del sistema contro le interruzioni,
  • Confidenzialità: Bloccare l’accesso agli utenti non autorizzati,
  • Integrità dei dati: Protezione da modifiche non autorizzate dei dati,
  • Autenticità: Verifica della identità di ogni utente e della validità dei dati.

Gestione delle risorse e ordine di assegnazione

Una responsabilità chiave di un SO è di maneggiare le varie risorse disponibili  e di assegnarne l’ utilizzo ai vari processi attivi. L’ ordine di assegnazione e l’allocazione delle risorse, tiene in considerazione tre fattori:

  • Giustizia: Tipicamente vogliamo che processi della stessa importanza abbiamo una stessa assegnazione di risorse.
  • Differente Reattività: Nonostante il punto precedente, alcuni processi hanno necessariamente bisogno di maggiori risorse perchè necessitano di essere concluse il prima possibile.
  • Efficenza: Un SO deve massimizzare il più possibile l’utilizzo delle risorse, e cercare di soddisfare più utenti nel minor tempo possibile.

Un esempio di tecnica per lo scheduling dei processi, è quello di assegnargli il processore per breve tempo ad ogn’uno: questa tecnica è chiamata Round-Robin.

Un’altra potrebbe essere quella di assegnare una priorità ad ogni processo ed eseguirli in base ad essa.

Struttura di Sistema

Come già abbiamo detto nell’ introduzione ai Sistemi Operativi, una delle caratteristiche di un SO è quella dell’ evolbilità: ovvero deve essere facilmente aggiornabile.

Per questo motivo e per semplificare le cose, un SO viene suddiviso in moduli: questa organizzazione permette di modificare, aggiornare, o totalmente cambiare parti del SO senza aver bisogno di effettuare grosse modifiche sul resto del sistema operativo.

Nonostante questo comunque, la programmazione modulare non è sufficiente a gestire le milioni di linee di codice che formano un comune sistema operativo di oggi.

Per questo motivo, oltre che alla modularizzazione, si suddivide il sistema operativo in livelli (Layers) concetto che, per fare un pò di pubblicità, è simile a quello dello stack TCP/IP.

Idealmente comunque, ogni livello offre servizi al livello superiore (tranne quello a livello più alto), e sfrutta quelli del livello inferiore (tranne quello a livello più basso). Inoltre ogni livello deve, in rispetto alla modularizzazione, aggiungere delle funzionalità facilmente sostituibili (per aggiornamenti o modifiche quindi in funzioni analoghe).

La suddivisione del sistema operativo in una pila è difficile da fare con i sistemi operativi odierni visto che utilizzano strutture diverse, in ogni modo possiamo fare una generalizzazione:

Livello 1: Circuiti elettrici, registri, celle di memoria e porte logiche. Il livello più basso e più “fisico”.

Livello 2: L’ insieme di istruzioni del processore. Le operazioni a questo livello sono quelle permesse dal linguaggio macchina (operazioni matematiche load e store).

Livello 3: Aggiunge il concetto di Subroutine, quindi le funzioni call/ return

Livello 4: Introduce gli Interrupts, che permettono di salvare lo stato del processo e di gestire gli errori

Con il quarto livello finisce lo strato più basso dello stack, a questo punto cominciano a spuntare anche delle caratteristiche del SO:

Livello 5: La notazione di processo come programma in esecuzione è introdotta.  Questa è la funzione di base di un SO che supporti la multiprogrammazione e quindi comprende salvare lo stato del processo. Viene aggiunta inoltre la sincronizzazione fra processi

Livello 6: Si occupa dei sistemi di archiviazione secondari. Vengono introdotte quindi le funzioni per leggere e salvare dati in memoria che sfruttano la sincronizzazione fra processi del Livello 5 per sapere quando l’ operazione è terminata.

Livello 7: Crea gli indirizzi logici per i processi. Questo livello organizza la memoria virtuale e in blocchi che possono essere spostati fra la memoria principale e quella secondaria.

Arrivati a questo punto, abbiamo visto i livelli che si occupano della gestione delle risorse di un singolo processore. Da quì in poi però, passiamo a livelli con oggetti totalmente logici e tipici del SO.

Livello 8: Si occupa della comunicazione delle informazioni e dei messaggi fra i processi. Il livello 5 si occupa della sincronizzazione tramite semplice segnali, questo invece arricchisce tali segnali con molte più informazioni: uno degli strumenti più importanti è la Pipe (tubo)  un canale logico in cui fluiscono i dati per la comunicazione fra processi.

Livello 9: Aggiunge il supporto a lungo termine dei file salvati. A questo livello, i dati presente sui dispositivi secondari vengono visti di dimensione variabile nonostante la visione a “basso livello” (quindi più fisica) del livello 6 (che invece vede la memoria secondaria divisa in blocchi di settori di lunghezza prefissata).

Livello 10: Offre l’accesso a dispositivi esterni usando interfacce standardizzate.

Livello 11: E’ responsabile del mantenimento dell’ assocazione fra gli identificatori esterni ed interni degli oggetti e delle risorse del sistema.  Gli identificatori esterni sono nomi che possono essere assegnati dall’utente o da un programma, quelli interni sono puntatori utilizzati dai livelli più bassi.

Livello 12: Mette a disposizione tutti gli strumenti necessari per la gestione dei processi. Questo livello è completamente diverso da quello che troviamo a livello 5. Questo infatti offre tutte le informazioni necessarie sullo stato del processo (indirizzo virtuale), lista degli oggetti e dei processi che interagiscono con lui,  e tutte le caratteristiche necessarie al SO per gestire i processi.

Livello 13: L’interfaccia Utente- SO. Viene spesso conosciuta col nome di Shell (conchiglia) perchè separa il SO da tutte la sua complessità presentandolo come un insieme di Servizi. La shell accetta comandi utente e JCS (Job Control Statements).