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