mercoledì 2 dicembre 2015

Main Memory :: II

Allocazione contigua
La memoria principale deve supportare sia processi del SO che dell'utente.
Ed è solitamente divisa in due partizioni: una per il sistema operativo che contiene anche i vettori per gli interrupt e l'altra per i processi.

Quando la CPU chiede accesso ad un indirizzo logico, prima lo confronta con il registro limite(per evitare trap di addressing error), viene calcolato l'indirizzo fisico e si va in memoria.

Allocazione partizione multipla
Dynamic Storage-Allocation Problem
First fit: alloca nel primo hole che è grande abbastanza.
Best-fit: alloca nell’hole più piccolo che è abbastanza grande; deve quindi cercare nell'intera lista.
Worst-fit: alloca nell’hole più grande, nonostante non rispecchi le esigenze del processo; deve quindi cercare nell'intera lista.

First fit e Best-fit sono più efficienti in termini di velocità.


Frammentazione
La frammentazione può essere distinta in esterna ed interna.
La frammentazione esterna si manifesta quando in memoria c’è abbastanza spazio libero da allocare a un nuovo processo, ma non si presenta in maniera contigua.
Questo può essere un problema serio, e nel peggiore dei casi, ogni due processi potrebbe esserci un blocco di memoria inutilizzabile.
Poiché potrebbero rimanere frammenti molto piccoli - anche solo di un paio di byte - e per tenerne traccia se ne impiegherebbero molti di più, si tende ad evitare questo problema, dividendo la memoria in blocchi fissati. Si alloca per tutta la dimensione di questi, e la memoria occupata sarà leggermente più del necessario.
La differenza fra la memoria totale occupata dal blocco e quella realmente impiegata dal processo, viene chiamata frammentazione interna.
L’obiettivo della compattazione è quello di compattare la memoria libera in un unico blocco.
Questo tuttavia non è sempre possibile. Se la riallocazione è fatta in assembly o load time, ed è quindi statica questo non può essere fatto. In quanto gli indirizzi assegnati al codice saranno assoluti e non è possibile spostati eventuali blocchi.
Per quanto riguarda la riallocazione dinamica, è possibile spostare i blocchi, sostituendo solo l’indirizzo di base del registro a cui ci si riferisce, poiché gli indirizzi interni sono relativi.


Segmentazione
I programmatori non vedono necessariamente la memoria come un array di bytes, qualcuno contente dati e altro istruzioni.
Piuttosto preferiscono vederla come una collezione di segmenti, non necessariamente ordinati.
Un segmento è un'unità logica come: programma principale, procedura, funzione, metodo, oggetto, variabili locali, variabili globali, blocco comune, stack, tabella di simboli, array...

Ogni blocco di un programma ha degli indirizzi relativi, una volta che il programma viene eseguito è come se fossero messi in maniera contigua, pur essendo in spazi di memoria fisica non contigui.
Tuttavia la segmentazione non risolve del tutto il problema della frammentazione, poiché persiste, sebbene in una scala più piccola (la dimensione degli holes inutilizzabili è minore).


Architettura della segmentazione
Questo tipo di implementazione necessita di un hardware specifico, in quanto l'indirizzo logico consiste di due valori:
Normalmente quando un programma è compilato, il compilatore costruisce automaticamente segmenti che riflettono il programma di input.
Il programmatore può ora riferirsi a oggetti nel programma attraverso un indirizzo bidimensionale, ma l’attuale memoria fisica è ancora una sequenza unidirezionale di bytes.
Si deve quindi definire un’implementazione per una mappa bidimensionale degli indirizzi definiti dal programmatore che si riferisca a quella unidimensionale degli indirizzi fisici.
Questa mappatura è effettuata con una tabella di segmenti, in cui ogni entry nel tabella dei segmenti ha un una base e un limite.
La base del segmento contiene l’indirizzo fisico di partenza dove il segmento risiede in memoria, mentre il limite specifica la lunghezza del segmento.
Un indirizzo logico come detto prima, consiste in due parti: un numero di segmento(s) e un offset (d) all’interno dello stesso.
Tale offset deve essere un numero da zero al limite del segmento. Se non lo è, viene inviato un trap al sistema operativo (poiché l’indirizzo logico prova ad andare oltre il termine del segmento). Mentre quando l’offset è legale, viene sommato alla base del segmento per produrre l’indirizzo nella memoria fisica del byte desiderato. La tabella dei segmenti è essenzialmente un array di registri di basi (Segment table base register (STBR)) e di limiti (Segment table length register (STLR)).
/* segment numer s is legal if s < STLR */

martedì 1 dicembre 2015

Main Memory :: I

Address Binding
Quando si scrive codice non ci si interessa direttamente di cosa succede a livello di memoria.

Le implementazioni dell'address binding possono essere nelle seguenti modalità:
- Compile time
Se si conosce a priori, in fase di compilazione, la locazione che dovrà avere la memoria, è possibile generare un codice assoluto. Nel caso in cui gli indirizzi di partenza cambino è necessario ricompilare.
- Load time
  In questo caso la memoria non è conosciuta quando si compila e bisogna generare codice rilocabile.
- Execution time
Qui la memoria viene definita solo all'esecuzione e può essere spostata da un segmento di memoria a un altro. In questo caso necessitiamo di un supporto hardware che consenta di mappare indirizzi.


Il concetto di uno spazio di indirizzi logici, che è limitato a uno spazio di indirizzi fisici, è centrale nella gestione della memoria.
Gli indirizzi fisici sono visti dall'unità di memoria.
Gli indirizzi logici sono generati dalla CPU e si riferiscono anche all'indirizzo virtuale.

Quando l'address binding viene fatto in execution time allora gli indirizzi vengono fissati dalla CPU istruzione per istruzione.

La differenza tra logico e fisico è soltanto in execution time, poiché l'istruzione in memoria ha un indirizzo assoluto, ma è relativo al punto in cui è presente l'istruzione nel codice.


(Memory-Management Unit)
La MMU è l'unità che si occupa in maniera diretta del processo di binding tra indirizzo fisico e indirizzo logico.
Estremamente veloce istruzione per istruzione, a ogni colpo di clock viene fatto almeno un address binding.
Ai bits relativi all'indirizzo vengono sommati dalla MMU e viene assegnato poi alla posizione assoluta.

L'utente non si deve mai occupare della memoria.
E' il dispositivo hardware che sta a runtime, che agli indirizzi virtuali, deve mappare quelli fisici.

Dynamic Linking
Si usa in quasi i tutti i programmi moderni, perché i SO moderni contengono molte librerie condivise
All'interno del codice vengono inserite delle piccole stub(routine) e al posto del codice per l'apertura di un file jpeg, ad esempio, vengono richiamate le libreria di sistema.
Un programma quindi sarà composto da vari moduli già caricati in memoria che verranno richiamati dal nucleo del programma.

Questo prevede che uno stesso programma sia composto da un blocco principale che è il nostro e da altri blocchi che sono le librerie di sistema.

Swapping
Un processo può essere swappato temporaneamente fuori dalla memoria al backing store e poi viene riporto indietro in memoria per continuare l'esecuzione...
I sistemi operativi moderni usano lo swapping quando la memoria è molto piena.

Il backing store è un disco veloce e abbastanza grande da contenere copie di tutte le immagini della memoria per tutti gli utenti e deve fornirne accesso diretto
Per quanto riguarda i sistemi mobili tipicamente non ci sono meccanismi di Roll out, roll in.

martedì 24 novembre 2015

Tratti generali dei S.O.

Uno degli aspetti più importanti di un s.o. è l’abilità di multiprogrammazione. Un singolo programma, in generale, non tiene occupati la CPU o il dispositivo di I/O per tutto il tempo dell’esecuzione.
Poiché la maggior parte degli utenti ha diversi programmi contemporaneamente in esecuzione, si utilizza il multiprogramming per incrementare l’utilizzo della CPU, organizzando i jobs affinché la CPU ne abbia sempre uno da eseguire. L’idea è quella di tenere molti jobs in memoria in maniera simultanea, ma poiché la memoria principale di solito è troppo piccola per contenerli tutti, vengono tenuti nel disco, nel job pool contenente tutti i processi che aspettano di essere allocati in memoria. I sistemi multiprogrammed forniscono un ambiente nel quale le varie risorse sono effettivamente utilizzate, ma non forniscono un’interazione per l’utente con il sistema.

Multitasking
Il multitasking (detto anche time sharing) è un’estensione logica del multiprogramming. In questi sistemi la CPU esegue jobs multipli passando da uno all’altro, in una velocità tale che gli utenti possano interagire con ogni programma nel corso dell’esecuzione.
Il time sharing richiede un sistema computer interattivo (o hands-on) che fornisce una comunicazione diretta tra l’utente e il sistema, che da istruzioni attraverso un dispositivo di input e aspetta l’immediata risposta sul dispositivo di output. Per questo motivo il response time dovrebbe essere tipicamente inferiore a un secondo.
Un sistema di questo tipo deve fornire: un file di sistema; una gestione dei dischi; un meccanismo di protezione delle risorse da un utilizzo improprio; meccanismi per la sincronizzazione dei jobs; meccanismi di comunicazioni; garanzie per non incappare in deadlocks.

Un programma caricato in memoria e in esecuzione è detto processo. Se ci sono diversi jobs pronti da caricare in memoria e non vi è lo spazio necessario per tutti, il sistema deve fare la scelta di quali caricare. Questa scelta è data dal job scheduling. Se invece più processi sono già pronti in memoria, la scelta è data dal CPU scheduling.
Per garantire una certa velocità nel response time, si può utilizzare il meccanismo di swapping, in cui i processi sono passati all’interno e all’esterno, dalla memoria principale, al disco.

Virtual Memory
Un altro metodo è il virtual memory, una sorta di astrazione della memoria principale in un array di storage uniforme, separando la memoria logica, per com’è vista dall’utente, da quella fisica. Questo consente l’esecuzione di un processo sebbene non sia del tutto caricato in memoria. Il quale vantaggio principale è quello di permettere il caricamento di programmi più grandi dell’attuale memoria fisica, per cui i programmatori non devono preoccuparsi delle limitazioni di memoria.