Eccovi!
In questa sezione troverete le lezioni
del corso di sistemi operativi.
A:”Ma
tu allora non capisci che esigenze abbiamo noi nonvedenti!”
B:”Invece si!”
A:”Si vede, si vede!”
B:”Ok, guardate: essendo un nonvedente anch’io so che in un testo lungo siamo
svantaggiati. Così hoposizionato dei segnalibri durante il testo ed un indice
all’inizio! Così la consultazione sarà molto più rapida!”
A:”Evvaiiiii!”
Indice
“Presentazione”:
Parte 2: Importanza di un sistema operativo
Evoluzione dei sistemi operativi I
Evoluzione dei sistemi operativi II
“Evoluzione
dei sistemi operativi III:”
“Gestione
dei processi I:”
“Gestione
dei processi II:”
“Gestione
della memoria I:”
“Gestione
della memoria II:”
“Gestione
del file system”Lezione 12
“Funzioni
di un SO: gestione della memoria secondaria e dell’I/O”Lezione 13
Software
Planet - Corso di Sistemi Operativi - Presentazione
Corso
di Sistemi Operativi
Presentazione
Con questa
puntata iniziamo un interessante excursus all'interno di un campo molto
vario ed affascinante, quello dei
sistemi operativi, del quale vedremo
differenti aspetti
di sicuro interesse, sia
per gli appassionati del
genere sia per i comuni utenti del
classico PC.
Il corso
si articolerà essenzialmente in tre
parti: la prima parte introdurrà
i concetti teorici di
base dei sistemi operativi tradizionali; nella
seconda
parte vedremo
una rassegna dei sistemi
operativi attualmente in
circolazione, dai classici sistemi Microsoft all'affermato Linux agli emergenti
BeOS e
QNX; infine, nella terza parte,
approfondiremo la conoscenza della filosofia
GNU e della sua GPL (General Public License), e di
Linux in particolare, alla
diffusione del
quale si deve, principalmente, l'impetuosa avanzata dell'Open
Source alla quale stiamo
tuttora assistendo.
Data
la loro maggiore diffusione,
prenderemo in considerazione principalmente le
architetture Intel e quelle
ad esse compatibili, evidenziando, quando possibile, disponibilità e
caratteristiche per le
altre piattaforme.
Per eventuali
suggerimenti e
segnalazioni potete fare riferimento direttamente al redattore del corso
(Angelo Carpenzano)
o
al webmaster di Software Planet
(Tommaso Trani).
Cominciamo ricordando che un sistema di
elaborazione può essere suddiviso,
in prima
approssimazione, in due componenti: l'hardware e il software.
Con
il termine hardware si intende essenzialmente l'insieme dei
costituenti fisici di un computer: circuiti
integrati, fili,
alimentatori, tubi a
raggi catodici e altri dispositivi
di questo genere.
Con
il termine software si intende una
grandissima categoria di programmi e applicazioni, senza i quali
un computer, secondo
la definizione
dell'autorevole professore A. S.
Tanenbaum (http://www.cs.vu.nl/~ast/),
non sarebbe altro che un
"ammasso di ferraglia".
E' il
software, infatti, che consente ad un calcolatore di memorizzare
ed elaborare
informazioni, di inviare
e ricevere e-mail da Internet,
di far girare un gioco d'avventura, di eseguire tutta una serie
di compiti, più o meno complessi, che lo rendono utile e,
ai nostri giorni, indispensabile
per molti
(pensiamo a quante persone ci si guadagnano da vivere!).
Non
bisogna dimenticare, tuttavia, che il
software esiste anche in moltissime altre macchine di cui
si fa
uso abitualmente: dalle centraline di controllo delle automobili ai programmi di lavaggio delle moderne lavatrici, dai sistemi di navigazione aerospaziale ai
telefoni cellulari...
E' comunque
evidente che tutti questi sistemi sono
dedicati ad uno scopo ben
preciso e, con rare e lievi eccezioni,
durante il loro ciclo di vita
non subiscono modifiche di
comportamento; in questo si
differenziano dai
computer, oltre che
per ovvie caratteristiche fisiche quali dimensioni, forma e complessità.
Un calcolatore può essere anch'esso
adibito ad un solo uso: si pensi,
ad es., ai
grossi mainframe che si
occupano di raccogliere ed elaborare i dati provenienti dai
satelliti e dalle stazioni orbitanti
sparpagliate intorno al nostro pianeta.
Diverso è il
caso dei personal computer, ai quali, invece, viene chiesto di soddisfare
un ampio ventaglio di esigenze
diverse; si suole dire
che il PC è general purpose, per indicare proprio questa caratteristica di estrema
varietà di applicazioni ed usi che gli vengono assegnati.
In
un sistema del genere è chiaro che i
problemi si complicano, perché si
richiede che più applicazioni girino contemporaneamente e che più utenti possano simultaneamente
accedere alle risorse
della macchina per stampare,
collegarsi ad Internet, accedere ad un server remoto in una rete locale,
e tanto altro ancora.
E'
in uno scenario di questo tipo che sorge
l'esigenza di avere qualcosa che si occupi di gestire questa complessità.
Questo qualcosa
è proprio il sistema operativo, di cui cominceremo a parlare a partire dalla prossima puntata.
A
Cura di Angelo Carpenzano ( (c) )
Software
Planet - Corso di Sistemi Operativi
- Importanza di
un sistema operativo
Corso
di Sistemi Operativi
Importanza
di un sistema operativo
(parte
2)
Nella puntata precedente abbiamo accennato
alla complessità che caratterizza un calcolatore, a partire
proprio dal semplice PC, di cui
tutti (o quasi) disponiamo.
In
virtù di questa complessità, il software
di un calcolatore può essere suddiviso
in due grosse categorie: i programmi di
sistema (che controllano e
regolano il comportamento del
calcolatore stesso) e i programmi
applicativi (che servono per la
risoluzione dei più disparati
problemi degli utenti).
I
programmi applicativi sono noti a tutti
gli utenti di un computer e possono essere di vario tipo: editor, word
processor, foglio di calcolo, browser
Internet, giochi, programmi per la riproduzione e la creazione di file
multimediali, e così via.
I
programmi di sistema sono quelli che,
in gran parte nascosti agli
occhi degli utenti comuni, rendono difficile,
ma allo stesso tempo interessante e stimolante, la vita
degli esperti del settore; tra questi
il più importante è il sistema operativo, croce e delizia
di almeno due generazioni di programmatori e studiosi.
Il sistema
operativo (che in seguito indicheremo con l'acronimo
SO) svolge una funzione
fondamentale: agisce come intermediario tra l'utente e il calcolatore stesso,
controllando tutte le risorse
dell'hardware e mettendo a
disposizione un ambiente nel
quale eseguire i programmi applicativi.
Esaminiamo
innanzitutto il motivo per il quale esistono i sistemi operativi.
Un calcolatore moderno è un sistema molto articolato,
costituito da uno
o più processori, da una
certa quantità di memoria principale (la famosa RAM), da uno o più dischi,
dalla tastiera, dal monitor, dal
mouse, dalla stampante, dalla
scheda video, dalla scheda
audio, dal modem, dalle interfacce
di rete e da tanti altri dispositivi di basso livello, di
cui molti ignorano l'esistenza.
Se
chi scrive un programma dovesse tener
conto di tutti
questi componenti e usarli correttamente, curandosi dei
dettagli con cui essi
lavorano e delle decine di cose che potrebbero andare storte durante ognuna delle loro possibili operazioni, certamente
ci penserebbe due volte prima di complicarsi la vita e probabilmente molti programmi non sarebbero mai stati scritti.
Molti
anni fa ci si rese conto che era
necessario nascondere al programmatore la complessità
dell'hardware, sia per un problema di
efficienza dei programmi, sia
per permettere il trasporto
del software da una macchina all'altra con caratteristiche
hardware differenti.
Venne
allora introdotto uno strato di
software al di
sopra dell'hardware nudo e crudo, che si occupasse di controllare
tutte le parti del
sistema, presentando all'utente un'interfaccia più semplice da capire e più facile da programmare: il sistema
operativo, appunto.
Riepilogando possiamo
affermare che un SO ha principalmente due compiti:
gestire
i dispositivi hardware;
supervisionare l'esecuzione dei programmi,
assegnando in maniera opportuna le risorse e rilevando eventuali errori.
Per comprenderne pienamente l'importanza
diamo uno sguardo all'architettura interna
di un calcolatore, di
cui uno schema semplificato è riportato in figura
2.1.
Fig. 2.1. Struttura a livelli di un calcolatore digitale
Come possiamo vedere, l'architettura
di un elaboratore può essere
strutturata in livelli, ognuno dei quali comunica solo con quelli adiacenti.
E' interessante notare che il linguaggio macchina, pur essendo
un software, viene annoverato all'interno del
componente hardware: ciò è dovuto al fatto
che
esiste un legame strettissimo tra le istruzioni del linguaggio macchina e i dispositivi
fisici che le implementano.
Ogni piattaforma (Intel, AMD,
Motorola, IBM, ecc.)
ha caratteristiche
hardware differenti dalle altre ed è dotata di un proprio linguaggio
macchina; se non esistesse
il sistema operativo, un
programmatore dovrebbe preoccuparsi di
scrivere una versione del proprio programma per ognuna di esse, o
limitarsi ad ignorarne alcune, seguendone
le evoluzioni per adattarvisi.
Per ovviare
a questo enorme inconveniente, il SO fornisce al programmatore, e
agli utenti in genere, un'unica interfaccia, qualsiasi sia
la piattaforma
sottostante: è compito
di chi scrive il SO far sì che
le azioni decise dal programmatore siano tradotte nelle opportune
istruzioni per la macchina.
Nella puntata successiva vedremo cosa succede quando accendiamo un calcolatore.
A
Cura di Angelo Carpenzano ( (c) )
Software
Planet - Corso di Sistemi Operativi - Avvio di un SO
Corso
di Sistemi Operativi
Avvio
di un SO
Nella lezione precedente abbiamo compreso
che il SO
ha un'importanza fondamentale, perché si addossa la
responsabilità di gestire il
calcolatore liberandoci
dall'obbligo di
conoscerne la struttura
interna.
Prima di
esaminare le caratteristiche
dei SO, spendiamo qualche parola
su quello che avviene al nostro PC al momento dell'accensione, ossia durante la cosiddetta fase di boot, facendo
riferimento alla piattaforma hardware i386 (Intel) non potendo avere accesso ad altri tipi
di architettura.
Per prima
cosa il processore carica da una ROM
(Read Only Memory - memoria a sola lettura) il firmware
della scheda madre, ossia un
programma che provvede alla gestione dei primi istanti di funzionamento della
macchina, fornendo una serie di funzionalità che verranno sfruttate da
programmi successivi per completare l'avviamento.
Guardando con
attenzione le scritte che
vengono mostrate a video
non appena si accende
il computer, possiamo vedere
che il bios (cioè il firmware
degli elaboratori i386) esegue
alcuni test, riconoscendo l'hard
disk e altre periferiche collegate
alla scheda madre: questa
fase prende il nome di post, ovvero power on self test.
Al
termine di questa fase, il bios carica
il master boot
record (spesso indicato con
l'acronimo MBR), costituito da
un settore localizzato nei
primi 512 byte dell'hard disk, contenente, tra le
altre cose, il programma deputato
al caricamento del
sistema operativo, il cosiddetto
boot loader.
Nel
caso di un sistema contenente un solo SO, questo viene avviato automaticamente, spesso mostrando a
video delle informazioni che permettono di sapere
quali operazioni esso sta compiendo;
se il nostro sistema
contiene più sistemi operativi,
un boot loader opportunamente configurato presenterà un menu o un
prompt che ci permette di specificare quale di essi avviare.
Ogni
SO ha il proprio boot loader (ad es. il
lilo di Linux), ma non tutti consentono
il multiboot, cioè la
possibilità di poter avviare SO differenti.
Ad
es. i sistemi basati su MS-DOS, come i
diffusissimi Windows 9x, permettono di installare un loader
proprietario nell'MBR (tramite il
comando fdisk /mbr),
che non consente di
indicare sistemi alternativi, ma
provvede semplicemente a
caricare il boot sector di una
partizione indicata come attiva.
Per comprendere il meccanismo di avviamento di un SO, soffermiamoci
sull'organizzazione di un
hard disk, facendo riferimento alla figura 3.1.
Fig. 3.1.
Organizzazione di un hard disk in due partizioni.
Un hard
disk può essere logicamente suddiviso in partizioni,
ossia in porzioni dello spazio di memorizzazione trattate come
unità separate e costituite da un piccolo settore di
avvio, il boot sector, e da una frazione
più o meno grande deputata a
contenere i programmi e i dati veri e propri.
La forma con cui una partizione viene
organizzata dipende dal tipo di file system che la gestisce.
Nel
caso più semplice un hard disk è
costituito da una
sola partizione, di cui
programmi e dati condividono lo spazio; tuttavia
una buona prassi è quella
di mantenere almeno due
partizioni, una per il
SO e i programmi applicativi e una per i dati.
Il
master boot record contiene una tavola
delle partizioni, che specifica il modo in
cui sono partizionati tutti gli hard disk connessi al sistema
e gli attributi delle varie
partizioni.
Tra
le altre cose, una partizione può
essere:
Attiva (serve
al boot loader MS-DOS per sapere in
quale partizione si trova
il SO da avviare; può esserci
una sola partizione attiva);
Bootable (se contiene un sistema operativo avviabile)
Nascosta
(non viene resa visibile dal SO
all'utente)
Quando
si configura un boot loader per il
multiboot, si associa ad ogni
voce di SO da
avviare la relativa partizione
e il suo programma di caricamento.
All'avvio il SO esegue una serie di controlli e
procedure d'inizializzazione
che possono prendere anche parecchi secondi, dopodiché rimane sempre
attivo, fornendo un
ambiente all'interno del quale le
applicazioni vengono di volta in volta
aperte e chiuse.
Dalla
prossima puntata cominceremo a conoscere i concetti e le teorie che stanno dietro alla
progettazione di un SO.
A
Cura di Angelo Carpenzano ( (c) )
Software
Planet - Corso di Sistemi Operativi -
Il SO come macchina astratta
Corso
di Sistemi Operativi
Il
SO come macchina astratta
Nella seconda
puntata abbiamo visto che
l'architettura di un generico
sistema di calcolo può essere
strutturata in un
certo numero di livelli (cfr. fig. 2.1); ogni livello prende il nome
di macchina virtuale e
possiamo indicarlo come MV0, MV1, ..., MVn.
Fig. 4.1. Relazioni tra macchine virtuali.
Ogni
macchina virtuale MVi dispone di un
insieme di oggetti Oi e di un
linguaggio Li per la gestione e il
controllo degli oggetti dell'insieme Oi.
Le istruzioni di Li sono implementate per
mezzo di programmi scritti nel
linguaggio Lj con j<i,
dove tipicamente j=i-1; ciò
vuol dire che
le istruzioni del linguaggio Li
sono generalmente realizzate per
mezzo di programmi del
linguaggio al livello
immediatamente inferiore.
Generalmente le istruzioni di Li sono dette meccanismi, perché rappresentano le
azioni che quel linguaggio è in grado
di compiere; i programmi di Lj che implementano tali meccanismi sono detti politiche, in quanto costituiscono
il modo in cui un
determinato meccanismo è realizzato.
Quando
si realizza un livello MVj, occorre
conoscere quali sono
i meccanismi di Li,
cioè del linguaggio al livello immediatamente sopra, e scegliere una politica per l'implementazione di quel meccanismo.
Generalmente non
esistono due sviluppatori che implementano lo stesso
meccanismo alla stessa identica maniera: ciò vuol dire che
lo stesso meccanismo
può essere realizzato attraverso politiche differenti, che
portano, evidentemente, a
prestazioni con caratteristiche
diverse.
Se esaminiamo
la struttura a livelli di un calcolatore secondo questo modello, vengono fuori alcuni dettagli molto
interessanti, che ci
fanno comprendere meglio in che modo
il SO esegue i propri compiti,
ossia controllare e allocare le
risorse in modo "equo" ed efficiente
(gestione della concorrenza, protezione, gestione
dei files, etc.) e
semplificare l'utilizzo delle
risorse da parte dell'utente (librerie
standard, interfacce, etc.).
Fig.
4.2. Macchine virtuali in un
calcolatore digitale.
In figura 4.2 è reso evidente il ruolo
della macchina virtuale SO come
intermediario tra l'utente e
l'hardware, attraverso delle interfacce chiare e semplici verso
l'uno e l'altro livello.
Analizziamo la
figura partendo dall'alto.
L'utente
rappresenta una macchina virtuale che
lavora con degli oggetti, quali ad es.
file e database, che
può gestire e controllare per mezzo di programmi
scritti in
linguaggi ad alto livello.
Questi ultimi
utilizzano delle istruzioni,
quindi dei meccanismi, che vengono implementate dalle corrispondenti
politiche del SO, il quale
agisce direttamente sulle risorse della
macchina, come la CPU e la memoria.
Tutto
ciò viene fatto grazie alle politiche
che gli vengono offerte dalla macchina
virtuale Assembler attraverso il linguaggio macchina (indicato con L/M), le
cui istruzioni sono formate
da sequenze di cifre
binarie che vengono
interpretate dal firmware per eseguirle
sull'hardware vero e proprio.
Infine, l'hardware
non ha un linguaggio nel pieno
senso del termine, perché
il modo di realizzare i meccanismi del firmware è di tipo elettronico, cioè per mezzo di
segnali elettrici che viaggiano all'interno di
una struttura fisica in grado
di implementare funzioni
logiche.
Non
è nostra intenzione procedere
all'esame del modo
in cui l'hardware viene
realizzato, ma possiamo accennare al fatto che, al pari di ogni
altro livello, è possibile realizzare
gli stessi meccanismi del
firmware per mezzo di politiche
hardware differenti, con conseguenti differenze nelle prestazioni, nell'affidabilità e
nel consumo di energia della macchina (problema, questo, molto sentito in caso di
dispositivi portatili).
Sono proprio
le politiche del linguaggio macchina, del firmware e
dell'hardware a fare
la differenza tra le
diverse architetture: un SO
che gira su una macchina può
dar luogo a prestazioni del tutto diverse da
quelle che riesce a dare su
una macchina con un'architettura differente, così
come diverse possono essere le
prestazioni che vengono fuori
dallo stesso hardware quando è gestito da SO distinti.
Svariati
studi hanno rilevato che le politiche del SO sono determinanti nel decidere quanta potenzialità dell'hardware riesce ad essere sfruttata
realmente nell'esecuzione
dei programmi utente.
E' proprio di questi aspetti che ci occuperemo nella seconda parte del corso.
A
Cura di Angelo Carpenzano ( (c) )
Evoluzione dei sistemi
operativi (I)
Prima
di procedere oltre diamo una rapida
occhiata alla storia dei sistemi
operativi, esaminando in
che modo si è giunti allo
stato attuale attraverso un
graduale processo di astrazione che ha reso le scelte progettuali
dei sistemi operativi indipendenti dalle caratteristiche della macchina.
Si
ritiene che il primo prototipo di calcolatore digitale si debba all'inglese Charles Babbage, che
intorno alla metà dell'Ottocento progettò la
"macchina analitica", un
apparecchio meccanico che egli non potè mai vedere realizzato a causa dell'impossibilità tecnologica di allora di realizzare gli ingranaggi e le
parti meccaniche richieste con la
dovuta precisione.
Da
allora fino alla seconda guerra mondiale
furono fatti pochissimi
progessi nella realizzazione di calcolatori digitali.
Occorre attendere
il 1945 per vedere comparire la prima generazione di
calcolatori automatici, quando vari professori universitari e studiosi
inglesi e
americani (tra cui
von Neumann) riuscirono a
costruire delle enormi macchine
di calcolo usando delle valvole
elettroniche.
Questi apparecchi avevano dimensioni tali da riempire intere stanze e una
velocità di calcolo a dir poco modesta
in confronto al più scarso degli
attuali personal computer.
Allora era
lo stesso gruppo di persone ad occuparsi della progettazione, costruzione, programmazione e manutenzione di ogni macchina.
La programmazione era fatta
esclusivamente in linguaggio macchina, ossia
secondo codici binari direttamente eseguiti dal processore, ed era realizzata per
mezzo di
una serie di spinotti inseriti su particolari schede adibite al controllo delle
più elementari funzioni della macchina.
I linguaggi
di programmazione,
compreso l'assembler, erano sconosciuti e di sistema operativo
nemmeno si parlava.
Quasi
tutte le applicazioni erano
semplici calcoli numerici,
come produzioni di tabelle
di seni e coseni.
Fino alla
metà degli anni
50 l'unico progresso riguardò
la possibilità di scrivere i programmi su schede perforate, che potevano essere lette direttamente
dal
calcolatore, anziché usare gli spinotti.
A cambiare
radicalmente la
situazione intervenne
l'introduzione dei transistor, che accrebbero
l'affidabilità dei
calcolatori così da
renderne possibile la
commercializzazione: entra
in scena la
seconda generazione dei calcolatori digitali.
Cominciarono allora a
delinearsi dei profili professionali distinti tra progettisti, costruttori, operatori, programmatori e tecnici della
manutenzione.
Questi primi
sistemi, molto costosi, furono
detti sistemi batch o sistemi
di elaborazione a lotti e funzionavano
nel seguente modo.
I calcoli
che dovevano essere eseguiti dal computer erano memorizzati in un job, che poteva essere un singolo programma o un
insieme di programmi.
Un piccolo calcolatore (relativamente) poco costoso
(tipicamente l'IBM 1401) leggeva le
schede perforate sulle quali erano
memorizzati i job
e li trasferiva su un nastro magnetico.
Quando
il nastro aveva raccolto un lotto
(batch) di lavoro, veniva riavvolto e inserito in
un calcolatore più potente (prevalentemente l'IBM
7094), dotato dell'antenato degli attuali sistemi operativi, l'IBSYS.
Questo si occupava di leggere in sequenza i vari job e di eseguirli
uno dopo l'altro memorizzando i risultati su un
secondo nastro magnetico.
Una volta
completato il lotto, venivano rimossi i nastri d'ingresso e di uscita, riavvolti,
e il
nastro di uscita
veniva inserito in un altro IBM 1401 per
stampare i
dati ottenuti
dall'elaborazione.
Tramite l'utilizzo
dei nastri d'ingresso e di uscita,
veniva realizzata una "primordiale" forma di concorrenza, potendo eseguire l'elaborazione dei
lotti contemporaneamente
alla memorizzazione del lotto successivo e
alla stampa dei risultati di quello precedente.
Questa
tecnica era detta spooling (dal termine
inglese Simultaneous Operation On
Line).
Essenzialmente
i calcolatori della seconda generazione
furono usati per eseguire calcoli
scientifici e ingegneristici, anche perché
il loro costo poteva essere sostenuto solo da grosse compagnie, dalle agenzie governative e dalle università.
Furono
programmati in FORTRAN e in linguaggio assembler.
Accanto all'IBSYS
un sistema operativo tipico di
quegli anni era il
FMS (Fortran Monitor System).
A
Cura di Angelo Carpenzano ( (c) )
Evoluzione dei sistemi
operativi (II)
Proseguendo
il nostro viaggio nel passato ci
troviamo all'inizio degli anni 60, quando la maggior parte dei produttori di calcolatori
avevano due linee distinte di prodotti: da un lato i
grossi calcolatori scientifici come l'IBM 7094, dall'altro
i calcolatori commericali di
più limitata capacità elaborativa, come l'IBM 1041.
Per risolvere
tutti i problemi correlati ad una tale situazione, l'IBM (allora in una situazione pressoché monopolistica) introdusse il
System/360, una serie di macchine
compatibili a livello software,
che differivano solo per prezzo e
prestazioni (si andava dal
1041 a macchine più potenti del 7094).
Il sistema 360 fu la prima linea di
calcolatori ad usare
la tecnologia dei circuiti integrati (IC), allora a bassa
scala di integrazione, che portò innumerevoli
vantaggi in termini di prezzo e
prestazioni.
Inutile dire che il successo dei calcolatori compatibili convinse gli altri
costruttori a fare altrettanto.
La compatibilità software fra
i diversi computer fu un fattore
di grande forza, ma anche un
punto debole.
Principalmente ciò
è dovuto al fatto
che l'intenzione era
di avere lo stesso
software, SO compreso, su tutti i modelli, ma le
grandi differenze nelle caratteristiche hardware e nell'uso in ambito
scientifico e commerciale avevano
requisiti spesso in conflitto fra loro.
Il voluminoso
sistema operativo che venne
adottato, l'OS/360, era costituito da
milioni di righe di codice assembler
scritto da migliaia di
programmatori e conteneva
centinaia di errori, che rendevano
necessarie versioni
sempre nuove, con le quali
si correggevano i vecchi
e se ne introducevano di nuovi...
Nonostante
le enormi dimensioni e gli errori, i
sistemi operativi della terza
generazione seppero
soddisfare gli utenti,
anche grazie
all'introduzione di una nuova tecnica,
la multiprogrammazione.
A
causa dei costi elevatissimi dei calcolatori, si impose la necessità di ridurre la quantità
di tempo in cui la CPU
rimaneva inattiva, permettendo ad un job di occupare il processore mentre un altro
job era in
attesa del completamento di
un'operazione di I/O.
Per
fare ciò la memoria principale venne
divisa in partizioni, una dedicata al SO e
le rimanenti assegnate a vari
job.
Tramite lo
spooling le schede contenenti i job venivano subito
lette e i job caricati su disco; in maniera analoga si operava per la
gestione dell'uscita dei risultati, eliminando così per sempre la "scocciatura" del continuo spostamento di nastri magnetici da un calcolatore all'altro.
In questi sistemi, detti a lotti multiprogrammati, si presentarono per la prima volta
nuove problematiche:
lo scheduling dei job (decidere quale tra i
job nel disco caricare in memoria);
lo scheduling del processore
(decidere quale tra
i job in memoria mandare in esecuzione);
la gestione della memoria
(ottimizzarne l'utilizzo, date le
limitate dimensioni);
la protezione reciproca (da
eventuali interazioni
indesiderate).
Il
desiderio di un veloce tempo di risposta
portò ben presto
al timesharing
(condivisione di tempo), una
variante della
multiprogrammazione che permetteva a più utenti di avere un terminale ciascuno.
In questi sistemi la CPU veniva assegnata a turno ai vari utenti che ne richiedevano l'uso e venne reso
disponibile un servizio interattivo molto veloce, sfruttando i
tempi di inattività degli utenti che
stavano pensando, parlando o prendendo il caffè per eseguire i job degli altri.
I primi
sistemi operativi timesharing furono
il CTSS, sviluppato dal
1962 al MIT (Massachussets Institute of Technology) e l'ambizioso
MULTICS (MULTiplexed
Information and Computing Service),
sviluppato nella seconda metà
degli anni 60 da gruppi del MIT,
dei laboratori Bell e
della GE (General Electric).
Entrambi i
sistemi non ebbero rilevanza commerciale a causa della mancanza di un necessario hardware per
la protezione e MULTICS
in particolare fu
un fiasco.
Tuttavia
quest'ultimo preparò la strada
alla creazione di uno dei più
popolari ed efficienti sistemi operativi di tutti i tempi: UNIX.
Vedremo in che modo
ciò avvenne nella prossima
puntata.
A
Cura di Angelo Carpenzano ( (c)
Evoluzione dei sistemi
operativi (III)
Lezione
7
Oltre
a quanto visto nella puntata precedente,
durante la terza generazione ebbe luogo una fenomenale crescita dei
minicalcolatori, a partire
dal PDP-1 del 1961.
Nella serie dei
PDP successivi, fino al PDP-11,
il più importante (per ragioni
storiche) fu il PDP-7, sul quale venne sviluppata,
da Ken Thompson (uno degli informatici dei laboratori Bell che
aveva collaborato alla realizzazione di
MULTICS), una versione ridotta
per singolo utente di MULTICS,
chiamata per
gioco
UNICS (Uniplexed Information and
Computing Service), che non tardò a convincere gli addetti ai lavori, diventando ben presto UNIX
(implementato per la prima volta proprio su un PDP-11/20).
L'evoluzione incredibile dei minicalcolatori
portò ad una migrazione delle caratteristiche dei
SO nati per mainframe verso i SO usati
su tali sistemi,
cosicché UNIX potè avvantaggiarsi
dei progressi compiuti negli anni
precedenti.
A
Cura di Angelo Carpenzano ( © )
Evoluzione dei sistemi
operativi (III)
Lezione
7
La quarta generazione nasce con l'avvento dei
circuiti LSI (Large Scale Integration), che portò, alla fine
degli anni 70,
alla costruzione dei primi
personal computer, molto simili
ai minicalcolatori come il PDP-11, ma più economici.
Venne introdotto il termine
workstation (stazione di lavoro) per
indicare i PC utilizzati da uomini d'affari, agenzie
governative ed università, sebbene
non fossero
altro che personal computer un pò più
grandi, talvolta
connessi insieme attraverso
una rete.
L'aumentata diffusione dei calcolatori
e l'accresciuta potenza di
calcolo, portò allo sviluppo di varie case aziende per la
produzione di software per PC,
il cui
scopo principale era produrre software user-friendly,
cioè amichevole, rivolto a persone che
nulla conoscevano o volevano
conoscere dei calcolatori, ma che non volevano
rinunciare ad utilizzarli.
A
Cura di Angelo Carpenzano ( © )
Evoluzione dei sistemi
operativi (III)
Lezione
7
Una di queste
aziende nascenti,
denominata Microsoft, venne fondata da un giovane programmatore di nome Bill Gates, che
qualche anno prima
aveva scritto una versione di BASIC
per Altair (che molti
considerano il primo vero
personal computer,
prodotto nel 1975), che in
poco tempo lo fece
diventare multimilionario
(quando si dice nato
con la camicia_:-); nessuno
allora era in grado di prevedere quanto grande sarebbe stata l'influenza di
questa azienda nello sviluppo e
la diffusione dei PC.
Sin dall'inizio
i due maggiori sistemi operativi
che dominarono la scena
dei PC e
delle workstation furono MS-DOS
della Microsoft e UNIX.
MS-DOS
(Microsoft - Disk Operating System) fu largamente usato su PC IBM e sui sistemi compatibili che utilizzavano i processori Intel (dall'8088 ai suoi successori); le
prime versioni erano
piuttosto primitive, ma le
successive vennero
arricchite di
caratteristiche avanzate, molte delle quali riprese da
UNIX (infatti, ironia della
sorte, l'attuale avversario
numero 1 di Linux -
il più conosciuto dei figli di
UNIX - allora era
il principale
distributore dei sistemi UNIX
:-}).
A
Cura di Angelo Carpenzano ( © )
Evoluzione dei sistemi
operativi (III)
Lezione
7
UNIX invece
spopolava sui calcolatori non-Intel
e sulle workstation, particolarmente in quelle equipaggiate con chip
RISC ad alte prestazioni.
In questo
scenario, durante la metà degli anni 80, cominciavano a
prendere piede le reti di personal computer,
sui quali giravano
i primi sistemi operativi di rete
e i sistemi operativi distribuiti.
La fine
degli anni 80
è caratterizzata dal diffondersi dei vari dialetti di UNIX, che
dal progenitore di proprietà dell'AT&T (evolutosi nello UNIX System
V con le sue varie release), derivarono
diverse varianti (BSD,
Minix, Linux, FreeBSD, OpenBSD, NetBSD, e altri ancora), alcune
delle quali distribuite gratuitamente e
con i sorgenti.
A
Cura di
Angelo
Carpenzano ( © )
Evoluzione dei sistemi
operativi (III)
Lezione
7
Una
nota particolare merita Linux, apparso
per la prima volta
nel 1991, che, nato su
iniziativa di un giovane studente di
nome Linus Torvalds, allievo
di A. S. Tanenbaum (l'autore di Minix), ha ben presto ricevuto l'appoggio di numerosi
sviluppatori sparsi in tutto
il mondo, che hanno potuto
contribuire al suo
sviluppo soprattutto grazie ad Internet.
L'affermazione di
questo SO è storia
recente ed è tuttora
in crescita.
Per concludere questa carrellata storica,
non possiamo non menzionare l'incredibile diffusione dei sistemi operativi
basati sul DOS di Microsoft (MS-DOS, Windows
3.x e Windows 9x), che, seppure
con i loro difetti e demeriti, hanno certamente
il merito di
aver introdotto i PC
nelle case di tutto il mondo.
Per il
futuro siamo ancora
in attesa e, come
vedremo nella seconda parte
del corso, si prospetta ricco di interessanti novità!
A
Cura di
Angelo
Carpenzano ( © )
Funzioni di un SO:
gestione dei processi I
Lezione
8
Data
la complessità di un SO, esso può
avere componenti alquanto diverse tra loro e un sistema può differire da un altro
per moltissimi aspetti.
Tuttavia all'interno di un qualsiasi SO
è quasi sempre possibile trovare dei sottosistemi
che interagiscono tra
loro per realizzare le funzioni
necessarie
alla
gestione dell'intero sistema di
elaborazione:
1.
Gestione dei processi
2. Gestione della memoria
principale
3. Gestione della memoria
secondaria
4.
Gestione del file system
5.
Gestione dei dispositivi di I/O
6.
Protezione delle risorse
7.
Interprete dei comandi
A
Cura di
Angelo
Carpenzano ( © )
Funzioni di un SO:
gestione dei processi I
Lezione
8
Un concetto
chiave in tutti
i sistemi operativi è il processo, ovvero l'istanza di un programma
in esecuzione, che è
costituito da:
1. il
codice eseguibile (detto anche testo);
2.
i dati su cui il codice lavora;
3.
una lista (più o meno lunga) di strutture dati e informazioni che occupano risorse di sistema.
E'
indispensabile comprendere che
programma e processo sono due cose distinte.
Un
programma è un'entità passiva, un codice
binario che può risiedere in un sistema
di memorizzazione (come il
disco) anche se il computer è spento e la
cui durata può essere (almeno in teoria) infinita.
Di
contro, un processo è un'entità attiva,
che scaturisce da
un programma quando questo
viene mandato in esecuzione e la cui durata è variabile
e dipende
dall'ambiente
di esecuzione.
Inoltre per esistere un processo ha bisogno che il
computer sia almeno acceso :-).
A
Cura di
Angelo
Carpenzano ( © )
Funzioni di un SO:
gestione dei processi I
Lezione
8
Per chiarire meglio il concetto facciamo un
esempio.
Quando in
un sistema Microsoft WindowsÒ eseguiamo l'applicazione "Notepad" ("Blocco note" nella
versione italiana), il
sistema crea un processo a
partire dal programma
notepad.exe che si trova nella
directory di sistema (tipicamente C:\WINDOWS).
Il
processo non esiste finché non ne invochiamo
esplicitamente la creazione,
mentre il programma era già nel computer quando il sistema è stato avviato.
Se,
inoltre, mandiamo più volte in esecuzione il programma (attenti a non esagerare!), vedremo aprirsi tante finestre dell'editor quante sono
state le nostre selezioni: ciascuna copia del Notepad non è altro che un'istanza dello stesso programma notepad.exe, e per il sistema
ognuna di esse
è un processo distinto.
Altrettanto avviene in
qualsiasi altro SO, anche
se esistono varianti che al momento non è il caso di considerare.
A
Cura di Angelo Carpenzano ( © )
Funzioni di un SO:
gestione dei processi I
Lezione
8
Per
potere eseguire un programma è necessario
che esso sia caricato in memoria
principale, insieme ad una serie
di informazioni indispensabili,
come ad es. valori di variabili e il
puntatore alla prima istruzione da
eseguire (noto come program counter,
spesso indicato con la sigla
pc), che complessivamente
prendono il nome di contesto del
processo.
Durante l'esecuzione il pc viene continuamente
aggiornato per puntare alla
locazione di memoria che contiene
l'istruzione da eseguire al
passo successivo.
Se un
SO permette l'esecuzione contemporanea di
più processi, detti anche task, allora si dice che
il SO è multitasking o multiprogrammato.
Tutti i
sistemi attuali sono multitasking, classici esempi di sistemi
monoprogrammati sono le versioni di MS-DOS precedenti alla 5.0.
A
Cura di
Angelo
Carpenzano ( © )
Funzioni di un SO:
gestione dei processi I
Lezione
8
In
sistemi con un solo processore
(per intenderci, la
stragrande maggioranza dei PC)
non è possibile che più
processi vengano eseguiti
contemporaneamente dall'unica
CPU, ma esistono dei meccanismi del SO che consentono
di avere l'illusione che ciò avvenga, attraverso un continuo passaggio da un processo all'altro; si
parla perciò di pseudo-parallelismo, per distinguerlo dal
parallelismo reale che avviene nei sistemi con più CPU.
Perché il
multitasking possa funzionare è
necessario che il SO, prima di passare da un processo ad un altro, salvi il contesto del
processo che sta
eseguendo attualmente, in
modo che quando esso verrà rimesso in
esecuzione, potrà proseguire dal punto in cui era stato sospeso senza accorgersi di nulla.
Quando un processore passa da un processo
ad un altro si dice che esegue un
cambio di contesto, ovvero un context switch.
Nella prossima puntata continueremo
a parlare di processi.
A
Cura di Angelo Carpenzano ( © )
Funzioni di un SO:
gestione dei processi II
Lezione
9
Quanto
detto nella scorsa puntata ci fa comprendere che un SO vive di processi.
In realtà
il SO è anch'esso
costituito da processi
che cooperano
adeguatamente per
garantire un corretto funzionamento del sistema
(almeno, questo è quello che si
dovrebbe cercare di fare).
Riprendendo il
discorso sul multitasking,
diciamo innanzitutto che il modulo del SO che si occupa di gestire il context
switch viene detto scheduler.
Lo scheduling, cioè l'operazione dello
scheduler, può essere realizzato in
modi diversi, secondo le
modalità che il progettista del sistema sceglie di
adottare.
Un obiettivo importante che deve essere perseguito, quando si sceglie un algoritmo di scheduling, è sfruttare al massimo il
tempo di esecuzione della CPU,
evitando, nel
contempo, che un processo ne monopolizzi l'uso impedendo agli altri processi di
progredire.
A
Cura di Angelo Carpenzano ( (c)
Funzioni di un SO:
gestione dei processi II
Lezione
9
Riguardo a quest'ultimo aspetto, un SO è
detto preemptive (o prelazionato) se lo scheduler può arbitrariamente sottrarre il processore ad un
task per assegnarlo ad
un altro (ad es. perché quest'ultimo ha una priorità maggiore); altrimenti si parla di
scheduling non-preemptive.
Una
tecnica di schedulazione molto usata
è nota col nome di
round robin.
Secondo lo schema round robin ad ogni
processo viene assegnato un time slice,
ovvero una piccola frazione di tempo (dell'ordine dei
millisecondi), durante la
quale gli viene aggiudicata totalmente la CPU.
Allo scadere
del time slice il clock, ossia l'orologio di sistema che regola
la temporizzazione delle operazioni della macchina, invia al processore un'interrupt
(un'interruzione), per
indicare che il processo
attuale ha occupato l'intero
time slice assegnatogli e
che è possibile eseguire un nuovo processo.
In
base alle politiche attuate dal SO,
la CPU esegue un context switch o continua a lavorare con
il processo corrente.
Per sfruttare
al massimo le potenzialità del sistema, il SO sospende un processo in esecuzione
quando esso deve attendere che venga
completata una determinata
operazione.
A
Cura di Angelo Carpenzano ( (c)
Funzioni di un SO:
gestione dei processi II
Lezione
9
Ad
es. se un processo deve leggere un
file dal disco,
passerà un certo tempo
prima che i
dati attesi dal processo
saranno disponibili, a causa dell'elevato
tempo di
accesso del disco (rispetto alla scala dei tempi di una CPU).
Sospendendo il processo
fino al completamento del
trasferimento dei dati, la
CPU può essere assegnata ad un processo pronto per essere eseguito, evitando
così che essa rimanga inutilizzata.
Dunque,
un processo durante la sua esecuzione può trovarsi in diversi stati, che
possiamo riassumere
sommariamente in tre:
1. in
esecuzione (quando il processo sta utilizzando
effettivamente la CPU
in quell'istante);
2.
pronto (quando può essere messo in
esecuzione, ma è temporaneamente sospeso per permettere ad un altro processo di eseguire);
3.
bloccato (quando non può essere messo
in esecuzione finché
non accade un evento esterno,
ad es. il completamento di un'operazione di I/O).
Generalmente,
durante la sua vita, un processo transita continuamente da uno stato all'altro
secondo le regole adottate dal SO.
Funzioni di un SO:
gestione dei processi II
Lezione
9
Una transizione
interessante è quella dallo
stato di bloccato allo stato di pronto,
che presuppone che il SO sia in
grado di rendersi conto del verificarsi
di
un dato evento.
Le soluzioni adottate sono
generalmente due:
1.
polling
2.
interrupt
Con
il
polling il SO
deve periodicamente
interrogare i dispositivi di
I/O per verificare il loro
stato di esecuzione, spendendo tempo
di CPU per controllare
tutti i
dispositivi della macchina.
Con la tecnica
degli interrupt, invece, sono
le periferiche ad avvertire il SO quando si verifica un
evento che ne cambia lo stato: questa
soluzione è la
più
efficiente ed è quindi la più usata.
Il meccanismo
degli interrupt richiede un
hardware opportuno per il controllo dei
segnali di interruzione e i
sistemi operativi che lo utilizzano (detti
per
questo interrupt driven, cioè
guidati dagli eventi) mantengono delle strutture software adeguate per riconoscere il tipo
di interrupt ed intraprendere
le
relative azioni di gestione.
Nella
prossima puntata vedremo il
sottosistema di gestione
della memoria principale.
A
Cura di Angelo Carpenzano (c)
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria principale (I)
Lezione
10
Al
momento dell’esecuzione ogni programma deve risiedere, almeno in parte, nella
memoria centrale.
In
un sistema monoprogrammato, questa condizione può essere facilmente soddisfatta
imponendo un limite alla dimensione massima che un processo può avere;
soluzione,
questa, certamente non elegante e poco efficiente.
La
situazione si aggrava nei sistemi multiprogrammati, in cui un numero
imprecisato di processi aventi dimensioni variabili possono contemporaneamente
contendersi
il
processore per l’esecuzione.
La
contesa fra i vari processi riguarda naturalmente anche la memoria e il SO deve
fornire dei meccanismi per tenere conto delle parti di memoria utilizzate
e
di quelle libere, per allocare memoria ai processi quando ne hanno bisogno e
deallocarla quando hanno finito, e infine per gestire gli scambi fra la
memoria
principale e il disco, quando la memoria non è sufficiente a contenere tutti i
processi.
A
Cura di
Angelo
Carpenzano ( © )
Funzioni
di un SO: gestione della memoria principale (I)
Lezione
10
Infatti,
con il timesharing, ad un sistema sono normalmente collegati tanti utenti e la
memoria non è in grado di contenere tutti i loro processi, rendendo
così
necessario scaricarne alcuni su disco.
Per
far girare i processi che ad un dato istante risiedono nel disco, è necessario
che essi vengano caricati in memoria: l’operazione relativa allo spostamento
di
processi dalla memoria al disco e viceversa prende il nome di swapping
(scambio) e il modulo del SO che se ne occupa si chiama swapper.
Alcuni
sistemi operativi (ad es. Linux) richiedono una partizione dell’hard disk
dedicata esclusivamente a questo scopo, mentre altri sistemi (ad es. Windows)
creano
ed utilizzano un file particolare (in Windows questo file si chiama Win386.swp
e si trova nella directory C:\WINDOWS\).
A
Cura di
Angelo
Carpenzano ( © )
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria principale (I)
Lezione
10
Per
permettere a più processi di risiedere contemporaneamente in memoria, questa
viene divisa in partizioni o regioni, ossia porzioni di celle di memoria
contigue,
che possono essere di dimensioni fisse o variabili, secondo le scelte fatte da
chi sviluppa il sistema.
La
maggior parte dei sistemi attuali utilizza partizioni variabili, perché
presentano una maggiore flessibilità, resa necessaria dal fatto che i processi
hanno
dimensioni molto diverse tra loro e non tutti richiedono la stessa quantità di
memoria per poter eseguire.
Inoltre
gran parte dei processi cambia dinamicamente la propria dimensione durante
l’esecuzione, richiedendo di volta in volta una quantità di memoria variabile
e,
spesso, non predicibile.
Occorre
notare che, a seguito dello swapping, un processo non occupa sempre le stesse
le regioni di memoria, anzi, la cosa più probabile è che ogni volta
che
il processo viene rimesso in memoria dal dispositivo di swap (swap device), il
SO gli assegnerà regioni differenti, in relazione alla situazione di
occupazione
della memoria al momento in cui viene richiesto il caricamento di quel
processo.
A
cura di
Angelo
Carpensano
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria principale (I)
Lezione
10
Riguardo
allo spazio di swap, l’allocazione può avere caratteristiche differenti da
sistema a sistema: alcuni non allocano spazio sullo swap device fino
a
che il processo non viene scaricato e ad ogni scaricamento questo può essere
piazzato in una zona diversa del disco; altri sistemi al momento della
creazione
del
processo gli riservano spazio per lo swap sul disco, cosicché esso occuperà
sempre lo spazio che gli è stato assegnato.
In
quest’ultimo caso, quando il processo termina lo spazio viene liberato.
Lo
swapping puro prevede che il caricamento e lo scaricamento di un processo
avvenga per intero.
Esiste
però una soluzione alternativa che tiene conto di una caratteristica comune a
tutti i processi: ad un dato istante ogni processo esegue utilizzando
solo
un sottoinsieme del suo spazio di indirizzamento.
A
Cura di
Angelo
Carpenzano ( © )
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria principale (I)
Lezione
10
Lo
spazio di indirizzamento è l’insieme degli indirizzi di memoria generati da un
programma.
Ad
un dato istante, un programma in esecuzione utilizza solo gli indirizzi
relativi alle istruzioni che esegue in quel momento, per cui si potrebbe
pensare
di
tenere in memoria solo il sottoinsieme di indirizzi necessario, evitando di
occupare la memoria con informazioni non necessarie.
Su
questa importante proprietà si basa il metodo della memoria virtuale, di cui
discuteremo nella prossima lezione.
A
Cura di
Angelo
Carpenzano ( © )
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria principale (II)
Lezione
11
Nella
scorsa puntata abbiamo accennato al metodo della memoria virtuale: l’idea di
base della memoria virtuale è che la dimensione combinata di testo, dati
e
stack dei processi può eccedere la quantità di memoria disponibile per essi.
Abbiamo
anche notato che, ad un determinato istante, i processi non riferiscono tutto
il loro spazio di indirizzamento, ma solo una sua parte: per indicare
questa
proprietà si utilizza il concetto di località.
Poiché
un processo indirizza celle di memoria contigue in una piccola porzione dello
spazio di indirizzamento totale si parla di località spaziale (un caso
che
possiamo immaginare è quello in cui un processo sta scorrendo in sequenza gli
elementi di un array).
Poiché
una stessa cella di memoria può essere riferita frequentemente per un certo
intervallo di tempo, si parla di località temporale (ad es. una cella
di
memoria viene prima letta e poi scritta).
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria principale (II)
Lezione
11
Il
SO può allora mantenere in memoria le parti che sono in uso in un certo
momento, mentre le altre parti vengono mantenute nello swap device.
Tuttavia
occorre notare che il processo cambia località quando passa da una fase
dell’esecuzione all’altra, cosicché è necessario cambiare le sue parti
in
memoria quando deve accedere a nuovi indirizzi.
La
memoria virtuale si sposa molto bene con la multiprogrammazione: mentre un
programma aspetta che una sua porzione venga caricata in memoria, è di fatto
in
attesa di un’operazione di I/O e non può girare, cosicché il processore può
essere assegnato ad un altro processo.
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria principale (II)
Lezione
11
Naturalmente
in un sistema di questo tipo, gli indirizzi generati dai programmi non possono riferirsi
direttamente alla memoria, perché durante l’esecuzione
di
un processo la memoria viene dinamicamente allocata e deallocata, per
permettere di caricare e scaricare le porzioni degli spazi di indirizzamento
usate
dai
processi in memoria.
Per
questo motivo gli indirizzi generati dai programmi vengono detti indirizzi
virtuali e formano lo spazio di indirizzamento virtuale.
Gli
indirizzi virtuali, anziché essere inviati direttamente alla memoria - come
succede nei sistemi che non hanno la memoria virtuale -, vengono inviati
ad
un’unità hardware, detta MMU (Memory Management Unit – unità di gestione della
memoria), che trasforma gli indirizzi virtuali in indirizzi della memoria
fisica.
Possiamo
pensare alla MMU come ad una funzione che riceve un parametro d’ingresso
(l’indirizzo virtuale) e dà in uscita un valore (l’indirizzo fisico).
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria principale (II)
Lezione
11
Lo
spazio di indirizzamento virtuale viene suddiviso in unità chiamate pagine e le
corrispondenti unità nella memoria fisica vengono dette pagine fisiche.
Pagine
e pagine fisiche hanno la stessa dimensione e i trasferimenti da e verso il disco
vengono effettuati sempre in unità di una pagina.
Quando
la MMU riceve un indirizzo virtuale, essa calcola il numero della pagina alla
quale appartiene: se essa è presente in memoria, la MMU estrae il
corrispondente
numero
della pagina fisica e l’indirizzo fisico corrispondente all’indirizzo virtuale;
se la pagina non è in memoria viene invocato un page fault (fallimento
di
accesso alla pagina), con il quale il controllo viene passato al SO che si
occupa di recuperare al pagina dal disco.
Questo
metodo di gestire la memoria pone una complessa problematica che deve essere
accuratamente esaminata e risolta da chi progetta un SO.
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria principale (II)
Lezione
11
Innanzitutto
la scelta della dimensione delle pagine è un fattore molto importante, perché
da essa derivano le dimensioni delle strutture dati adibite alla
gestione
della memoria.
Inoltre
la traduzione di un indirizzo virtuale in un indirizzo fisico è un’operazione
che ricorre ad ogni accesso in memoria, quindi, proprio per l’elevata
frequenza
con la quale deve essere eseguita questa operazione, è necessario che la
traduzione avvenga velocemente.
Quando
si verifica un page fault il SO deve scegliere una pagina da rimuovere dalla
memoria principale per far posto a quella che deve essere caricata;
occorre
perciò scegliere un efficiente algoritmo che consenta di minimizzare i page
fault futuri causati dalla rimozione di una pagina.
Questi
sono solo alcuni dei problemi che un progettista di SO incontra nel progetto
del modulo di gestione della memoria, anche se spesso è aiutato da un
hardware
creato per supportare efficientemente una scelta piuttosto che un’altra.
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Lezione
12
Parte
1 di 5
Uno
degli aspetti che più interessano l’utente di un SO è la possibilità di
memorizzare permanentemente (anche per decenni) le proprie informazioni e che
sia
possibile reperirle facilmente in un secondo momento.
A
tal riguardo, i requisiti essenziali che vengono richiesti ad un SO sono tre:
1.
possibilità di memorizzare grandi quantità di informazioni;
2.
conservazione i dati anche dopo la terminazione del processo che li ha creati;
3.
possibilità per più processi di accedere concorrentemente a tali informazioni.
Per
raggiungere questi obiettivi la soluzione comune adottata da tutti i SO è
quella di registrare i dati su dischi e altri supporti esterni, in unità dette
file
(termine inglese che vuol dire archivio).
Il
sottosistema del SO che si occupa della gestione dei file è detto File
Management System (FMS).
I
file di un sistema, inclusi quelli che servono al suo stesso funzionamento,
sono raccolti in una struttura detta file system, che ha il compito di
presentare
agli
utenti un’interfaccia semplice e immediata per accedere ai dati.
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Gestione
del file system
Lezione
12
Le
caratteristiche e l’organizzazione di un file system variano da un sistema ad
un altro, ma presentano, complessivamente, dei punti in comune:
-
I file sono un meccanismo di astrazione, cioè offrono un modo per memorizzare e
recuperare dati su un disco, senza che l’utente debba preoccuparsi del
modo
in cui tale servizio viene realizzato.
-
Un processo che crea un nuovo file gli assegna un nome, permettendo ad altri
processi di utilizzare quel nome per accedere alle informazioni che esso
contiene.
-
Ad ogni file è associato un gruppo di attributi, come il nome del file, la data
e l’ora di creazione, la data e l’ora dell’ultimo accesso, la data e l’ora
dell’ultima
modifica, la dimensione, il proprietario, un flag di sola lettura (per indicare
se è possibile leggere/scrivere il file o se è consentita la
sola
lettura), un flag di sistema (per indicare se è un file utente normale o un
file di sistema), un flag “nascosto” (per indicare se è un file da non
visualizzare
nel listato del file system), campi di protezione e tanti altri.
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Gestione
del file system
Lezione
12
-
Il file system viene generalmente organizzato in una struttura ad albero, per
mezzo di opportuni contenitori chiamati directory, che possono raccogliere
al
loro interno file e altre directory.
-
Vengono fornite dal SO una serie di operazioni per creare, cancellare, leggere,
scrivere e rinominare file e directory, nonché operazioni per leggerne
e/o
scriverne gli attributi.
Sebbene
questi ed altri meccanismi siano forniti da tutti i sistemi operativi, vi sono
delle differenze significative.
Alcuni
sistemi, come MS-DOS, nominano i file mediante un meccanismo di estensione, cioè
dividono il nome del file in due parti separate da un punto, dando
un
significato particolare alla parte del nome che segue il punto.
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Gestione
del file system
Lezione
12
Ad
es. un file chiamato prog.c rappresenta, per sistemi che adottano questa
convenzione, un programma sorgente scritto in linguaggio C.
Altri
sistemi, come UNIX, lasciano all’utente piena libertà sul nome da assegnare,
permettendo che il nome di un file abbia più estensioni; ad es. prog.c.tgz
può
essere utilizzato per nominare un file sorgente C compresso.
Inoltre
il numero ed il tipo di attributi che un file può avere dipendono fortemente
dal SO.
Ad
es. i sistemi Windows 9x e Me, che adottano un file system basato su MS-DOS,
noto come FAT, non associano dei campi di protezione ai file, che così possono
essere
acceduti da chiunque.
Invece
i sistemi Windows NT e 2000 (che adottano il file system denominato NTFS) e
quelli basati su UNIX, come Linux, FreeBSD, Solaris e altri, associano
ad
ogni file dei bit di protezione, ad es. distinguendo tra l’utente proprietario,
il gruppo di utenti del proprietario e tutti gli altri utenti del sistema
(questi
concetti verrano chiariti nelle lezioni successive).
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Gestione
del file system
Lezione
12
Altra
importante differenza è l’implementazione delle directory.
Tipicamente
una directory contiene un certo numero di elementi, uno per ogni file e
directory in essa contenuti.
Prima
che un utente possa accedere ad un file, il SO deve esaminarne gli attributi
per decidere se rendere possibile o meno l’accesso e per aggiornarne
alcuni,
come ad es. la data e l’ora dell’ultimo accesso.
Il
progettista di un SO può scegliere di memorizzare gli attributi del file
nell’elemento corrispondente della directory oppure in altre strutture
indirizzate
da
quell’elemento (v. figura).
Nella
prossima puntata vedremo come un SO gestisce la memoria secondaria.
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria secondaria e dell’I/O
Parte
1 di 5
Nelle
scorse puntate abbiamo visto che, per rendere possibile la multiprogrammazione
e il timesharing, si ricorre ad una gestione della memoria principale
che
utilizza intensamente la memoria secondaria, con continui spostamenti di dati e
programmi da e verso lo swap device.
Data
l’elevata frequenza con cui viene richiesto questo trasferimento di
informazioni, per gestire efficacemente un ambiente di questo tipo, un SO deve
offrire
prestazioni elevate, riducendo al minimo il sovraccarico (overhead) dovuto a
queste operazioni.
Per
ottimizzare questi meccanismi, il SO deve fornire le seguenti funzionalità:
-
tener conto dello spazio occupato sul disco e di quello inutilizzato;
-
allocare spazio ai processi che lo richiedono e deallocarlo quando può essere
liberato;
-
schedulare le operazioni del disco rispetto a più richieste provenienti da
processi utente e di sistema.
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria secondaria e dell’I/O
Lezione
13
Un
problema interessante che caratterizza fortemente un sistema è
l’implementazione del file system, cioè il modo in cui vengono memorizzati file
e directory,
e
in particolare come vengono associati i blocchi di disco a ciascun file.
Il
più semplice schema di allocazione prevede che ogni file sia costituito da
blocchi contigui di dati sul disco.
In
questo modo è più semplice tenere traccia della collocazione dei blocchi
relativi ad un file (basta memorizzare l’indirizzo del primo blocco e la
dimensione
dei
dati) e l’accesso alle informazioni è più efficiente, perché è possibile
leggere l’intero file con una sola operazione.
Tuttavia
non è realizzabile, perché difficilmente si conosce a priori la grandezza
massima di ogni file; inoltre porta ad una frammentazione del disco,
dovuta
allo spreco di spazio che potrebbe essere utilizzato altrimenti.
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria secondaria e dell’I/O
Lezione
13
Quest’ultimo
problema potrebbe risolversi con la compattazione, o deframmentazione, che
risulta spesso proibitivamente lunga e costosa (in termini di risorse
di
sistema).
Le
soluzioni più diffuse ricorrono ad un’allocazione a lista concatenata, in cui i
blocchi associati ad un file sono sparpagliati nel disco.
Ogni
blocco può contenere un puntatore al blocco successivo, oppure si può ricorrere
ad una tabella, chiamata FAT (file allocation table) che contiene,
in
ordine, i puntatori ai blocchi di disco costituenti il file e che deve essere
costantemente mantenuta in memoria.
Utilizzare
una lista concatenata di blocchi permette di non perdere spazio a causa della
frammentazione del disco, ma, rispetto all’allocazione contigua,
rallenta
l’accesso ai file.
Un
ulteriore metodo per tenere traccia dei blocchi appartenenti ad un file
consiste nell’associare a ogni file una piccola tabella, detta i-node, che
elenca
gli
attributi e gli indirizzi dei blocchi del file.
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria secondaria e dell’I/O
Lezione
13
Questo
schema, utilizzato in UNIX e nei suoi “derivati”, è molto flessibile e consente
di avere una frammentazione del disco praticamente nulla.
Accenniamo
adesso ad un’altra utile funzione dei SO, la gestione dell’I/O, il cui scopo è
quello di nascondere sia all’utente che al sistema, le caratteristiche
dei
dispositivi di input/output.
Nel
sottositema di I/O possiamo trovare essenzialmente tre elementi:
-
gestione della memoria, per ottimizzare gli scambi di informazioni tra la
memoria centrale e i dispositivi;
-
una collezione di driver associati ai vari dispositivi del sistema, contenenti
tutte le caratteristiche che dipendono dall’hardware;
-
presentazione di un’interfaccia generica, che permette ai programmatori di
utilizzare in maniera uniforme i driver dei dispositivi.
A
Cura di
Angelo
Carpenzano (©)
Corso
di Sistemi Operativi
Funzioni
di un SO: gestione della memoria secondaria e dell’I/O
Lezione
13
Una
caratteristica molto importante dei sistemi UNIX, che vale la pena di citare, è
il buffer cache, una particolare soluzione adottata per ottimizzare
l’accesso
al disco, che utilizza la memoria principale come una “cache” per aumentare le prestazioni
di I/O.
Infatti,
ogni blocco di disco che deve essere letto e/o scritto viene prima caricato in
questa struttura, che viene utilizzata direttamente dai processi
in
modo da non dover ricorrere al disco ogni qualvolta occorre accedere a quel
blocco.
Poiché
la memoria principale è molto più veloce dei dischi, il buffer cache produce un
sostanziale miglioramento delle prestazioni, riducendo le operazioni
di
I/O solo al momento in cui sono necessarie.
A
Cura di
Angelo
Carpenzano (©)