APPUNTI
DI
SISTEMI OPERATIVI
ARCHITETTURA DI UN SISTEMA A MICROPROCESSORE
Introduzione
Agli inizi degli anni '60 comparvero sul mercato i primi circuiti integrati digitali. Servendosi di
tali dispositivi divenne più semplice la progettazione e la realizzazione di sistemi logici.
Utilizzando la tecnica denominata a logica cablata, i sistemi venivano progettati utilizzando i
circuiti integrati digitali offerti dal mercato ( porte logiche, registri, contatori, ecc.) in funzione
dell'applicazione specifica. Inoltre, aumentando sempre più il numero di componenti integrati su
un unico chip di silicio, ovvero le funzioni logiche che il singolo circuito integrato era in grado di
svolgere, divennero sempre più complesse e sofisticate le funzioni disponibili. Tuttavia il costo
del dispositivo integrato aumentava con la sua complessità, e solo una sua larga diffusione
permetteva l'abbattimento del costo di produzione, cosa non sempre possibile in quanto esso
spesso era rivolto ad utilizzazioni specifiche. Nacque pertanto ben presto la necessità di disporre
di un unico dispositivo che permettesse di essere utilizzato in applicazioni diverse con poche
modifiche. Sulla base di queste motivazioni venne realizzato agli inizi degli anni '70 un circuito
integrato cui fu dato il nome di microprocessore (C.P.U. = Central Process Unit). Tale
dispositivo aveva bisogno di essere programmato, cioè era necessario fornirgli una successione
ordinata di istruzioni sul modo di operare. Il microprocessore può quindi essere considerato un
dispositivo adatto a svolgere funzioni diverse, modificando solo in modo minimo la parte
circuitale ( hardware ) ad esso connessa, ma variando di volta in volta, in base all'applicazione
richiesta, il programma di gestione ( software ). I sistemi realizzati con l'applicazione di una CPU
sono detti a logica programmabile.
Il microprocessore può essere utilizzato in una vastissima gamma di applicazioni che vanno dai
sistemi di controllo, alla strumentazione di misura, dalle apparecchiature domestiche quali
lavastoviglie e televisori a quelle elettromedicali, senza dimenticare una delle più diffuse
applicazioni, ovvero il Personal Computer.
Architettura di un sistema a microprocessore
Un microprocessore in genere, pur contenendo al suo interno tutti i circuiti di calcolo e di
controllo, per poter operare correttamente ha bisogno di essere collegato con altri dispositivi in
base alle applicazioni per cui il processore è impiegato (da solo non è utilizzabile). Al
microprocessore debbono essere collegati sia dei moduli di memoria che i dispositivi per
l’ingresso e l'uscita dei dati. E dunque necessario parlare di sistema a microprocessore e non
semplicemente di microprocessore. Nella figura è riportato un semplice schema di sistema con
memorie e dispositivi di I/O. In esso è evidenziata la funzione di collegamento tra i vari
componenti svolta dai bus dati, bus indirizzi e bus di controllo.
2
bus indirizzi
R/W
interfacce
dispositivi
input
memoria
CPU
interfacce
dispositivi
output
bus dati
nmi
clock
input
interrupt reset
output
Il sistema è costituito da una CPU da memorie RAM e ROM da uno o più dispositivi d'ingresso
e d'uscita. Lo schema può servire per comprendere come il microprocessore possa scambiare
informazioni con la memoria e con i dispositivi di I/O (Input / Output).
Mediante il BUS INDIRIZZI vengono identificate le locazioni di memoria dove vengono
memorizzati i dati nelle operazioni di scrittura o da cui la CPU riceve i dati (o i codici delle
istruzioni, cioè il programma) nelle operazioni di lettura. Inoltre, sempre per mezzo del bus
indirizzi il microprocessore identifica i dispositivi d'ingresso, ad esempio la tastiera di un
computer, da cui riceve i dati nello operazioni di lettura da periferica o quelli d'uscita, ad esempio
il monitor di un computer, a cui invia i dati nelle operazioni di scrittura su periferica. Il bus dati è
utilizzato per il trasferimento (lettura o scrittura) dei dati dalla CPU con le locazioni di memoria o
con i dispositivi di ingresso / uscita (detti anche periferici o dispositivi di I/O). Il bus controlli,
oltre a funzioni specifiche che non saranno qui esplicitate, è utilizzato dalla CPU per segnalare
alla memoria e ai dispositivi di I/O la direzione del flusso dei dati e il tipo di dispositivo coinvolto
nel trasferimento (memoria o I/O).
L’architettura a BUS presenta diversi vantaggi il principale dei quali è dato dalla modularità del
sistema; è infatti particolarmente semplice ampliare il sistema con l’aggiunta di nuovi componenti
collegandoli ai BUS. Tale vantaggio risulta evidente pensando alle schede madri dei Personal
Computer, dove attraverso gli slot di espansione è possibile ampliare le prestazioni e le
caratteristiche del sistema attraverso nuove schede (es: modem, acquisizione dati, porte parallela)
o nuovi chip di memoria. I diversi “oggetti” sono collegati ai bus con una modalità che consenta
di evitare conflitti e/o indecisioni nella comunicazione che avviene sempre tra due soli
componenti del sistema e quasi sempre uno dei due è il microprocessore.
BUS DATI (bidirezionale)
3
Bus controllo
Lo schema di figura (dove per semplicità si sono rappresentate solo 4 linee per bus) mette in
evidenza i circuiti di decodifica che, generando dei segnali di chip select, permettono di
selezionare i diversi dispositivi. I segnali CS permettono di togliere il componente dallo stato di
alta impedenza (che corrisponde al virtuale scollegamento del componente dal bus dati)
garantendo la corretta comunicazione solo tra due componenti. Altri segnali particolarmente
importanti per la comunicazione provengono dal BUS CONTROLLI e sono i segnali di READ e
WRITE, pilotati dalla CPU, e specificano la direzione dei dati nel trasferimento.
Elementi caratteristici di una CPU
La CPU costituisce il cuore e l’elemento più importante del sistema; in esso sono rappresentate le
funzioni logiche, di controllo e aritmetiche. La CPU è in grado di prelevare le istruzioni del
programma da svolgere contenute nella memoria (fase di fetch) e di interpretarle ed eseguirle
(fase di execute). Tale istruzioni sono evidentemente in forma binaria. Grazie ai programmi
compilatori è possibile scrivere programmi per Personal Computer utilizzando linguaggi
particolarmente semplici ed intuitivi (linguaggi ad alto livello) che vengono poi tradotti nell’unico
linguaggio interpretato ed eseguito dalla CPU che viene denominato linguaggio macchina. Le
principali caratteristiche di una CPU che ne identificano le peculiarità e la potenza possono essere
cosi sintetizzate:
Set istruzioni, cioè l’insieme delle istruzioni che la CPU è in grado di svolgere;
Velocità di funzionamento ed esecuzione delle istruzioni; si identificano tali caratteristiche
attraverso il clock di sistema (frequenza di funzionamento passata negli anni da qualche MHz a
centina di Mhz) e il MIPS (milioni di istruzioni eseguite in un secondo).
Architettura interna, insieme dei registri di lavoro, coprocessore matematico per svolgere le
operazioni logico aritmetiche di una certa complessità, memoria cache per il reperimento veloce
4
delle istruzioni
I registri sono memorie di pochi bit ( 8, 16, 32 o 64 ) contenuti fisicamente nella CPU che vengono
utilizzati come memoria di transito per appoggiare provvisoriamente i dati ( indirizi, dati o
istruzioni ). Essi consentono un funzionamento più efficiente della CPU, evitando l’accesso
continuo alla memoria esterna che dovrebbe avvenire tramite i bus e ciò porterebbe ad un impiego
maggiore di tempo.
L'Unità di Controllo. è l'organo o unità che gestisce, controlla e presiede l'esecuzione di tutte le
operazioni di elaborazione per il particolare programma da eseguire ovvero comanda tutte le altre
parti del processore attraverso il pilotaggio dei componenti stessi (ALU ecc..) impartendo a questi
comandi di input e facendo da supervisore; rappresenta la parte a logica sequenziale della
macchina a stati generale che, a sua volta, rappresenta la logica elettronica generale del processore
stesso. Ad essa spetta, ad esempio,
• l'interpretazione dell'istruzione che si trova di volta in volta nel registro IR;
• abilitare alla lettura ed alla scrittura due registri tra i quali deve avvenire uno scambio di
informazione.
L'unità di controllo scandisce i passi o stati di un'istruzione:
1. fetch: preleva l’ istruzione dalla memoria e determina il tipo di istruzione, i suoi
argomenti
2. execute: esegue l’istruzione, memorizza i risultati, e torna a 1.
1.
Tutte le fasi del ciclo del processore avvengono attraverso l'invio ai vari componenti di un
insieme di impulsi di controllo, in una sequenza temporale ben precisa. Più precisamente, ad ogni
colpo di clock le linee di controllo assumono un particolare stato; il susseguirsi dei diversi stati
contribuisce all'esecuzione completa di un'istruzione. Per questo motivo si può dire che una
5
singola istruzione in linguaggio macchina viene eseguita attraverso la opportuna composizione di
più micro-operazioni.
ALU., l'unità logico-aritmetica, è l'organo deputato allo svolgimento di
• operazioni aritmetiche ( somma, sottrazione, moltiplicazione, divisione, cambio di segno );
• operazioni logiche ( or, and, xor, not );
• confronti;
• operazioni di scorrimenti e rotazione a sinistra e a destra di un dato;.
Essa preleva gli operandi tipicamente dai registri generali, così come nei registri generali depone
i risultati dei calcoli. In seguito ad un calcolo l'ALU ha anche il compito di impostare alcuni flag
in modo da tenere traccia di determinati eventi (es. riporto di una somma).
La Memoria. Contiene un numero generalmente molto elevato di celle nelle quali vengono
memorizzati i dati e le istruzioni di un programma. Ogni cella è caratterizzata da un indirizzo (
intero positivo ) specificando il quale è possibile leggere o scrivere nella cella stessa. Il tempo
impiegato per accedere ad una cella di memoria è costante e superiore a quello impiegato per
l'accesso ad uno dei registri del processore. È per questo motivo che, per quanto possibile, si tenta
di utilizzare i registri interni per effettuare le operazioni, limitando gli accessi in memoria allo
stretto necessario. Pur contenendo la memoria un numero molto elevato di celle, in ciascun istante
temporale solo uno di questi è abilitato a partecipare ad operazioni di lettura o scrittura: quello il
cui indirizzo è contenuto nel registro MAR. Per sopperire alla lentezza della memoria RAM è
stata introdotta anche la memoria cache.
Internal Bus. È un canale di comunicazione principale condiviso dai vari componenti ed
attraverso il quale essi possono dialogare scambiandosi informazioni quali comandi di input,
output ecc.. In questo contesto, il dialogo consiste nello scambio di dati binari tra registri secondo
una modalità parallela. Ciò significa che un certo numero di bit viene contemporaneamente
trasferito attraverso il bus da un registro mittente ad un registro destinatario. Durante
un'operazione di trasferimento, i due registri implicati nella comunicazione si trovano in uno stato
di lettura (destinatario) e scrittura (mittente) in modo tale da poter acquisire il dato presente sul
bus e da potercelo scrivere, rispettivamente. Tutti gli altri registri sono in uno stato di “riposo” nel
quale non possono né leggere i dati che circolano sul bus né influenzare lo stato del bus con i dati
che contengono. Il numero di bit contemporaneamente trasferiti indica il parallelismo del bus ed
è pari al numero di bit contenuti in un singolo registro. Esso caratterizza anche il parallelismo
interno del processore.
Il Data Bus e il registro MDR. Il Data Bus è un bus che collega la memoria con il registro MDR
( Memory Data Register ). Esso serve a trasferire dati in entrambi i sensi, sempre secondo una
modalità parallela. Tutti i dati e le istruzioni che dalla memoria devono essere elaborati nel
processore, transitano inoltre attraverso il registro MDR e solo successivamente da questo
raggiungono gli opportuni registri per l'elaborazione vera e propria. Analogamente, tutti i risultati
(output) di un'elaborazione che devono essere immagazzinati in memoria transitano prima per il
registro MDR e solo successivamente da esso raggiungono l'esatta posizione (cella) di memoria.
L'Address Bus e il registro MAR. Durante un accesso alla memoria, sia in fase di lettura che in
fase di scrittura, il registro MAR ( Memory Address Register ) contiene l'indirizzo della cella di
memoria a cui si deve accedere. Questo indirizzo, trasferito all'organo memoria attraverso
l'Address Bus, abilita alla comunicazione una sola tra tutte le locazioni di memoria (celle)
disponibili.
Il registro PC. (Program Counter). Contiene l’indirizzo di memoria in cui è contenuta la
prossima istruzione da eseguire. Esso viene interrogato tipicamente all'inizio di ogni fase di fatch
ed immediatamente dopo viene aggiornato alla posizione di memoria “seguente” preparandolo
così per il prelievo dell'istruzione successiva. Può accadere comunque che l'istruzione prelevata
6
rientri nella categoria delle istruzioni di salto: in questo caso si procede ad un ulteriore
aggiornamento del PC durante la fase di execute dell'istruzione. Da questo deriva che lo scopo di
un'istruzione di salto (condizionato) è esclusivamente quello di alterare (eventualmente) il valore
del PC. Spesso il registro PC è chiamato anche IP (Instruction Pointer).
Il registro IR ( Instruction Register ). Questo registro ha il compito di accogliere dalla memoria
(attraverso il MDR), durante una fase di fetch, l'istruzione da eseguire, quella cioè puntata dal PC.
Una volta in questo registro, l'istruzione deve essere interpretata dall'unità di controllo per
procedere alla eventuale fase di preparazione degli operandi ed alla fase di esecuzione.
Il registro di stato o dei flag è un registro che memorizza una serie di bit detti flag indicativi dello
stato corrente del processore. Ad esempio:
ZF = Zero flag (o flag zero). Indica se il risultato di un'operazione matematica o logica è zero.
CF = Carry flag (o flag di riporto). Indica se il risultato di un'operazione produce una risposta non
contenibile nei bit usati per il calcolo.
SF = Sign flag (flag di segno). Indica se il risultato di un'operazione matematica è negativo.
OF = Overflow flag. Indica se il risultato di un'operazione è in ovwrflow, secondo la
rappresentazione in complemento a 2. È simile al carry flag, ma viene impiegato nelle operazioni in
cui è presente il segno degli operandi.
IF = Interrupt enable flag (o flag di abilitazione dell'interruzione). Gli interrupt possono essere
abilitati ponendo a 1 questo flag, se 0 allora sono disabilitati.
PF = Parity flag (o flag di parità). Indica se il numero di bit del risultato è pari o dispari.
I registri generali I registri generali non hanno un preciso ruolo come gli altri, e da ciò scaturisce il
loro nome. Sono utilizzati per contenere i dati in transito per un'elaborazione: gli addendi di
un'addizione che l'ALU sta per effettuare, il risultato di un calcolo che l'ALU ha effettuato, un
indirizzo di memoria in cui si trova un dato che dovrà essere acceduto in seguito, ecc. Un numero
elevato di tali registri conferisce maggiore flessibilità nella programmazione, ma complica la
struttura del processore dal punto di vista architetturale.
Modalità di esecuzione di un’istruzione
Dopo aver esaminato le caratteristiche degli elementi che compongono un generico sistema a
microprocessore vediamo come essi siano coinvolti nell’esecuzione di una istruzione. Una
generica istruzione è formata:
Codice operativo
Codice operando o campo dati
Il codice operativo rappresenta l’azione che la CPU deve eseguire sugli operandi ( registri o dati ).
Ad esempio
0110 mov ax, 30
0111
0112 inc ax
3E
30
3C
; prima istruzione
; seconda istruzione
Il codice operativo della prima istruzione è 3E mentre il campo dati è il numero 30 e nell’
insieme l’istruzione è lunga 2 byte.
La seconda istruzione è lunga 1 byte . Nel complesso la prima istruzione pone nel registro a 16
bit AX il dato 30, la seconda lo incrementa di 1. Dopo l’esecuzione della seconda istruzione in
AX avremo 31.
Fase di fetch della prima istruzione
La CPU deve prelevare la prima istruzione il cui indirizzo 0110 è nel registro PC quindi:
1.
Il contenuto di PC 0110 viene posto sul bus indirizzi
7
2.
Il contenuto della cella 0110 viene posto sul bus dati;
3.
L’istruzione trasferita nel registro IR viene decodificata, permettendo alla logica di
controllo di generare i segnali necessari a eseguire effettivamente l’istruzione;
4.
Il contenuto di PC viene incrementato di 1 ( il PC “punta” al dato 30 )
Fase di execute della prima istruzione
1.
Il contenuto del PC (0111) viene posto sul bus indirizzi, “puntando così alla cella che
contiene l’operando 30;
2.
Il contenuto della cella 0111 viene posto sul bus dati;
3.
il dato dal bus dati viene trasferito in AX
Fase di fetch della seconda istruzione
1.
Il contenuto di PC (0112) viene posto sul bus indirizzi;
2.
Il contenuto della cella 0112 viene posto sul bus dati;
3.
L’istruzione trasferita nel registro IR viene decodificata, permettendo alla logica di
controllo di generare i segnali necessari a eseguire effettivamente l’istruzione;
4.
Il contenuto di PC viene incrementato di 1 ( quindi punta alla terza istruzione);
Fase di execute della seconda istruzione
1.
Dal bus dati 30 viene trasferito in AX
2.
La ALU incrementa di 1 il contenuto del registro AX
Classificazione memorie
Le memorie sono organizzate in byte ( 8 bit ) ognuno dotato di indirizzo rappresentato da un
numero intero positivo. Per poter leggere o scrivere ( accedere ) ad un byte è necessario
specificare prima l’indirizzo del byte a cui si vuole accedere. A volte si parla di parola o word
costituite da 2 byte.
La capacità della memoria è il numero massimo di byte che essa può contenere.
Il tempo di lettura è l’intervallo di tempo che intercorre fra l’inizio dell’indirizzamento del byte
che si vuole leggere a quando il dato risulta disponibile.
Il tempo di scrittura è l’intervallo di tempo che intercorre fra l’inizio dell’indirizzamento del
byte che si vuole scrivere a quando il dato viene scritto in memoria.
Il tempo di accesso è una media fra il tempo di scrittura e quello di lettura.
Le memorie possono essere volatili e non volatili: le prime perdono i dati contenuti in mancanza
dell’alimentazione le seconde no.
Un’ altra possibile classificazione è:
ROM ( read only memory ) vengono scritte quando sono fabbricate e non possono essere più
cancellate.
PROM ( programmable ROM ) possono essere scritte dall’ utente tramite dei programmatori
appositi e da quel momemnto diventano ROM.
EPROM ( erasable PROM ) programmabili e cancellabili più volte.
EEPROM ( electrically erasable programmable ROM ) è un tipo di memoria non volatile
cancellabile elettricamente
RAM ( random access memory ) sono memorie volatili.
Gestione dei dispositivi di input / output
Un sistema a microprocessore deve eseguire programmi più o meno complessi, trasferire dati
alle periferiche esterne e ricevere da queste informazioni. Durante l'esecuzione dei programmi vi
è anche un flusso continuo di dati dalla CPU alle memorie e viceversa. Il video, la tastiera, la
stampante, il plotter, il drive ecc., sono gli esempi più noti di dispositivi periferici. In molti casi il
trasferimento dei dati dalla CPU alle periferiche e alle memorie deve rispettare procedure ben
precise e codificate. Le tecniche di scambio dei dati tra il sistema e le periferiche possono essere
classificate in: polling, interruzione e DMA
8
L’utilizzazione di una tecnica piuttosto che un'altra richiede una attenta analisi delle procedure
software e dei dispositivi hardware al fine di valutare quale sia la tecnica che aumenta l’efficienza
del sistema a microprocessore e che meglio si adatti alle specifiche di progetto.
Polling ( interrogazione ciclica )
È la tecnica più semplice per lo scambio di informazioni tra microprocessore e periferiche
perché è essenzialmente software. Prevede che la CPU sotto il controllo di un apposito
programma, controlli in ciclicamente lo stato delle periferiche ad essa collegate.
Se il flag è attivo (generalmente a livello logico basso), la CPU esegue un programma di servizio
al termine del quale verifica lo stato del flag della seconda periferica e così via. Naturalmente la
CPU passa immediatamente a controllare il flag della seconda periferica se quella interpellata non
ha bisogno dell'attenzione della CPU. Questa tecnica di gestione delle periferiche presenta alcuni
problemi:
• la CPU è impegnata continuamente a verificare i flag;
• non si ha la possibilità di definire una gerarchia se due richieste arrivano contemporaneamente;
• se una periferica appena interrogata ha bisogno di essere servita dalla CPU deve aspettare,
prima di essere nuovamente interrogata, perchè la CPU deve verificare lo stato di tutte le
rimanenti periferiche, e soddisfare le eventuali richieste di servizio da esse avanzate. Ed è anche
possibile che i dati inviati dalla periferica vengano, nel frattempo, persi.
Interruzione ( Interrupt )
Per superare le limitazioni della tecnica del polling, la gestione dello scambio di informazioni tra
CPU e periferiche è spesso affidata alla tecnica dell'interruzione, la quale prevede che sia la
periferica, a differenza di quanto avviene nel polling, ad avanzare la richiesta di servizio alla
CPU. In tal modo la periferica può interrompere la CPU in qualsiasi momento, in relazione alle
9
esigenze specifiche, senza dover attendere i tempi dell’interrogazione ciclica. Tutte le CPU sono
provviste, a seconda della complessità della loro architettura di uno o più ingressi predisposti per
ricevere il segnale di richiesta di interruzione da parte della periferica.
Sistema a
microprocessore
periferica
interruzione
Il flow chat che segue descrive le varie fasi che seguono la richiesta di interruzione.
L’ interruzione non viene servita immediatamente ma solo alla fine della fase di esecuzione
dell’istruzione in corso, prima quindi che inizi la fase di fetch dell’istruzione successiva.
1. viene interrogato l’ IF nel registro dei flag per verificare se sono abilitate le interruzioni.
2. viene salvato il contesto ( registri, PC, PSW ).
3. viene riconosciuta l’interruzione, cioè quale periferica l’ha inviata e quindi viene prelevato in
un’opportuna zona di memoria l’indirizzo della ISR ( interrupt service routine ) associata al
dispositivo.
4. viene eseguita la ISR associata al dispositivo.
5. viene quindi ripristinato il contesto, cioè vengono ricaricati i registri salvati in precedenza. Fra
i registi vi è il PC ( program couter ) che sappiamo contenere l’indirizzo dell’istruzione
10
successiva, di conseguenza viene ripresa l’esecuzione del programma originario, all’istruzione
seguente a quella durante la cui esecuzione è arrivata l’interruzione.
Il problema dell’interruzione è che oltre il tempo necessario alla gestione dell’I/O abbiamo anche
i tempi relativi al salvataggio e al ripristino del contesto. Questi tempi sono piccoli ma in
presenza di molte interruzioni con grosse quantità di dati possono “pesare” notevolmente.
DMA (Direct Memory Access – accesso diretto in memoria)
Nella tecnica DMA la periferica richiede alla CPU il controllo dei BUS per gestire in modo
autonomo, senza cioè l’intervento del microprocessore, il trasferimento dei dati con le memorie.
In pratica nella tecnica DMA un dispositivo di supporto, detto DMA controller, regola il flusso
dei dati dalla periferica alla memoria e viceversa. La tecnica DMA è utilizzata quando bisogna
gestire i trasferimenti di blocchi di dati ad alta velocità perché, a differenza di quelli gestiti da
programma, non sono necessarie operazioni di lettura e scrittura.
bus indirizzi
bus dati
CPU
R/W
memoria
DMA
controller
Interfaccia
di I/O
periferica
Microcontrollori e PLC
Con il termine microcontrollore si intende comunemente un sistema a microprocessore
integrato su un unico chip, che comprende, oltre alla CPU, una memoria di programma,
solitamente EPROM o EEPROM, una memoria RAM, generalmente di dimensioni ridotte, per i
risultati intermedi dell'elaborazione e per lo stack e periferici di I/O vari (porte seriali ,contatori,
timer, convertitori analogico digitali ecc.); per quanto appena esposto questi circuiti integrati
vengono anche detti microcomputer single chip. Con queste caratteristiche, dovrebbe essere
evidente che i microcontrollori sono stati concepiti soprattutto per applicazioni industriali di
controllo, in cui il programma di gestione, una volta messo a punto non ha più necessità di essere
modificato (o di esserlo raramente). Sono le applicazioni che gli americani chiamano
“embedded”, cioè incorporate in prodotti e apparati finiti, che possono andare dagli
elettrodomestici intelligenti, ai sistemi di comunicazione o sicurezza, alla strumentazione,
all'automazione in campo automobilistico, ecc In questo senso si vogliono qui ricordare ad alcuni
microcontrollori particolarmente diffusi quali la famiglia PIC (il 16C84 costituisce importante
elemento delle PlayStation), la famiglia ST o le famiglie prodotte da Motorola, Siemens, Atmel e
da diversi altri importanti produttori che hanno progettato e realizzati microcontrollori dedicati a
loro applicazioni.
11
I microcontroller PIC (acronimo per Programmable. Integrated Controller) si distaccano dalla
struttura di un microprocessore classico, essenzialmente perché sono delle CPU RISC ( Reduced
Instruction Set Computing, elaborazione con insieme di istruzioni ridotto) basate su una struttura
del tipo Harward (dall'Università dove è stata sviluppata), che si distingue dalla macchina di Von
Neuman classica per avere memoria programma e memoria dati (e relativi bus) separati. La
filosofia RISC consiste sostanzialmente nel prevedere poche e semplici istruzioni, tutte della
stessa lunghezza e (possibilmente) tutte richiedenti lo stesso numero di cicli macchina sta per il
fetch che per l'esecuzione. Questa caratteristica unita alla separazione fisica dei canali lungo cui
fluiscono istruzioni e dati, permette di ottenere una sovrapposizione (Pipelining) delle fasi di
Fetch di un'istruzione con quella di esecuzione della precedente “senza buchi" e quindi in modo
molto più efficiente di quello che, per esempio, si ha in termini di velocità complessiva
dell'elaborazione nei microprocessori della Famiglia 8086, dove istruzioni diverso hanno tempi
diversi sia di fetch che di esecuzione.
Da un punto di vista delle applicazioni di potenza in impianti industriali e civili hanno avuto larga
diffusione i PLC, Controllori Logici Programmabili, che al pari dei microcontrollori hanno la
caratteristica della semplice riprogrammabilità per ottenere le più svariate funzioni attraverso le
linee (in genere a 24 (v) ed in numero variabile da modello a modello) di segnali I/O.
12
SISTEMI OPERATIVI
Introduzione
Un Sistema Operativo ( SO ) è un insieme di programmi che gestiscono le risorse sia hardware
che software del sistema di elaborazione rendendone più semplice l’uso (supporto per l’utente)
ed ottimizzano l’utilizzazione delle stesse (gestore di risorse).
In particolare il SO è
• Un sistema per gestire l’interazione utente-computer;
• Un’interfaccia fra le applicazioni e le risorse del computer
• Un sistema per gestire e condividere le risorse;
• Un sistema per ottimizzare l’uso delle risorse;
• Un fornitore di servizi allo sviluppo del software e all’amministrazione del sistema.
L’utente interagisce con il SO attraverso un’interfaccia denominata shell. La shell può essere
"a linea di comando", tipo MS-DOS, o "grafica" ( GUI -Graphics User Interface ), tipo
Windows. Il SO vero e proprio è formato dal nucleo o kernel che gestisce le funzioni
principali e gli altri moduli del SO stesso.
In base alle modalità di funzionamento un SO può essere:
• SO Mono Tasking, Mono Utente ( un solo programma, un solo utente ), tipo MS-DOS
• SO Multi Tasking, Mono Utente (più programmi, un solo utente), multiprogrammazione,
tipo Windows. Evoluzione dei sistemi Multi Tasking sono i sistemi Multi Threading
(ogni processo è costituito da uno o più thread che possono essere eseguiti
contemporaneamente). Windows appartiene a questa categoria di sistemi operativi.
• SO Multi Tasking, Multi Utente ( più programmi, più utenti) , multiprogrammazione,
tipo Unix/ Linux.
• SO in Tempo Reale (Real Time), per il controllo dei processi industriali, non
interagiscono con l’utente, ma con i dispositivi che controllano il processo
• SO a Macchine Virtuali, che permettono di simulare hardware e/o sistema operativo
diverso (computer virtuale), ad esempio la finestra DOS di Windows
• SO di Rete per la gestione delle LAN (reti locali), ad esempio Windows Server 2003
Esamineranno alcune caratteristiche di un generico SO in multiprogrammazione e
multiutente. Perché sia possibile la multiprogrammazione occorre che la CPU sia svincolata
dall’esecuzione delle operazioni di I/O ( input / output ), delle quali si occuperanno appositi
processori di I/O ( canali, DMA ) che lavorano in parallelismo effettivo con la CPU. La CPU
(processore centrale) può così occuparsi esclusivamente dell’avanzamento dei processi.
Funzioni e struttura di un sistema operativo
Un SO nell’eseguire i suoi compiti si prefigge obiettivi di convenienza, efficienza e versatilità.
Il SO deve inoltre ottimizzare l’uso delle risorse e interfacciare SO con le applicazioni.
Le principali risorse del sistema operativo sono:
• Processore ( CPU )
13
• Memoria centrale
• Dispositivi di I/O
• Informazioni
Per gestire le risorse dispone di strutture dati e programmi per
• Tenere conto dello stato delle risorse, ad esempio se c’è memoria libera per collocare
un programma;
• Avere una politica per operare scelte nell’allocazione delle risorse, ad esempio quale
programma mandare in esecuzione fra quelli in memoria;
• Avere metodi per allocare e togliere le risorse.
Un SO è quindi un sistema software complesso; di seguito è mostrata una sua
rappresentazione del SO collegata al suo compito di gestore di risorse.
L’idea alla base della strutturazione di un SO a livelli o strati è la seguente
• Ogni livello assolve a compiti specifici utilizzando i servizi offerti dallo strato inferiore
e offrendo servizi al livello superiore;
• Ogni livello è visto come una macchina virtuale in grado di fornire determinati servizi;
• Ogni livello opera sulla macchina virtuale del livello inferiore e costruisce una
macchina virtuale più potente al livello superiore.
Processi e risorse
Un processo è l’insieme delle azioni compiute dal processore per eseguire un programma. Si
può semplificare dicendo che "un processo è un programma in esecuzione".
Per meglio definire un processo analizziamone la differenza con il programma.
Un programma è costituito dalle istruzioni scritte dal programmatore e tradotte in linguaggio
macchina e viene visto come un’insieme di bit presenti sul disco rigido che non cambia nel
tempo e non modifica lo stato della macchina.
Un processo invece modifica lo stato della memoria e dei registri.
In generale potremo dire che:
14
•
•
•
Un processo è un’entità dinamica il cui stato è determinato dai valori assunti dal
registro Program Counter, dai registri generali, dallo stato della memoria, dallo stack
e dall’ SR;
Un processo ha una traccia d’esecuzione, che può essere definita come la sequenza
degli stati assunti dal processore durante l’avanzamento dei processi.
Ad un processo sono associati il codice, l’area dati, i registri e le altre risorse ad
esso assegnate.
La risorsa è un oggetto hardware e software che serve per far avanzare un processo dallo
stato di new a quello di terminated.
È compito del SO, oltre all’avanzamento dei processi, la gestione delle risorse. Le risorse
possono essere
• consumabili ad esempio i messaggi scambiati tra processi;
• riusabili quelle che vengono date in uso ai processi e controllate dal gestore di risorse
del SO.
Ci sono risorse che possono essere usate da un solo processo alla volta e sono dette seriali (
CPU, stampante,...). Spesso può essere utile costringere il processo a rilasciare la risorsa (
prerilascio ).
Lo scheduling è l’algoritmo di gestione della risorsa, che consente la scelta di assegnare o
togliere una risorsa al processo che l’ha richiesta.
Stati di un processo
Un processo, dopo la sua creazione ed attivazione, può trovarsi in diversi stati di avanzamento:
Attesa di caricamento ( new ) è stata richiesta l’esecuzione del programma memorizzato su
disco, attende di essere caricato in memoria centrale; più processi in attesa di caricamento.
Pronto ( ready ) il processo è in memoria centrale ed attende che gli venga assegnata la CPU;
più processi in stato di pronto
Esecuzione ( running ) la CPU esegue il processo; un solo processo in esecuzione
Waiting ( in Attesa ) il processo ha richiesto una operazione di I/O e ne attende il
completamento; più processi in attesa; spesso le operazioni di I/O vengono eseguite dai
processori di I/O in parallelismo effettivo con la CPU
Terminazione ( terminated ) il processo ha terminato l’esecuzione può rilasciare le risorse
utilizzate.
new
terminated
ready
running
waiting
15
Se non consideriamo la freccia tratteggiata, lo schema illustra un sistema operativo in
multiprogrammazione "a semplice richiesta di I/O": un processo lascia lo stato di running per
richiesta di I/O o per terminazione. Ciò, in alcune situazioni, può causare il blocco dell’intero
sistema (se, ad esempio, si esegue un ciclo infinito per errore del programmatore all’interno del
quale non sono presenti istruzioni di richiesta di I/O). Per ovviare a questo inconveniente
occorre "costringere" il processo al prerilascio della CPU. Quando un processo passa da Pronto
ad Esecuzione ( gli viene data la CPU ) si assegna ad esso un "quanto di tempo" ( time slice ) di
qualche decina di millisecondi scaduto il quale, se ancora in esecuzione, il processo viene
costretto a rilasciare la CPU. Un sistema di questo tipo ( tutti i moderni sistemi operativi in
multiprogrammazione ) è un sistema time-sharing ( a suddivisione di tempo). Windows è un
SO time-sharing, monoutente, multithreading. Linux è un SO time-sharing, multiutente,
multitasking.
Si definisce grado di multiprogrammazione la somma del numero di processi in stato di
ready, wait e run.
Descrittore del processo
Ogni processo ha associato un proprio descrittore di processo ( o PCB. – Process Control
Block) che viene creato in fase di "creazione" del processo e mantenuto in memoria centrale, in
un’area riservata al sistema operativo, per tutta la durata del processo. Tale descrittore
contiene:
• PID ( Process Identification) , un numero progressivo che identifica il processo,
assegnato dal SO al momento della creazione del processo;
• PPID ( Parent Process Identification ) cioè il PID del processo padre cioè il processo
che l’ha generato;
• puntatore alla lista dei processi figli ;
• stato del processo ;
• priorità;
• limiti della memoria centrale utilizzata;
• puntatore alla lista delle risorse in uso;
• copia del contesto del processo ( PC, IR, MAR, MDR, registri generali, …).
Quando un processo rilascia la CPU nel suo PCB viene salvato il contenuto dei registri della
CPU. Quando a un processo viene data la CPU la copia dei registri contenuta nel suo PCB
viene copiata nei registri fisici della CPU. Ciò consente al processo di riprendere
l’esecuzione dal punto in cui era stata sospesa, dall’istruzione puntata dal PC al momento della
sospensione. Per Creare un processo occorre assegnare un’area di memoria centrale, copiare il
programma in tale area, creare il suo PCB, porre il processo in stato di "pronto". Per Terminare
un processo occorre liberare la memoria centrale allocata, rilasciare tutte le risorse da esso
utilizzate, aggiornare il PCB del processo che l’ha creato.
Scheduling della CPU
Esistono diverse tecniche di scheduling della CPU ( scheduling a basso livello ), cioè politiche
di assegnamento della CPU a uno dei processi in stato di Ready. Lo scheduling della CPU
viene effettuata dal modulo del sistema operativo denominato Dispatcher.
L’obiettivo dello scheduling della CPU è la massimizzazione dell’utilizzo della CPU, questo si
ottiene assegnando al processore processi che sono pronti per eseguire istruzioni. Lo scheduler
a breve termine seleziona uno tra i processi in memoria pronti per essere eseguiti e lo assegna
alla CPU.
Lo scheduler interviene quando un processo:
1. passa dallo stato di running a quello di waiting
16
2. passa dallo stato di running a quello di ready
3. passa dallo stato di waiting a quello di ready
4. Termina
Qualunque sia la strategia di scheduling essa si deve proporre di ottimizzare differenti
parametri:
1. massimizzare la percentuale di utilizzo della CPU;
2. massimizzare il throughput, cioe il numero di processi terminati nell’unità di tempo;
3. minimizzare il sovraccarico del processore nell’eseguire processi di sistema e non
utente;
4. minimizzare il tempo che passa dall’ inizio di un processo alla sua fine (tempo di
turnround) ;
5. prevenire starvation situazione in cui un processo non viene mai eseguito;
6. minimizzare il tempo di risposta dei processi interattivi, tempo impiegato a fornire il
primo output.
Gli algoritmi di schedulazione possono essere classificati secondo due categorie:
Schedulazione senza prerilascio cioè un processo in stato di running deve necessariamente
proseguire fino allo stato di terminato.
Schedulazione con prerilascio cioè un processo in stato di running, dopo un certo tempo
assegnatogli ( time slice o quanto di tempo ) passa in stato di ready, ovviamente se non
intervine prima una richiesta dio I/O che lo porterebbe in stato di waiting.
L’esigenza che accomuna tutti gli algoritmi di scheduling è che siano veloci per minimizzare il
sovraccarico della CPU.
Schedulazione First-Come First-Served ( FCFS )
Algoritmo senza prerilascio che prevede che il processo arrivato per primo debba essere
eseguito per primo.
CPU
ready
evento
wait
attesa evento
Le conseguenze di un algoritmo come questo che tiene in conto solo l’ordine di arrivo sono:
• Viene penalizzato un processo breve che arriva dopo una serie di processi lunghi;
• Vengono favoriti i processi che fanno largo uso della CPU ( CPU bound ) rispetto a
quelli in cui prevale l’I/O ( I/O bound ).
• veloce
Schedulazione Shortest Job First ( SJF )
Algoritmo senza prerilascio che prevede che la coda dei processi ready sia ordinata per valori
crescenti di durata. In questo modo il primo ad essere eseguito è il processo di durata minore
fra quelli in attesa.
• I processi brevi sopravanzano quelli lunghi con il rischio per quest’ultimi di starvation;
• Il principio di favorire i processi brevi è anche alla base dell’idea di privilegiare i
processi I/O bound rispetto a quelli CPU bound, permettendo anche agli altri di essere
serviti. Infatti un processo I/O bound può esser visto come una serie di brevi processi
che rilasciano la CPU per fare I/O;
• SJF è il migliore per ridurre i tempi di attesa ma è di difficile utilizzo data la difficoltà
di misurarne la durata, infatti viene usato nella schedulazione dei processi batch.
17
Schedulazione a priorità
La priorità è un parametro che indica l’importanza o l’urgenza che viene assegnata ad un
determinato processo. Questo algoritmo con prerilascio prevede che il primo processo ad essere
eseguito è quello a priorità più alta. In sostanza differisce dai precedenti oltre che per il
prerilascio, per il fatto che la coda dei processi ready è ordinata in base alla priorità.
CPU
ordinata per priorita
wait
evento
attesa evento
fine quanto di tempo
Si ha anche un prerilascio quando arriva nella coda di ready un processo a priorità più alta.
Schedulazione Round Robin
E’ un algoritmo di schedulazione FCFS con prerilascio.
CPU
FIFO
evento
wait
attesa evento
fine quanto di tempo
Schedulazione Multilevel Feedback
I processi "pronti" vengono inseriti in N code. La prima ha priorità massima e time slice
minimo, l’ultima ha priorità minima e time slice massimo (da coda 1 a coda N la priorità
diminuisce e lo slice aumenta). I processi della coda i-esima vengono eseguiti quando le code
precedenti sono vuote. Un processo che lascia la CPU per fine time slice viene inserito nella
coda seguente a quella dalla quale era stato prelevato ( diminuisce la priorità ed aumenta il time
slice ). Un processo che entra in "pronti" per fine I/O o per caricamento viene inserito nella
prima coda, quella a priorità maggiore. Questo meccanismo favorisce i processi con alto tasso
di I/O, cioè i processi interattivi.
18
PR1
CPU
PR2
PR3
PR4
PR5
priorità
quanto di tempo
Overhead di sistema
L’assegnazione della CPU a un nuovo processo comporta il salvataggio contesto del vecchio
processo, la scelta del processo da avanzare e il caricamento del contesto di questo nuovo
processo. Il tempo di cambio di contesto ( context-switch time ) è l’overhead di sistema. Gli
algoritmi di scheduling debbono essere efficienti, ma non eccessivamente complessi, così da
minimizzare il consumo di CPU da parte dell’algoritmo di scheduling stesso (e minimizzare
l’overhead di sistema). Questo vale per tutti i processi del sistema operativo.
Interruzioni
Il passaggio dei processi da uno stato all’altro, durante il loro avanzamento, è causato dal verificarsi
di eventi. A parte la richiesta di I/O, che viene fatta dal processo in esecuzione, gli altri eventi sono
esterni alla CPU. Per segnalare tali eventi è previsto il meccanismo delle interruzioni (o interrupt).
Una interruzione può essere
• un segnale hardware che viene inviato dai vari dispositivi e periferiche alla CPU;
• software con la quale il processo richiede al sistema operativo una operazione di I/O.
Sistema a
microprocessore
periferica
interruzione
Quando arriva una interruzione alla CPU si ha l’automatica sospensione del processo in esecuzione
sulla CPU (con il salvataggio del suo PCB) e l’attivazione di una routine di gestione interruzioni
(caricata in memoria centrale, nell’area riservata al sistema operativo, all’accensione del computer).
Tale routine, in base al tipo di interruzione arrivata, prende i provvedimenti del caso. Ad esempio,
se l’interruzione segnala la fine di un I/O individuerà il processore di I/O che l’ha inviata ed il
processo per il quale è stata inviata cioè il processo che ha terminato l’I/O -, lo metterà nello stato
di pronto e riprenderà l’esecuzione del processo che aveva temporaneamente sospeso; se
l’interruzione segnala la fine del time-slice metterà il processo sospeso in pronti ed attiverà lo
scheduling per scegliere il prossimo processo da avanzare; etc.
Il flow chat che segue descrive le varie fasi che seguono la richiesta di interruzione.
19
L’ interruzione non viene servita immediatamente ma solo alla fine della fase di esecuzione
dell’istruzione in corso, prima quindi che inizi la fase di fetch dell’istruzione successiva.
1. viene interrogato l’ IF nel registro dei flag per verificare se sono abilitate le interruzioni.
2. viene salvato il contesto ( registri, PC, PSW ).
3. viene riconosciuta l’interruzione, cioè quale periferica l’ha inviata e quindi viene prelevato
in un’opportuna zona di memoria l’indirizzo della ISR ( interrupt service routine )
associata al dispositivo.
4. viene eseguita la ISR associata al dispositivo.
5. viene ripristinato il contesto, cioè vengono ricaricati i registri salvati in precedenza. Fra
registi vi è il PC (program couter) che sappiamo contenere l’indirizzo dell’istruzione
successiva, di conseguenza viene ripresa l’esecuzione del programma originario,
all’istruzione seguente a quella durante la cui esecuzione è arrivata l’interruzione.
Il problema dell’interruzione è che oltre il tempo necessario alla gestione dell’I/O abbiamo anche
i tempi relativi al salvataggio e al ripristino del contesto. Questi tempi sono piccoli ma in
presenza di molte interruzioni con grosse quantità di dati possono “pesare” notevolmente.
Gestione delle Risorse
Una risorsa, sia essa hardware ( memoria, disco, cpu,…) o software ( file, PCB,…), è una
qualche cosa necessaria al processo per avanzare verso lo stato di terminato. Una risorsa può
essere assegnata:
• in modo statico per tutta la durata del processo;
• in modo dinamico viene assegnata, usata e rilasciata dopo l’utilizzo da parte del
processo; se il processo può essere costretto anche a rilasciare la risorsa, allora la risorsa
è detta risorsa prerilasciabile come la CPU.
Il nucleo (o Kernel) del sistema operativo si occupa, della gestione della CPU, scheduling
della CPU e dell’avanzamento dei processi. La gestione delle altre risorse è effettuata da
appositi gestori delle risorse quali il gestore della memoria centrale, il gestore delle periferiche,
e il gestore delle informazioni (o File System).
20
Due processi si dicono disgiunti se sono completamente indipendenti l’uno dall’altro, interagenti
se accedono a risorse comuni. L’interazione fra processi può essere :
• diretta ( cooperazione ) due processi per avanzare si scambiano messaggi;
• indiretta ( competizione ) due processi “competono” per l’accesso ad una stessa risorsa.
Mutua esclusione
In genere una risorsa riusabile e non prerilasciabile può essere assegnata ad un solo processo
per volta, in mutua esclusione cioè se un processo P ha la risorsa R, nessun altro può utilizzarla
finché P non la rilascia, garantendo un tempo di attesa finito ed evitando situazioni di stallo ( o
deadlock ). La mutua esclusione va garantita, pena il verificarsi di errori non controllabili.
Competizione tra processi ( interazione indiretta )
Primitive Lock e UnLock (attesa attiva)
Per garantire la mutua esclusione ad una risorsa si associa a questa un semaforo binario che può
valere
• 0 "semaforo verde", cioè risorsa libera
• 1 "semaforo rosso", risorsa occupata.
La richiesta della risorsa viene fatta con la primitiva Lock che verifica il semaforo: se il semaforo è
a 0 lo pone a 1 ed assegna la risorsa al processo, altrimenti resta in attesa che il semaforo diventi 0 (
verificandolo ad intervalli regolari ).
Questo meccanismo, che prevede il test ripetuto del semaforo ( attesa attiva ) garantisce la mutua
esclusione.
int Lock ( s )
{
disable();
while ( s == 1 ); // cicla se il semaforo è occupato
s=1;
// occupa la risorsa
enable();
return 0;
}
int UnLock ( s )
{
disable();
s = 0;
// pone il semaforo s a “ libero”
enable();
return 0;
}
Il processo P che utilizza la risorsa sarà :
…………
Lock ( s );
………
// questa è la porzione di codice in cui viene usata la risorsa R
………
// a cui è associato il semaforo s ( detta sezione critica )
UnLock ( s );
Questo sistema ha due gravi inconvenienti:
1. attesa attiva : la Lock() impegna inutilmente il processore a scapito di altri processi;
2. rinvio indefinito: un processo può attendere indefinitamente il semaforo verde anche se
questo evento si è verificato infinite volte. Infatti se il semaforo diventa verde prima che
venga riesaminato ed un altro processo chiede la risorsa, la ottiene, rimettendo il semaforo a
rosso; quando si va a testare il semaforo lo si trova ancora rosso, ma nel frattempo la risorsa
è stata data ad un alto processo; in teoria questo potrebbe verificarsi per un tempo indefinito.
Primitive wait e signal ( attesa passiva )
Con questo meccanismo a ciasuna risorsa viene associata
• semaforo s
• Q(s) una coda destinata a contenere i PID dei processi che l’hanno richiesta.
21
La richiesta della risorsa viene fatta con la primitiva wait che testa il semaforo: se il semaforo è a 0
lo pone a 1 ed assegna la risorsa al processo, altrimenti pone il PID del processo nella coda di
attesa e pone in wait il processo.
Il rilascio della risorsa viene fatto con la primitiva signal che controlla se la coda di attesa è vuota:
se si pone il semaforo a verde, altrimenti lo lascia a rosso ed assegna la risorsa al primo processo
della coda. Questo meccanismo di attesa passiva garantisce sia la mutua esclusione che l’attesa
finita.
int Wait ( s )
{
if ( s == 1 )
// poni in wait il processo e mettilo nella
// coda Q(s) associata al semaforo s
else s = 1; // assegna risorsa
}
int Signal ( s )
{
if ( Q(s) è vuota? )
s = 0;
// pone il semaforo s a “ libero”
else
// s resta occupato e la risorsa viene assegnata
// al primo processo di Q(s)
}
Cooperazione tra processi ( interazione diretta )
Soluzione tramite la tecnica del produttore-consumatore
Un processo produttore genera dati e li mette in un buffer , da dove li preleva il processo
consumatore. Osserviamo che :
• il produttore deve accertarsi che il dato nel buffer sia stato prelevato, prima di inserirne un altro;
• il consumatore deve accertarsi che il produttore abbia inserito un nuovo dato prima di prelevarlo
preleva informazione
dal buffer
prepara informazione
inserisci informazione
nel buffer
utilizza informazione
In sintesi i processi devono essere "sincronizzati", cioè la "produzione" di un messaggio da parte
del processo trasmittente deve avere sempre lo stesso tempo del "consumo" da parte del processo
ricevente. La sincronizzazione viene fatta tramite le primitive P e V e altri due semafori
• "prelevato" inizializzato a verde, utilizzato dal consumatore per segnalare lo svuotamento del
buffer;
• "depositato" inizializzato a rosso, utilizzato dal produttore per segnalare la presenza nel buffer di
nuovi dati.
22
Il produttore, dopo aver depositato un dato nel buffer, non può scrivere nel buffer sino a
quando il consumatore non ha prelevato il dato e, analogamente, il consumatore non può usare
più volte il dato inserito nel buffer. Per queste ragioni l’accesso al buffer avviene in mutua
esclusione.
Stallo (deadlock)
Lo stallo si ha quando due o più processi che posseggono risorse e, per avanzare necessitano di
ulteriori risorse possedute da altri processi che non le possono rilasciare.
L’avere più programmi che competono per l’uso delle risorse può causare situazioni di stallo
(deadlock), situazioni nelle quali i processi non possono più avanzare. Le condizioni per le quali si
può verificare lo stallo sono:
• necessità di garantire la mutua esclusione;
• attesa di risorse aggiuntive (i processi richiedono le risorse una alla volta e non tutte
insieme);
• non prerilascio delle risorse ottenute;
• verificarsi di una situazione di "attesa circolare".
Ad esempio il processoP1 ha in uso la risorsa R1che rilascerà solo dopoaver ottenuto la risorsaR2;
P2 ha in uso R2 cherilascerà solo dopo averottenuto R1: STALLO!Per ovviare allo stallo ci sono
alcune diverse tecniche:
la Prevenzione dello stallo ( deadlock prevention ) consiste nell’invalidare una o più condizioni di
stallo (ad esempio si obbligano i processi a chiedere le risorse tutte insieme); in genere si previene
l’attesa circolare con l’allocazione gerarchica delle risorse
la Fuga dallo stallo ( deadlock avoidance ) permette di rifiutare la richiesta di risorsa che causa (o
potrebbe causare) stallo; l’algoritmo più noto di questa strategia è l’algoritmo del banchiere
il Riconoscimento e recupero dello stallo ( deadlock detection and recovery ) non previene lo
stallo, ma riconosce quando esso avviene (ad esempio esaminando periodicamente il grafo di
allocazione delle risorse) e lo elimina (ad esempio forzando uno dei processi a terminare).
23
Gestione della memoria
Ogni processo, per avanzare, necessita che il relativo programma risieda in memoria centrale (
immagine del processo ). Tale programma, in linguaggio macchina, è costituito da istruzioni e dati.
Lo spazio fisico è l’insieme degli indirizzi accessibili fisicamente e costituisce la memoria fisica;lo
spazio logico è uno spazio astratto di indirizzi che vengono assegnati al momento della traduzione
del programma e costituisce la memoria logica. Il compilatore al momento della traduzione del
programma assegna agli identificatori e alle istruzioni degli indirizzi logici come se il programma
debba esser caricato a partire dalla locazione 0, ma durante l’esecuzione tali indirizzi debbono
essere fisici ( l’indirizzo fisico della prima istruzione sarà l’indirizzo della locazione di memoria a
partire dalla quale è stato caricato il programma ). Occorre quindi trasformare gli indirizzi logici in
indirizzi fisici ( o assoluti ). Questa "traduzione" è detta Mapping o Rilocazione degli indirizzi. Ci
sono diverse tecniche di gestione della memoria.
Contigua singola
La memoria è divisa in due parti, una riservata al sistema operativo ed una al programma utente. Il
programma utente sarà sempre caricato a partire dalla stessa locazione fisica. In questo caso non c’è
possibilità di multiprogrammazione, ma solo di sistemi operativi uniprogrammati. La rilocazione
è assoluta, cioè gli indirizzi vengono trasformati da logici a fisici al momento della compilazione
del programma.
Partizioni Fisse
La memoria è divisa, al momento dell’installazione del SO, in un certo numero di partizioni fisse (
non necessariamente uguali ) ciascuna delle quali potrà contenere un processo. Il SO, per la
gestione, si avvarrà di una tabella delle partizioni di tante righe quante sono le partizioni e per
ogni partizione l’ indirizzo iniziale, indirizzo finale (o lunghezza), PID del programma che la
occupa ( 0 se libera ). La rilocazione è statica, gli indirizzi vengono trasformati da logici a fisici al
momento del caricamento del programma in memoria, dal caricatore ( loader ), che trasformerà gli
indirizzi sommando all’indirizzo logico l’indirizzo iniziale della partizione dove viene caricato il
processo.
Il grado di multiprogrammazione è minore o uguale al numero di partizioni. Due sono i principali
problemi di questa organizzazione della memoria:
• uso inefficiente della memoria a causa della frammentazione interna alle partizioni. La
dimensione dei processi deve essere minore delle partizioni e lo spazio inutilizzato in ogni
partizione forma un insieme di frammenti di memoria inutilizzabili;
• impossibilità di eseguire programmi più grandi della partizione di dimensioni maggiore.
24
Sistema Operativo
Partizione 1
da
10 MB
Partizione 2
da
30 MB
Partizione 3
da
60 MB
Partizione 4
da
10 MB
Partizione 5
da
40 MB
processo 4 da
10 MB
processo 5 da
30 MB
Le partizioni 4 e 5 sono occupate
tabella delle partizioni
Indirizzo iniziale
della partizione
dimensione
PID
processo
25
35
65
125
135
10
30
60
10
40
0
4
5
0
0
Partizioni Variabili
Le partizioni vengono create dinamicamente, in base alle richieste. Inizialmente si hanno due
partizioni, una del SO ed una costituita dal resto della memoria libera. La creazione di una nuova
partizione divide la partizione libera in due. E così via. Quando un programma termina si libera la
sua partizione (e se quella precedente e/o seguente sono libere si accorpano in un’unica partizione
libera). Dopo un certo tempo si può avere il problema della frammentazione esterna ( tanti
frammenti di partizioni libere, ciascuno dei quali troppo piccolo per contenere un altro processo ). Il
SO, per la gestione, si avvarrà di una tabella simile a quella delle partizioni fisse, ma con un
numero variabile di righe.
tabella delle partizioni
partizione
locazione iniziale
dimensione
PID
processo
1
2
3
----n
25
35
65
-----
10
30
60
-----
0
4
5
-----
La scelta della partizione libera da utilizzare può essere fatta con diverse strategie:
• first fit viene scelta la prima partizione sufficiente a contenere il programma; favorisce il
formarsi di una grande partizione libera verso il fondo della memoria;
• best fit viene scelta la più piccola partizione sufficiente a contenere il programma, così da
non frammentare inutilmente le grosse partizioni;
• worst fit viene scelta la più grande partizione che può contenere il programma.
La rilocazione è generalmente dinamica ma può anche essere statica. La rilocazione dinamica
consiste nel caricare il programma in memoria senza alcuna trasformazione degli indirizzi, che
vengono trasformati da logici a fisici al momento dell’esecuzione da una apposita unità hardware
25
chiamata MMU ( Memory Management Unit ). L’indirizzo iniziale della partizione viene posto
in uno speciale registro base e durante l’esecuzione l’indirizzo fisico viene calcolato come
indirizzo fisico = contenuto del registro base + indirizzo logico
Col partizionamento variabile si crea ancora frammentazione, ma in questo caso possiamo superarla
lanciando un processo di compattazione della memoria che sposta i programmi in modo da creare
un’ unico spazio libero in coda a tutte le partizioni. Questa operazione però è abbastanza "costosa"
in termini di overhead di sistema e non può essere utilizzata in sistemi interattivi.
Segmentazione
Le tecniche di gestione memoria fin qui illustrate prevedono che ogni programma sia logicamente
lineare e venga caricato in un’area contigua di memoria. Con la segmentazione questo vincolo
cade: un programma viene suddiviso in più segmenti ( ad esempio dal compilatore o su indicazione
del programmatore ) ciascuno dei quali potrà essere successivamente caricato in una diversa
partizione. In ogni segmento gli indirizzi logici partono da 0; il campo indirizzo delle istruzioni è
formato numero del segmento [ 1 byte ] e displacment ( indirizzo all’interno del
segmento ) [2 byte]. La memoria centrale viene gestita a partizioni variabili, con l’ausilio della
tabella delle partizioni, contenente, per ogni partizione, indirizzo iniziale, indirizzo finale (o
dimensione ), PID e numero segmento del segmento del programma che la occupa ( 0 se libera ).
tabella delle partizioni
partizione
1
2
3
----n
indirizzo
iniziale
10
30
60
-----
Indirizzo
finale
30
60
80
-----
PID
processo
numero
segmento
5
0
5
-----
1
2
La rilocazione è dinamica e per risolvere gli indirizzi, per ogni programma si usa una tabella dei
segmenti (di tante righe quanti sono i segmenti del programma e avente, per ogni segmento,
l’indirizzo della partizione nella quale è stato caricato il relativo segmento del programma) il cui
indirizzo è contenuto nel registro base.
26
Paginazione
La segmentazione ridimensiona il problema della frammentazione, ma non lo elimina. La tecnica
della paginazione è simile a quella della segmentazione, con la differenza che le pagine sono a
lunghezza fissa. Il programma viene, dal compilatore, suddiviso in pagine a lunghezza fissa. La
memoria centrale è suddivisa in frame o cornici di pagine della stessa dimensione delle pagine.
Le pagine di un programma possono essere caricate ovunque in memoria. Con la paginazione non
esiste più il problema della frammentazione e la memoria viene sfruttata al 100% (un po’ di
"spreco" ci può essere sull’ultima pagina di ciascun programma). Il campo indirizzi delle istruzioni,
come per la segmentazione, è formato è formato da numero pagina e displacment (indirizzo
all’interno della pagina). La memoria centrale viene gestita con l’ausilio di una tabella di
occupazione pagine, (di tante righe quante sono i frame ) contenente, per ogni riga, l’indicazione
di chi occupa il frame corrispondente [PID e numero pagina del processo che la occupa] o 0 se
libera. La rilocazione è dinamica e per risolvere gli indirizzi, per ogni programma si usa una
tabella delle pagine, ( di tante righe quante sono le pagine del programma e avente, per ogni riga, il
numero di frame nel quale è stata caricata la relativa pagina del programma) il cui indirizzo è
contenuto nel registro base.
Memoria Virtuale paginata o Paginazione Dinamica
Con le precedenti tecniche di gestione memoria i processi debbono essere caricati completamente
in memoria. La memoria virtuale, invece, consente che un processo sia parzialmente presente in
memoria centrale e che le sue parti siano caricate e scaricate da disco quando serve. Ad ogni
processo è assegnata un’area di memoria centrale (che lo contiene parzialmente) ed un’area su
disco area di swap che contiene la sua immagine, istruzioni e dati, e che viene aggiornata durante
l’esecuzione ( il contenuto dell’area di swap del processo è diverso da quello del file contenete le
istruzioni del programma, che non viene modificato ). I processi, oltre alla memoria centrale, usano
anche l’area di swap su disco, usano cioè una Memoria Virtuale più grande della memoria fisica. Il
loro spazio indirizzabile ( indirizzi logici ) può essere maggiore dello spazio fisico della memoria
centrale e possono essere eseguiti contemporaneamente tanti processi dei quali è presente in
memoria solo una piccola parte. A fronte di questo indubbio vantaggio, occorre pagare un prezzo:
lo swapping ( carico e scarico ) di parti di processo da/su area di swap rallenta l’esecuzione del
processo ( il tempo di accesso alla RAM è dell’ordine dei nanosecondi, mentre quello al disco è
dell’ordine dei millisecondi ). I processi permanenti del SO non vengono mai gestiti
dinamicamente.
L’inutilità di portare in memoria l’intero processo è dovuta al fatto che:
• è probabile che non tutto il codice verrà effettivamente eseguito ( si pensi ad esempio alla
parte then o else di un if, al while subito falso ecc..ecc.. ).
• il principio di località temporale ci dice che è molto probabile che , in un dato intervallo di
tempo, un processo tende ad accedere agli stessi indirizzi di memoria e quindi solo la pagine
che li contiene può risiedere in memoria centrale.
• il principio di località spaziale che ci dice che se un programma accede ad una cella
dimemoria probabilmente entro breve tempo accederà a celle vicine.
Avere in memoria solo una parte del processo è importante per due ragioni:
• aumenta il grado di multiprogrammazione del sistema cioè il numero di processi in stato di
running + ready + wait;
• è possibile eseguire processi di dimensioni maggiori dell’intera memoria fisica.
27
Questa tecnica permette di eseguire un programma anche se non tutte le sue pagine sono presenti in
memoria; le altre pagine restano nell’area di swap e vengono caricate all’occorrenza. Quando il
programma fa riferimento ad una pagina non presente in memoria ( page fault ) il sistema provvede
a caricarla ( page swapping ) e il processo viene bloccato finché la pagina non è stata caricata. La
nuova pagina sarà caricata su un frame libero se presente o su una occupata; in quest’ultimo caso
potrebbe prima essere necessario salvare la pagina da sovrascrivere su disco (se, ad esempio, questa
ha subito modifiche dopo l’ultimo caricamento). La rilocazione è dinamica e la memoria centrale
viene gestita con l’ausilio di due tabelle:
• tabella di occupazione dei frame, di tante righe quante sono i frame contenente, per ogni
riga, l’indicazione di chi occupa il frame corrispondente [ PID e numero pagina del processo
che la occupa ] o 0 se libera, un bit di variazione che dice se la pagina è stata modificata e
un bit di riferimento che dice se la pagina è stata usata riferita.
PID
2130
0
7321
•
Numero
pagina
bit variazione
3
0
1
1
bit
riferimento
1
0
tabella delle pagine di un processo, ( di tante righe quante sono le pagine del processo e
avente, per ogni riga, il numero del frame nel quale è stata caricata la relativa pagina del
programma oppure 0 se non presente in memoria e l’indirizzo del blocco sull’area di swap
del disco che contiene l’immagine della pagina ) il cui indirizzo è contenuto nel registro
base.
frame
Indirizzo blocco
area swap
123
0
21
4
7
8
Nell’esempio abbiamo la tabella del processo P1 costituito da 3 pagine la prima e la terza sono
in memoria rispettivamente nei frame 123 e 21, la seconda non è in memoria.
Il flow chart che segue sintetizza l’evento page fault e le azioni conseguenti.
28
Abbiamo visto che quando il programma fa riferimento ad una pagina non presente in memoria
avvene il cosiddetto page fault, cioè un’interruzione per mancanza di pagina. A questo punto
possiamo attuare due diversi algoritmi per caricare la pagina mancante, che prendono il nome di
strategia di prelevamento.
Paginazione a richiesta che porta in memoria la sola pagina richiesta, cioè quella che contiene
l’indirizzo richiesto.
Prepaginazione che cerca di prevedere le esigenze del processo, portando in memoria un certo
numero di pagine per volta, per esempio quelle vicine alla pagina che ha causato il page fault. Alla
base di questa strategia vi è un principio detto principio di località spaziale che afferma che un
programma che accede ad una parola di memoria accederà probabilmente , entro breve termina a
celle di memoria vicine.
29
Strategie di sostituzione
Riguarda la selezione della pagina da rimuovere della memoria centrale per far posto ad una
buona pagina da caricare. Le strategie di sostituzione possono seguire:
• Strategie di sostituzioni locali viene liberato un frame fra quelli occupati dal processo che
ha generato il page fault;
• Strategie di sostituzioni globali viene liberato un frame fra quelli occupati, senza tener
conto del processo.
Vediamo quali sono i principali algoritmi di sostituzione.
Algoritmo FIFO
La pagina da eliminare è quella che è stata più a lungo in memoria
Algoritmo LRU
La pagina da eliminare è quella che è non è stata usata da maggior tempo
Algoritmo della seconda opportunità
E’ una variante del FIFO che evita di eliminate una pagina dalla memoria solo perché è stata
caricata da molto tempo. L’algoritmo fa uso del bit di riferimento che dice se la pagina è stata
referenziata ( letta e/o scritta ). L’algoritmo esamina la coda FIFO , ma prima di rimuovere la
pagina che si trova in cima alla coda controlla il valore del bit di riferimento :
se = 0 ( pagina non modificata ) la pagina viene rimossa
se = 1 ( pagina modificata ) la pagina viene messa in fondo alla coda con il valore riportato a 0
30
Gestione delle periferiche
Occorre assegnare i dispositivi ai processi secondo un’opportuna politica di scheduling,
controllare l’esecuzione delle operazioni sui dispositivi e fornire un’interfaccia uniforme agli stessi.
Le operazioni di I/O vengono svolte da processori specializzati processori di I/O che interagiscono
con il sistema attraverso buffer di comunicazione e segnali di interruzione. Le periferiche sono viste
in modo astratto indipendentemente dalla loro struttura hardware. Tale astrazione viene realizzata
dai driver, dei programmi che 'pilotano' la periferica e che fanno da interfaccia tra la periferica e i
programmi che vi possono accedere richiamando il driver relativo.
Il disco rigido
Il disco fisso è un componente fondamentale del computer poiché stabilisce i tempi di accesso alle
applicazioni e quindi la capacità di risposta del sistema operativo. Il principio di funzionamento
degli hard-disk si è andato via via perfezionando, ma il principio base è rimasto sempre lo stesso.
Una testina volante fissata ad un dispositivo molto aerodinamico simile ad un’ala effettua le
operazioni di scrittura e lettura; l’hard-disk gira velocemente creando un microvortice in grado di
far allontanare con lo spostamento d’aria la testina dal disco di qualche micron (1 micron= 1/1000
di millimetro ). In esso vengono fisicamente archiviati i dati preposti al funzionamento del sistema
operativo ed i dati utente; Le informazioni sono memorizzate su di esso sotto forma di files. Un file
è semplicemente una sequenza di bytes. I bytes possono rappresentare un codice ASCII per un
carattere di un file testo oppure possono essere delle istruzioni per un particolare software oppure
ancora un colore di un pixel in un immagine. Non importa quello che contiene, un file è
semplicemente una sequenza di bytes. Quando un programma lo richiede, l'hard disk lo trova e byte
per byte lo invia alla CPU per la successiva elaborazione.
Se apriamo un hard-disk ci accorgiamo che è costituito da una pila di uno o più piatti ( i veri e
propri dischi ) ricoperti da uno strato di supporto magnetico simile ad una microscopica asfaltatura,
su cui vengono registrati i dati. I piatti ruotano assieme su un perno, chiamato asse.
31
I piatti degli hard disk vengono mantenuti in costante rotazione, ciò fa si che i dati in esso
registrati siano immediatamente disponibili, letti e scritti alla maggior velocità possibile.
Normalmente i piatti di un disco fisso sono in alluminio ( da cui il nome hard ) su cui è applicato un
materiale magnetico, ed è lavorato con macchine di precisione per ottenere tolleranze molto basse,
misurata in frazioni di micron.
Il disco fisso tradizionale è un meccanismo combinato in parte elettronico e in parte meccanico.
Da un punto di vista elettrico esso svolge la funzione di trasformare gli impulsi di dati digitali
elettronici in campi magnetici permanenti; analogamente ad altri dispositivi di registrazione
magnetici, floppy disk, registratori audio e video, anche il disco fisso raggiunge il suo fine usando
un elettromagnete, la testina di lettura/scrittura per allineare le polarità delle particelle magnetiche
sugli hard disk stessi. Il resto dell’elettronica presente ha il compito di controllare e ottimizzare la
parte meccanica del drive aiutandola a organizzare adeguatamente la memorizzazione magnetica e
a localizzare le informazioni immagazzinate sui piatti del disco.
I piatti sono suddivisi in zone chiamate tracce, queste a loro volta sono suddivise in settori (
record fisici o blocchi ). L'insieme di tracce che si trova alla stessa distanza dal centro su tutte le
facce di tutti i piatti viene chiamato cilindro. Si è soliti individuare ciascuna traccia con un numero
a partire dalla traccia zero sita sul bordo esterno.
Per individuare un informazione sul disco è necessario conoscere il cilindro in cui l’ informazione
stata memorizzata, nell’ambito del cilindro su quale faccia ed infine individuata la faccia in quale
settore. In sintesi per individuare l’informazione abbiamo bisogno della terna < cilindro, faccia,
settore > o CFS
La suddivisione CHS è logica e non fisica e viene impostata una volta per tutte in fabbrica, quando
si esegue la formattazione a basso livello, differente dalla formattazione ad alto livello, fatta
generalmente dall’utente, che prepara il disco ad essere utilizzato secondo le richieste del file
system adottato dal sistema operativo.
Le tracce sono degli anelli (ideali) presenti su ogni piatto e sono identificati da un numero intero
che parte da zero. Indipendentemente dal tipo di supporto magnetico, le testine di lettura e scrittura
devono arrestare il proprio moto laterale sul disco ogni volta che leggono o scrivono dati. Mentre le
testine sono ferme, i piatti del disco girano sotto di esse e, ogni volta che un piatto completa un
giro, le testine descrivono una circonferenza completa sulla sua superficie. Questa circonferenza
viene chiamata traccia.
I settori sono “fette” di tracce, rappresentano la minima quantità di memorizzazione di dati che
supporta il disco.
32
La dimensione del settore viene calcolata in base al numero di piatti presenti nel disco fisso e alla
capacità di esso, solitamente avremo un settore di 512B o 1024B o 2048B.
Le testine vengono pilotate da un motorino che fa ruotare anche i dischi ad una velocità di
rotazione sempre costante. Queste testine, sulla base delle indicazioni fornite dall'elettronica di
controllo, si muovono avanti e indietro fino a raggiungere la zona esatta nella quale devono essere
lette o scritte le informazioni. Queste in realtà non toccano il disco ma galleggiano su un cuscino
d'aria sfiorando la superficie. Infatti, la testina tocca il disco in una zona chiamata “landing zone”
(che non contiene dati) solo quando il computer è spento. Le testine sono un componente molto
delicato del disco fisso: se queste sbattono violentemente contro i piatti possono rimanere
permanentemente danneggiate (ecco perché è molto rischioso urtare violentemente un computer in
funzione). La polvere o le particelle di sporcizia presenti nell’aria possono danneggiare la testina;
se questa tocca e danneggia il supporto magnetico si può arrivare ad una situazione (crash) che non
solo distrugge la capacità di memorizzazione del supporto nell’area colpita, ma può liberare
particelle di materiale magnetico che a loro volta danneggiano altre zone del disco in un perverso
meccanismo a catena.
Il numero di giri ( rpm ) che il disco effettua in un minuto è indicato con queste sigle: 5400 rpm,
7200 rpm, 10000 rpm, 15000 rpm ecc.., cioè 5400 rotazioni per ninuto , 7200 giri al minuto e così
via. Più alto è questo numero, maggiore sarà l'accesso ai dati di lettura e scrittura. In ogni caso, un
disco a 10.000 rpm ha bisogno di essere ben ventilato in quanto viene prodotto molto calore. Il
tempo di accesso ai dati indica il tempo necessario affinché il disco possa accedere alle
informazioni..
Le caratteristiche principali di un disco rigido sono:
• la capacità
• la velocità di trasferimento
• il tempo di accesso
La capacità è la massima quantità di byte che possono essere memorizzati sul disco rigido. In
genere la capacità viene espressa in espressa in gigabyte ( GB = 1000.000.000 Byte ). La capacità
può essere aumentata sia aumentando il numero di piatti che incrementando la densità con cui le
informazioni vengono memorizzate sui piatti che compongono l'hard disk.
La velocità di trasferimento è la quantità di dati fornita dal disco rigido in un determinato tempo (in
genere si prende 1 secondo come riferimento). Usare dischi che ruotino più velocemente o
incrementare la densità di memorizzazione porta ad un miglioramento diretto della velocità di
trasferimento.
Il tempo di accesso è il tempo medio necessario perché un dato, residente in un punto casuale del
disco, possa essere reperito. Il tempo di accesso impiegato dipende dalla
• velocità della testina a spostarsi sul cilindro dove risiede il dato
• dalla velocità di rotazione del disco; maggiore è la velocità e più breve è il tempo impiegato
dal dato a ripassare sotto la testina nel caso questa non fosse arrivata in tempo sul dato,
durante la rotazione precedente ( latenza rotazionale ).
Il tempo di accesso medio tipico per un disco rigido da 7200 rpm ( rotazioni per minuto ) è di circa
9 millisecondi. Per uno da 15.000 rpm è inferiore a 4 ms.
IL tempo di accesso medio si può calcolare come :
TAccesso = TS
TS
TSEL
TL
TT
+
TSEL + TL +
TT
dove
tempo di seek cioè il tempo necessario a spostare la testina sul cilindro richiesto;
tempo di selezione della faccia ( trascurabile );
tempo di latenza cioè il tempo necessario a che il settore richiesto passi sotto la testina;
tempo di trasferimento di un settore dalla superfice del disco al buffer del disco.
33
Fra questi quattro tempi quello che incide maggiormente sul tempo di accesso è TS ( è nell’ordine di
qualche ms ) per cui è necessario minimizzare lo spostamento del braccio attraverso efficaci
algoritmi di schedulazione del braccio del disco. Essi sono:
FIFO ( First In First Out )
Le diverse richieste sono servite secondo l’ordine di arrivo. L’algoritmo risulta
• lento
• equo
SSTF ( Shortest Service Time First )
Vengono servite prima le richieste che generano il minor spostamento della testina. L’algoritmo
risulta
• efficente
• non equo
SCAN
Si ha la scansione del braccio prima in una direzione e poi nella direzione opposta, durante la
scansione vengono servite le richieste in arrivo ( come ascensore ). Il senso di scansione si inverte
quando non vi sono più richieste da soddisfare nella direzione del movimento. L’algoritmo risulta
• meno efficiente rispetto al SSTF;
• più equo anche se favorisce le richieste vicine ai cilindri più esterni e interni.
C-SCAN ( Circular SCAN )
Variante dello SCAN il braccio serve sempre le richieste in una sola direzione. Questo algoritmo è
• Più equa rispetto allo SCAN
N-SCAN
Variante dello SCAN che divide le richieste che via via arrivani in più code. Mentre una coda è
servita, le nuove richieste sono inserite nelle altre code. Le diverse code sono servite ciclicamente.
• Efficiente come lo SCAN
• Più equo rispetto allo SCAN
34
Virtualizzazione delle periferiche ( SPOOL )
I dispositivi virtuali sono dispositivi fisicamente "non esistenti" simulati con l’ausilio di altri
dispositivi fisici, come la Memoria Virtuale precedentemente esaminata. Altro esempio di
virtualizzazione delle periferiche è lo SPOOL (Symultaneous Peripheral Operations On Line), una
tecnica che consente la gestione multipla di periferiche seriali (che possono essere concesse ad un
processo alla volta) quali la stampante.
Le stampanti virtuali vengono simulate con l’ausilio del disco e dell’unica stampante di sistema
presente. Ad ogni processo che usa la stampante viene associato, su disco, un file di spool nel quale
vengono memorizzate le informazioni da stampare. Quando il processo dichiara che la stampa è
terminata (o quando il processo termina), il relativo file di spool viene chiuso ed inviato alla
stampante. Se la stampante è libera esso viene stampato, altrimenti la sua richiesta di stampa viene
accodata nella coda di stampa ad attendere che la stampante abbia stampato i file di spool le cui
richieste di stampa sono in coda prima di essa.
SPOOL -virtualizzazione della stampante di sistema
35
La Gestione delle Informazioni
La gestione delle informazioni ha un ruolo importante nell’architettura di un SO, perché le
informazioni sono la risorsa fondamentale di ogni applicazione. Il file è l’oggetto della gestione
delle informazioni. Le informazioni sono memorizzate in file e i programmi applicativi
acquisiscono l’input dai file e restituiscono le informazioni sotto forma di file. A differenza della
memoria centrale , i file sono oggetti che esistono indipendentemente dalle applicazioni che li
usano e sono collocati in unità periferiche, come strutture permanenti di memorizzazione delle
informazioni.
La parte del SO che gestisce le informazioni si chiama file system ( sistema di archivi ). Esso ha il
compito di gestire i file registrati su disco e, di conseguenza, le operazioni di accesso ai file si
traducono in operazioni di input / output con il disco.
I file
Un file ( termine inglese per "archivio" ) è un contenitore di informazione in forma digitale. Le
informazioni
codificate
al
suo
interno
sono
leggibili
solo
da
software.
Tecnicamente, i dati codificati in un file sono organizzati come una sequenza di byte,
immagazzinati come un unico elemento su un disco rigido, all'interno del file system esistente su
quel disco rigido.
Se dal punto di vista logico cioè dell'utente, un file è solitamente visto come un singolo elemento,
da un punto di vista strettamente fisico il file è un insieme di blocchi ( settori o record fisici ). Uno
dei compiti del sistema operativo è nascondere alle applicazioni la reale suddivisione fisica del file
in blocchi e occuparsi di gestire la scrittura e il recupero delle informazioni.
Blocco 1
Blocco 2
Blocco 3
Blocco 4
Blocco 5
Blocco 6
L’utente non deve preoccuparsi dell’allocazione fisica dei blocchi, tracce o settori, ma solo di
associare ad un file un nome e conoscere le caratteristiche che ne definiscono la struttura e le
modalità di accesso.
Il file è una collezione di record logici, cioè di informazioni logicamente omogenee, che
descrivono istanze di un’entità. Ogni record è composto da un insieme di campi che contengono i
valori assunti dalle proprietà che descrivono l’entità.
Nell’esempio che segue l’entità è studente, ogni riga della tabella descrive un’istanza ( cioè uno
studente ) ed ogni campo è una proprietà dello studente.
225
345
657
123
341
151
100
campo
codice
Bianchi
Rossi
Magnati
Negri
Bruni
Viola
Gialli
campo
cognome
Adolfo
Francesco
Nicola
Annalisa
Giuliano
Pietro
Alessandra
campo
nome
……….
……….
……….
……….
……….
……….
……….
campo
……….
……….
……….
……….
……….
……….
……….
campo
1^
2^
3^
4^
5^
6^
7^
record logico
record logico
record logico
record logico
record logico
record logico
record logico
Il campo da un punto di vista logico è l’unità elementare di informazione ed è composto da un
certo numero di byte. Le sue caratteristiche essenziali sono
• Dimensione espressa in Byte;
• Tipo
carattere, numero, stringa, data ecc.. ecc…
In un settore o record fisico possono essere registrati più record logici il rapporto
36
dimensione del record fisico
----------------------------------dimensione del record logico
prende il nome di fattore di bloccaggio.
La dimensione del campo può esser fissa
Rossi
Giuseppe
30 Byte
20 Byte
E’ evidente in questo caso che vi è un grande spreco di spazio.
Roma
30 byte
Nel caso il campo sia di dimensione variabile dovremo in qualche modo indicare la dimensione del
campo stesso
19 5 Verdi 8 Giuseppe 6 Milano
o comunque delimitarne la fine attraverso un delimitatore di campo ( nel nostro caso # )
Verdi# Giuseppe# Milano#
I file hanno una serie di attributi
• Nome
• Identificatore
un numero che distingue il file nel file system
• Tipo
necessaria ad associare l’applicazione che deve manipolarlo
• Locazione
puntatore alla locazione del file sul disco
• Dimensione
• Protezione
identifica può leggerlo, scriverlo ed eseguirlo
• Data di creazione
• Data ultima modifica
• Identificazione dell’utente PID del proprietario del file
e su di essi si possono fare una serie di operazioni:
• creazione
• apertura
• chiusura
• lettura
• scrittura
• cancellazione
• posizionamento in un file
• troncamento di un file ( svuotamento )
Alcuni sistemi operativi provvedono a fornire meccanismi per bloccare file già aperti, in modo
da impedire che altri processi tentino di aprirli contemporaneamente. La tipologia di un file viene
usata per ottenere informazioni sulla sua struttura. Le operazioni di apertura e chiusura di un file
vengono definite nel modo seguente.
Apertura: trasferisco gli attributi del file in memoria centrale. Nel PCB del processo che opera sul
file c’è una struttura che si chiama tabella dei file aperti, che mi permette di avere accessi rapidi al
processo. Anche una parte del file può essere portato sulla memoria centrale ( perché può essere
molto grande ); nel caso dell’accesso sequenziale porterò un blocco a sinistra e a destra dal record
logico ‘n’ da cui parto.
Chiusura: mi sbarazzo delle informazioni del file messe in memoria e cancello dalla tabella dei file
aperti del PCB relativo al processo che lo stava utilizzando il puntatore al file.
37
Metodi di accesso. Per metodo di accesso si intendono le modalità con le quali è possibile
accedere al record di un file. Per accedere ad un file abbiamo essenzialmente un punto di vista
logico ( ovvero di come il file si presenta agli utenti ) e un punto di vista fisico ( ciò che è nella
realtà ) che sono diversi tra loro ma sono strettamente correlati. Dal punto di vista logico un file è
organizzato come un’insieme di elemento, detti record logici, che tutti insieme formano il file
stesso. Il sistema operativo consente di scorrere i vari record logici che compongono il file
operando essenzialmente su di essi tramite due tipi di accessi.
Accesso Sequenziale: per accedere ad un particolare record N, devo scandire gli N – 1 record
logici che lo precedono.
Accesso Diretto: si può accedere direttamente al record logico desiderato senza scorrere tutti i
record uno alla volta .
File System Logico
In genere il file system prevede una struttura gerarchica ad albero che parte dalla radice principale
( C: ) e ha i file organizzati in directory ( indici o cartelle ) gerarchiche, ciascuna delle quali può
contenere altri file e altre directory.
La directory stessa è un file speciale costituito da un record descrittivo per ogni file o directory in
essa contenuti. Il FS fornisce le operazioni di gestione creazione, cancellazione, spostamento di file
e directory. Si occupa dell’apertura dei file, della loro protezione, della condivisione di file e
directory gestendo gli accessi contemporanei per evitare incongruenze, delle organizzazioni dei file
e dei metodi di accesso (sequenziale, diretto, …), del recovery ( ad esempio "salva/ ripristina
configurazione di sistema" di Windows), etc..
Le informazioni relative ai file contenute nella directory sono gli attributi visti prima: Nome,
identificatore, tipo, locazione, protezione, data di creazione, data ultima modifica, identificazione,
dell’utente.
Le directory vanno organizzate in modo che:
• sia garantita una buona efficienza (ovvero localizzare velocemente un file).
• due utenti possano avere lo stesso nome per file diversi.
• lo stesso file possa avere differenti nomi per diversi utenti.
• che siano possibilmente raggruppati per tipo ( programmi, documenti, video,musica, etc..).
Essenzialmente vi sono cinque tipi di struttura di directory.
• Single-Level. Tutti i file sono contenuti in un’unica directory. Presenta gli svantaggi che i
file, essendo nella stessa directory, devono avere nomi unici e che è pesante gestire tutti i
file in un unico luogo.
• Two-Level. Ogni utente ha una sua directory personale chiamata ‘user directory’. Di tutte
queste directory tiene traccia la ‘master directory’. Questo sistema risolve il problema
dell’unicità dei nomi dei file fra utenti, ma non consente a più utenti di cooperare tra loro.
• Tree-Structure. Possono essere create sottodirectory. Per ogni file compaiono i cosiddetti
path name assoluti (iniziano dalla radice fino al file) e path name relativi (da una
sottodirectory al file). Non è possibile condividere files.
• Grafo Aciclico. Utile per condividere directory e files; la condivisione può essere applicata
tramite links (puntatori a un altro file contenete il percorso assoluto o relativo) o tramite
copie duplicate. Bisogna però gestire i problemi derivanti dai dati condivisi e soprattutto
38
•
verificare che non vi siano cicli nel grafo.
Grafo Generico. Assicurarsi che non via siano cicli.
Come un file va aperto prima di essere usato, un file system va montato ( mounted ) prima di
essere disponibile per i processi sul sistema; un mounted-point è il luogo dove un file system deve
essere attaccato ( tipicamente è un directory vuota ).
Con le strutture di directory ad albero è semplice assegnare ai file nomi significativi rispettando la
richiesta di dare nomi differenti ad ogni file. Infatti il nome del file è ottenuto , a partire dalla
directory radice, concatenando tutti i nome delle directory che si incontrano sino alla directory che
contiene il file. Ad esempio, facendo riferimento alla figura precedente:
C:\Documenti\pippo
Questo è il path assoluto ( percorso ) del file pippo assoluto perché parte dalla radice.
C:\Windows\System\pippo
è differente dal pippo dell’esempio precedente infatti il pathname è diverso
Se sono nella directory Windows posso riferirmi a pippo con il patname relativo alla cartella in cui
mi trovo cioè Windows:
\System\pippo
Protezione.
Quando le informazioni sono salvate sul disco, dobbiamo proteggerle dai danneggiamenti e dagli
accessi indesiderati. Dai danneggiamenti ci si tutela con le copie di backup; dagli accessi
indesiderati, invece, creando tipologie di accessi dipendenti dall’identità dell’utente.
Per ogni file avemo i diritti di accesso per il proprietario ( user ), il gruppo di appartenenza del
proprietario ( group ) e per gli altri ( other ) . Ad esempio nei sistemi Unix/Linux per ogni file
avremo:
user group other
|
|
|
------ ------ -----
-rwx r-x --x
-rwx rwx rwx
pippo
documento
Dove r = read ( lettura ) w = write ( scrittura ) x = execute ( esecuzione ).
Quindi il file pippo potrà essere scritto, letto e eseguito dal proprietario, letto e eseguito dal gruppo,
solo eseguito dagli altri. Sul file documento tutti potranno fare scrittura, lettura ed esecuzione.
Struttura e organizzazione dei file
Un file dal punto di vista fisico può essere descritto come un insieme di blocchi o record fisici o
settori. Il file system a questo livello ha il compito di tenere traccia dello stato dei blocchi sapendo
quali sono i liberi e quali occupati, e di allocare quote di disco ai file tramite metodi detti metodi di
allocazione dello spazio su disco. I principali metodi sono descritti di seguito.
Allocazione Contigua.
I blocchi che costituiscono il file sono allocati contiguamente sul disco.
Vantaggi:
• è semplice c’è solo bisogno del blocco iniziale e del numero di blocchi occupato dal file;
• supporta il metodo di accesso diretto.
Svantaggi:
• soggetto al problema della frammentazione esterna del disco rigido;
• i files non possono crescere di dimensione perché si rischia di collidere con altri files
bisogna necessariamente allocare nuovamente spazio su disco.
39
Allocazione Linkata.
I blocchi che compongono il file sono organizzati a lista linkata; questi blocchi possono essere
dislocati ovunque sul disco fisso. Ogni blocco è suddiviso in due parti, informazione e puntatore al
blocco successivo.
informazione
indirizzo del blocco successivo
Vantaggi:
• è un metodo semplice, per applicarlo c’è bisogno solo del puntatore iniziale alla lista
• non è soggetto al problema della frammentazione esterna.
Svantaggi:
• viene utilizzato dello spazio in più per memorizzare un puntatore in ogni blocco.
• non supporta il metodo di accesso diretto ( per accedere al blocco i-esimo devo
necessariamente partire dall’inizio della lista e scandire i blocchi che lo precedono ).
• poco affidabile, si pensi ad esempio a che potrebbe succedere se un puntatore venisse perso
o danneggiato.
Allocazione Indicizzata.
E’ simile all’organizzazione linkata solo che in questo caso tutti i puntatori ai blocchi che
compongono il file sono portati all’interno di un unico blocco che chiameremo blocco indice. I
puntatori sono memorizzati in sequenza, dal primo all’ultimo blocco. Se il blocco indice raggiunge
dimensioni troppo grandi è possibile fare un blocco indice di blocchi indice.
40
Vantaggi:
• supporta il metodo di accesso diretto quindi trovare un blocco è immediato;
• non è soggetto al problema della frammentazione esterna.
Svantaggi:
• non è semplice da realizzare ( c’è bisogno del blocco indice)
• spreco di spazio, specialmente se si hanno tanti file di piccole dimensioni.
Allocazione con mappa dei blocchi dei file ( FAT - File Allocation Table )
Utilizza le idee alla base dell’allocazione linkata e indicizzata. Infatti i blocchi che compongono
un file sono riconosciuti da un elenco di puntatori, come nel caso delle liste di blocchi collegate, ma
l’elenco dei puntatori è centralizzato, come nel caso dell’allocazione indicizzata.
In particolare l’elenco dei puntatori è memorizzato in una struttura dati centralizzata datta FAT.
La FAT è un vettore con tanti elementi quanto sono i blocchi del disco; gli elementi della fat sono
collegati in liste che permettono di trovare tutti i blocchi che compongono un file. La FAT è
memorizzata su disco ma per velocizzare le operazioni può essere caricata in memoria centrale.
Allocazione dei file nei sistemi Unix / Linux
Consiste nel tenere i primi 15 puntatori del blocco indice in una struttura di dati detta inode del
file. I primi 12 di questi puntano a blocchi diretti, cioè contengono direttamente gli indirizzi di
blocchi che contengono i dati del file. Quindi i dati per piccoli file non richiedono un blocco indice
distinto. Gli altri 3 puntatori puntano a blocchi indiretti. Il primo puntatore di blocco indiretto è
l’indirizzo di un blocco indiretto singolo, ovvero un blocco indice che non contiene dati ma
41
indirizzi di blocchi che contengono dati. Infine abbiamo un puntatore a un blocco indiretto doppio e
un puntatore a un blocco indiretto triplo.
Gestione deli blocchi liberi
Essenzialmente vi sono due modi per gestire lo spazio libero.
Se ne tiene traccia tramite una free-space list implementata come un vettore di bit.
bit[ i ] = 0 se il blocco è libero, bit[ i ] = 1 se il blocco è occupato .
Questo metodo ha l’inconveniente che, per ragioni di efficienza, il vettore di bit va tenuto in
memoria centrale e sappiamo che può essere molto grande.
Un altro metodo di gestione dei blocchi liberi consiste nel collegarli tutti in una lista linkata, e
quindi tenere un puntatore al primo di questi in una speciale locazione del disco e caricarlo nella
memoria. Questo primo blocco contiene un puntatore al successivo blocco libero e così via.
Protezione e Sicurezza
In una macchina esistono tanti oggetti, processi, etc.., e il sistema operativo deve assicurare la loro
protezione.
Struttura dei domini di protezione.
Diritti di accesso è una coppia che rappresenta <nome oggetto, {diritti}>
Dominio: insieme di diritti di accesso. Un dominio può rappresentare ciò che un processo è in
grado di fare.
Accesso a Matrice
Il sistema operativo deve fornire un meccanismo che permetta di specificare vari diritti su sti
oggetti e vedere che siano rispettati. Il sistema operativo può memorizzare questa matrice in due
modi:
utente/file File 1 File 2 Printer
Franco
r
Pino
r, w
p
42
Nicola
r, w, e
r
Svolgere attività sulla protezione e sicurezza comporta limiti all’efficienza del sistema, per esempio
aggiungere controlli al tempo d’esecuzione. Lo spingere la sicurezza dipende molto dall’ambiente
in cui ci troviamo. Tra la minacce fondamentali troviamo:
• Lettura dei dati (data confidentiality)
• Integrità dei dati (data modified)
• System avaiability (denial of service): il sistema impiega tutto il suo tempo nel gestire
continue richieste dell’esterno. Per esempio un sito web che non funziona più perché è
costretto a gestire un numero elevatissimo di richieste dalla rete esterna.
Autenticazione
Come ci garantiamo che un’utente che vuole lavorare su una macchina sia accettato? Abbiamo il
sistema basato su login ( username e password ), che però è facilmente attaccabile.
1) One time password. Per migliorare questo sistema si possono utilizzare password che vengono
utilizzate solo per una sessione. L'utilità sta nel fatto che se un intruso se ne appropria non se ne fa
niente perchè alla sessione dopo non viene più utilizzata.
2) Challenge-Response Authentication. Si tratta ancora di password che cambiano nel tempo.
L’utente seleziona un algoritmo, per esempio x^2
Durante il login:
• il server spedisce un numero intero i
• l’utente risponde con i^2
Problema: l’algoritmo può essere indovinato.
Soluzione:
una funzione ‘f’ (pubblica)
l’utente seleziona una chiave ‘k’
durante il login:
• il server spedisce un numero intero i
• l’utente risponde con f ( i, k )
43