appunti sistemi operativi

61
UNIVERSITÀ DEGLI STUDI DI BRESCIA FACOLTÀ DI INGEGNERIA APPUNTI DI SISTEMI OPERATIVI Prima versione: Paolo Bellagente AA 2006/2007 g k e r i o n @y a h o o . it Seconda versione: Luca Seghetto AA 2014/2015 1 - Materiale aggiuntivo e introduzione: Oltre ai presenti appunti è possibile trovare ulteriore materiale: nei file allegati. sul sito del corso: tritone.ing.unibs.it/soa su libri di testo, consigliati: "Sistemi Operativi - Stallings", "Sistemi Operativi Tanenbau" entrambi in lingua inglese. Lo scopo del corso è quello di dare una panoramica generale sull'architet- tura e il funzionamento dei sistemi operativi. 2 - Il Sistema Operativo Come detto sopra in questo corso si parlerà di Sistemi operativi, ma cosa vuol dire sistema operativo (SO da qui in poi)? Il nome in sè non dice molto; andando a leggere alcuni libri di informatici teorici molto fa- mosi si nota che anche loro fanno fatica a definire un concetto così astrat- to; mettiamoci allora d'accordo su cosa intendiamo quando parliamo di SO: " D e finizione " : un SO è un software che fa da intermediario tra l'hard- ware (HW di seguito) e il software (SW di seguito) il cui scopo è rendere possibile il passaggio da Programmi a Processi. La differenza tra Programma e processo è abbastanza immediata: Programma: specifica di un algo- ritmo scritta in un determinato lin- guaggio, entità statica ed astratta. Processo: Programma in esecu- zione su un esecutore; è un'entità concreta, dinamica e collocata nel tempo. Diamo un'occhiata all'architettura generale, essa presenta una struttu- ra a livelli: Le applicazioni utente vengono tra- dotte dai compilatori (interpreti etc..) in un linguaggio più vicino alla macchi- na tuttavia di solito è solo SO ad avere il controllo completo del HW e non l'appli- cazione utente. Per fare ciò il SO maschera parzialmente l'ISA (insieme delle istruzioni tipiche di ogni

Upload: leonkone

Post on 08-Nov-2015

256 views

Category:

Documents


4 download

DESCRIPTION

appunti del corso sistemi operativi dell'università degli studi di brescia, del prof. baronio

TRANSCRIPT

  • UNIVERSIT DEGLI STUDI DI BRESCIAFACOLT DI INGEGNERIA

    APPUNTI DI SISTEMI OPERATIVI

    Prima versione:Paolo Bellagente AA 2006/2007 g k e r i o n @y a h o o . it

    Seconda versione:Luca Seghetto AA 2014/2015

    1 - Materiale aggiuntivo e introduzione:Oltre ai presenti appunti possibile trovare ulteriore materiale:

    nei file allegati. sul sito del corso: tritone.ing.unibs.it/soa su libri di testo, consigliati: "Sistemi Operativi - Stallings", "Sistemi Operativi Tanenbau" entrambi in lingua inglese.

    Lo scopo del corso quello di dare una panoramica generale sull'architet-tura e il funzionamento dei sistemi operativi.

    2 - Il Sistema OperativoCome detto sopra in questo corso si parler di Sistemi operativi, ma cosa vuol dire sistema operativo (SO da qui in poi)? Il nome in s non dice molto; andando a leggere alcuni libri di informatici teorici molto fa-mosi si nota che anche loro fanno fatica a definire un concetto cos astrat-to; mettiamoci allora d'accordo su cosa intendiamo quando parliamo di SO:

    " D e finizione ": un SO un software che fa da intermediario tra l'hard-ware (HW di seguito) e il software (SW di seguito) il cui scopo rendere possibile il passaggio da Programmi a Processi.

    La differenza tra Programma e processo abbastanza immediata: Programma: specifica di un algo-ritmo scritta in un determinato lin-guaggio, entit statica ed astratta. Processo: Programma in esecu-zione su un esecutore; un'entit concreta, dinamica e collocata nel tempo.

    Diamo un'occhiata all'architettura generale, essa presenta una struttu-ra a livelli:Le applicazioni utente vengono tra-dotte dai compilatori (interpreti etc..) in un linguaggio pi vicino alla macchi-na tuttavia di solito solo SO ad avere ilcontrollo completo del HW e non l'appli-cazione utente.Per fare ci il SO maschera parzialmente l'ISA (insieme delle istruzioni tipiche di ogni

  • processore) che viene cos diviso in dueparti: le istruzioni non critiche (es somma tra due numeri) vengono ese-guite direttamente mentre quelle pi complesse o pi delicate (es accesso periferica) vengono eseguite attraver-so SO.

    Questa struttura presenta grossi vantaggi in termini di: facilit ed efficienza: le istruzioni complesse e delicate sono gi risolte

    ed ottimizzate dai programmatori del SO

    protezione: processi che tentano di usare le istruzioni privilegiate (spessocon la finalit di eludere i controlli) vengono bloccate.

    Ad esempio nel caso che un'applicazione utente per accedere al disco lo chiede al sistema operativo tramite una Chiamata di Sistema (o Sy-stem Call, syscall da ora in poi), i l quale eseguir la lettura e restituir il dato all'applicazione (operazione molto delicata perch consiste nell'indivi-duazione della pozione fisica de dato sul disco, quindi numero di settore e traccia, conseguente posizionamento della testina, lettura del dato ecc... se ogni volta i l programmatore deve pensare a tutto questo...).

    Oltre all'interfaccia con l'HW il SO gestisce anche la convivenza tra pro-grammi diversi ed utenti diversi, infatti ogni processo deve poter ac-cedere alle risorse HW e SW (es stampante o sezione critica) senza col-lidere con gli altri.Nel caso che esistano utenti diversi, essi vanno identificati e autenticati,per assegnare a ciascuno gli opportuni diritti di accesso.

    In breve il SO fondamentalmente un:

    fornitore di servizi: "dimmi quello che ti serve e poi ci penso io". gestore delle risorse: come un vigile che controlla il traffico stra-dale (regola i passaggi agli incroci, assegna le multe ecc...).

    Concetto riassuntivo : Il SO un VIRTUALIZZATORE. Maschera la realt fi-sica facendo credere all'entit di livello superiore che ne esiste un'altra di-versa.

    FRUITORE DELLA VIRTUALIZZAZIONE VIRTUALIZZATORE REALT VIRTUALE REALT "VERA"

    Uomo LSD mondi fantastici reazioni chimiche(dannose) nel cervello

    Uomo Sistemi di realtvirtuale scenari 3D caschi, guanti, ...

    Uomo SO macchina dedicata macchina condivisa

    Processi SO CPU dedicata CPU condivisa

    Processi SOSpazio in memoriacontinuo e dedicato(memoria virtuale)

    memoria allocata apezzi ed in parte

    su disco

    Processi SO File dispositivi HW diversi

  • Grazie a questo ci si pu con-centrare su concetti astratti (processi, file) ignorando la pi complessa realt.La virtualizzazione pu essere operata ricorsivamente su pi li-velli (es SO virtualizza HW, JVM virtualizza a sua volta SO.... Vir-tualizzazione al cubo).

    Ci viene operato per esigenze diverse, ad esempio, permette di confinare le singole applicazioni in un ambienti protetti (con il conseguente incremento della sicurezza) o, nella maggioranza dei casi, per aumentare la portabilit delle stesse su sistemi HW e SW diversi.

    Lo stesso SO consente agli sviluppatori di realizzare le loro applicazioni secon-do solo le specifiche del SO e non dei diversi HW su quale vengono realmenteeseguite.

  • 2.1 - Caratteristiche del SOVediamo di stilare una rapida "Carta d'identit" di un SO:

    Nome: SO. Et: circa 50 anni. Sesso: SW (ma molto vicino al HW). Professione: virtualizzare. Pu essere a pi livelli dove la "realt

    vera" una virtuale generata dal livello inferiore (es Java Virtual Ma chine). Per separare utenti che condividono la stessa macchina pu creare diverse realt separate nelle quali pu fare tutto.

    Domicilio: sistemi di elaborazione. Segni particolari: dual mode.

    2.2 - ClassificazioneI SO possono essere suddivisi in categorie a seconda delle loro caratteristi-che/piattaforme per cui sono progettati, ad esempio: SO mono/multi-processore: Sono in base alla tipologia di CPU: Mono-

    core (es Intel Pentium) o multi-core (es Intel Duo, I7, I3 ecc) SO di rete: Possono connettersi ad una rete (es Internet) SO distribuiti: Sfruttando una rete di calcolatori, tramite la virtualiz-

    zazione, da l'impressione di avere un unico enorme calcolatore. SO cluster: Virtualizza un enorme sistema, costituito da molti processo-

    ri posto in una zona ben delimitata e circoscritta (Super-calcolatori, ser-ver di grandi dimensioni).

    SO embedded: SO dedicati ed molto ottimizzati per sistemi con po-che risorse di calcolo, generalmente dedicati ad applicazioni partico-lari (es lavatrici, PLC, automobili ecc...).

    Generalmente gli SO comuni (es Windows o Ubuntu) sono a multipro-cessore e di rete.

    2.3 - Il DUAL MODEI moderni processori sono Dual Mode, cio permettono o meno, l'esecuzione di certe istruzioni da parte dell'applicazione a seconda del modo impostato (tra-mite lo stato di un bit di un particolare registro).Ci permette di avere un certo controllo sia sul HW e sia sui processi che sono inesecuzione.Si distinguono perci due modi di funzionamento:

    modo Kernel: (o privilegiato o superuser), il calcolatore esegue qualsiasi tipo di istruzione, indica che st eseguendo istruzioni del SO.

    modo Utente: In questo modo girano le applicazioni. Se durante l'esecuzione viene richiesta l'esecuzione di codice privilegiato (istru-zioni alle quali sarebbe opportuno sostituire una syscall) viene generataun'eccezione ed subentra SO.

    Durante la vita di un calcolatore si continua a passare tra i due modi, ed il passaggio tra un modo e l'altro di appannaggio esclusivo del SO e tale fase prende il nome di mode switch.

  • Es di vita di un calcolatore:

    Durante un ciclo di vita (da quando viene acceso il calcolatore a quando vie-ne spento) esso passa tre frasi principali: Boostrap (dove viene preparato il sistema per l'utilizzo da parte dell'utente), si ha poi un funzionamento regola-re e una fase di Shutdown al termine (si salvano i dati non salvati e si pre-para il calcolatore alla prossima accensione).

    2.3.1 - BootstrapIl bootstrap la sequenza di accensione di un calcolatore. La CPU esegue solo le istruzioni che le vengono date, perci inizia dalla 0 che si trova su una ROM (Read Only Memory). Tale frammento di codice, che chia-mata BIOS (Basic Input Outpu System) serve per effettuare dei controlli HW e preparare il sistema a caricare SO.Esso si compone di diverse fasi; all'inzio viene effettuata una diagnostic, rilevamento e configurazione dell'HW (verifica e certificazione della pre-senza di RAM, della scheda video, audio ecc...), una successiva inizializza-zione dell'HW (lettura del SO del dispositivo, firmware) e la preparazione delle routine di interrupt.Fino ad adesso non stato ancora caricato e ne eseguito una singola riga delSO. Se questa fase fallisce, il dispositivo emette segnali audio o video e si blocca in attesa di riparazioni (es mancanza della tastiera).

    A questo punto viene identificato il dispositivo di Boot (es CD, DVD, USB, HDD ecc) dal quale viene letta una locazione fissata dello stesso.Questa locazione contiene un programmino di boot (bootloader),il quale consente all'utente di scegliere (se necessario) il SO da caricare e la posizione sul disco fisica dove esso contenuto (l'indirizzo da cui partire per caricare SO).Per fare ci deve quindi contenere anche informazioni sulle partizioni del di-sco.Il caricamento del SO (compia del suo codice dal dispositivo di memorizza-zione alla memoria centrale, RAM) detto a valanga, ed in questa fase che verranno create tutte le strutture per il corrretto funzionamento dello stesso (es struttura per la sessione utente con i permessi sui vari oggetti delSO).In questa fase verranno anche avviati i processi di sistema e quelli uten-te utili (es: login&password).

    Se il processo di Boot si blocca in questa fase, SO parte in modalit prov-visoria (un boot semplificato) che carica meno cose possibili (per es. invecedel driver di accelerazione grafica avvier il driver grafico base per tutte le schede) sperando di escludere il programma che ha generato l'errore; si consegner all'utente una macchina brutta ma funzionale, allo scopo di cor-reggere gli errori.

    2.3.2 - ShutdownProcedura d i spegnimento, in cui SO salva le informazioni sul suo stato e termina ordinatamente i processi per evitare la perdita di dati e salvaguarda-re l'integrit della macchina.

  • 2.3.3 - Funzionamento Normale

    Nella figura a sinistra viene rappresentato il normale ciclo di esecuzione di un programmasu un calcolatore.Come mostrato, esistono parti-colari segnali che possono in-terrompere l'esecuzione del programma, detti generalmenteinterrupt, che forzano il SOad intervenire (con un conse-guente mode switch).

    Essi si dividono in circa 3 categorie d i eventi che possono causare un mode switch:

    INTERRUPT: Eventi esterni all'esecuzione dell'istruzione corrente (eventi asincroni all'esecuzione del programma). Possono essere "cattivi" (eventi indesiderati come errori HW, segnale di restart etc...)oppure "Buoni" (eventi compresi nel normale funzionamento come il clock, il segnale di I/O completato, page fault etc...)

    TRAP: (anche dette eccezioni), eventi legati all'istruzione corrente (sin-croni all'esecuzione del programma). Sono sempre "Cattivi" (divisio-ne per zero, codice errato, violazione della memoria altrui, ten-tativo di esecuzione di istruzione privilegiata in modo utente ecc...).

    SUPERVISOR CALL: Chiamata di sistema, o syscall, in cui l'istruzione chiede un servizio al SO (detti anche interrupt software, sincroni).

    Quando avviene uno di questi eventi vengono eseguite alcune azioni HW:1) Viene rilevato l'evento.2) Viene identificato l'evento (operazione immediata nel caso di trap o sy-

    scall viceversa nel caso interrupt). Nel caso degli interrupt esterni la rilevazione pu avvenire in due modi: Polling: La CPU rileva il segnale di interrupt e passa tutte le pe-

    riferiche in ricerca di quella che ha generato l'evento (molto inefficiente).

    Interrupt Vettorizzati: La periferica stessa creando il segnale di interrupt fornisce anche il suo identificativo.

    3) Vengono salvati i registri di esecuzione, ad esempio: il Program Coun-ter (PC) e il Program Status Word (PSW).

    4) Dopodich viene identificato il nuovo valore del Program Coun-ter e viene cambiato il modo (mode switch e program switch). In questa fase si sceglie anche il codice da eseguire in risposta all'inter-rupt e ci pu essere fatto in due modi: si esegue sempre ad un'unica istruzione e da li si salta a quella specifica per l'interrupt (sempre all'interno del codice del SO), oppure, se nel caso dei vettorizzati, si as-socia l'identificativo della periferica (o dell'interrupt) ad un punto speci-fico del codice del SO.

    5) A questo punto si effettuano dei salvataggi ulteriori dei registri (quelli del SO che verranno sovrascritti). Qui si ha la fine del mode switch.

  • 6) Ora si avr la risposta specifica all'evento che ha generato l'interrupt. Qui si ha la fine del mode kernel.

    7) Quando sono finite tutte le operazioni richieste dall'interrupt si ha il ri-pristino di tutti i registri, il cambio di modo e la ripresa della normale esecuzione dei programmi (pu essere diverso da quello che c'era in precedenza, ad esempio se quest'ultimo ha fatto una richiesta di dati dal disco che ha bisogno di molto tempo per essere completata).

    ESEMPI: Interrupt dall'orologio di sistema --> viene aggiornato i l contatore -->

    torno a fare quello che facevo prima. System call --> richiesta di acesso al disco --> intanto che aspetto

    faccio continuare un'altro processo

    2.3.4 - Process SwitchUn process switch l'insieme de l le operazioni necessarie per il cambio del processo da eseguire tornando in modo utente.Lo schema di vita del calcolatore diventa:

    Il process switch salva tutto quello che riguarda P1, carica tutto ci che riguarda P2 (Si ha sempre l'intervento del SO e quindi anche un cambio di modo).Un mode switch deve comunque avvenire ogni decina di microsecondi,di conseguenza deve durare poco per non incidere eccessivamente sulle prestazioni della macchina.

    2.3.5 - Gestione degli interruptSi detto che sia in un mode switch che i n un process switch av-viene il salvataggio delle informazioni relative all'istruzione corrente, tutta-via nei calcolatori moderni il concetto di istruzione corrente potrebbe es-sere difficilmente determinabile dato che il calcolatore non esegue una sola istruzione alla volta, ma le esegue come in una catena di montaggio (pipline).Si hanno due tipi di microprocessori progettati per:

    interrupt precisi: il microprocessore a risolvere tutte le istruzio-ni accavallate e ad individuare l'istruzione corrente.

    interrupt imprecisi: il microprocessore passa tutto i l suo stato alSO al quale resta l'onere dei calcoli per individuare l'istruzione corren-te.

    La seconda configurazione spesso non v a bene per applicazioni real time (dove la durata del mode switch cruciale).Solitamente il sistema in grado di gestire interrupt nidificati, ovvero in-terrupt durante la gestione d i un altro evento (il sistema gi in modo ker-nel).

  • In tale caso il codice del SO deve essere rientrante (il SO ha la capacit di interrompere se stesso e ricaricare lo stesso codice senza errori).Per evitare che il sistema non progredisca (non esegua i programmi) a causadei troppi interrupt che genera una periferica, o peggio, non elabora un in-terrupt critico (es allarme della temperatura HW), con l'idea anche di ridurre il numero di mode switch e process switch, per aumentare l'efficienza del si-stema si assegnano dei livelli di priorit agli stessi e si lavora ad interrupt disabilitati.In questo modo un interrupt critico non verr interrotto da uno a priorit mi-nore, anche se arriva dopo, e quelli critici potranno essere elaborati tutti in una volta solo al process switch naturale del programma (vedere gestione dei processi), quando deve comunque il SO.Inoltre permette di gestire meglio periferiche che generano molti interrupt (es scheda di rete o HDD), in quanto la prima richiesta ad media priorit e attiva l'HW corrispondente e le successive verranno gestite direttamente dall'HW dedicato, liberando la CPU da compiti inutili come il trasferimento di dati.

    Nei sistemi Linux le routine di in-terrupt sono separate in:

    Top half:parte urgente Bottom half:parte meno ur-

    gente.

    Nei sistemi Windows: Routine di intterupt: par-

    te urgente. DPC/APC: (Deferred Procedu-

    re Call/Asincronous Procedu-re Call) parti meno urgenti. Le APC sono pi legate al pro-cesso.

    2.4 - Architetture dei SOOgni SO gestisce il rapporto dei modi User-Kernel in modo diverso in funzione della sua architettura interna:

    Kernel separato La memoria dei processi fisicamente un'altra arearispetto a quella del SO.

    Funzioni SO eseguite come funzioni di processo: Ogni proces-so vede il SO come parte di s non devo cambiare nulla nella gestione della memoria.

    Funzioni SO eseguite come processo kernel: Non esistono mode switch sono tutti process switch.

    All'inizio tutti i SO erano software quasi senza struttura, scritti completa-mente in assembler, cosa che ne impediva completamente la portabilit su architetture diverse.

  • Succcessivamnte si cercato di implementare un modello a li-velli, che per risultato impos-sibile da realizzare causa delle troppe interazione fra le varie parti.Uno schema di principio potreb-be essere quello mostrato in fi-gura.Una delle parti pi critiche e com-plesse di un SO il livello kernel, infatti esso si occupa principalmen-te della gestione tra i processi e HW ed esso pu essere costruito principalmente in tre modi:

    kernel monolitico:Tutto il codice del kernel (cuore) del SO in unico blocco. Le limitazioni pricipali di questo modello sono: tutto eseguito in modalit Kernel, quindi c' un alto rischio che il sistema si blocchi in caso di errore; lo sviluppo e l'estensione del progetto sono scarsamente fa-cilitati dato che, per apportare anche una piccola modifica, si deve mettere mano a migliaia di righe di codice; inoltre quando si accende la macchina si carica sempre tutto anche quello che non serve con unforte spreco d i tempo e risorse.

    kernel modulari (Linux): Dove una parte resta monolitica ma la maggior parte delle funzionalit racchiusa in moduli che possono essere aggiunti o rimossi (anche a macchina accesa per es i dri-ver).

    Microkernel: Prevede un piccolissimo (micro) kernel che contiene le funzioni di controllo dell'HW e di gestione degli interrupt, mentre tut-to il resto (es gestione del disco ) si appoggiano sul microkernel come processi eseguiti in modo utente.

    I vantaggi di una struttura a microkernel sono notevoli: La parte d i microkernel minima quindi minima la parte di codi-

    ce che gira in modo privilegiato, la possibilit ches succedano cose irreversibili minima.

    Esiste un potenziale incremento di stabilit grazie all'elevato modularit (se si pianta un modulo riavvio quello non tutta la mac-china; la modularit completa riesco a caricare esattamente ci che mi serve in quel momento.

    Il prezzo da pagare per tutto ci per non trascurabile: sono molto pi lenti a causa dei notevoli process e mode switch che ogni azione SO com-porta. Prendiamo il caso di dover gestire un accesso al disco: l'applicazione fa una syscall al SO per la lettura nel File System; la sequenza delle operazio-ni :

    Mode switch in modo kernel process switch per il modulo SO del File System, mode switch per l'esecuzione del codice del SO.

  • Ponendo il caso che riparta lo stesso pro-cesso di prima, alla fine della lettura si avr lo stesso procedimento in verso opposto.Totale alla fine dell'operazione: 4 mode swit-ch e 2 process switch contro i 2 mode switchdel caso in cui il SO gira in modo kernel mo-nolitico/modulare.

    2.4.1 - Esempio: Microsoft Windows NTPartiamo con un po' di storia, l'avventura Microsoft nei sistemi operativi parte nel 1981 con MS-DOS, da quel momento in poi si vista la nascita di una lunga serie di SO Microsoft. Considerando la moderna teoria dei SO, l' MS-DOS, win95, win98, winME non possono essere definiti SO a pieno titolo inquanto (sopratutto per retrocompatibilit con DOS) non supportano a pieno ildual mode e i meccanismi di virtualizzazione dell'HW. MS-DOS si lasciava sca-valcare tranquillamente dalle applicazioni utente nel controllo dell'HW per motivi di compatibilit con processori non dual mode.

    La svolta si ebbe con i sistemi della serie Windows NT (New Tecnology) dellaquale fanno parte tutti i si sistemi MS moderni (winNT, win2000, winXP, winVi-sta).NT nacque con l'obiettivo d i essere portabile su architetture diverse, ven-ne introdotto HAL, l'Hardware Abstraction Layer, i l livello che si appoggia di-rettamente al HW ed perci scritto in assembly ed specifico della macchi-na(I/O,memorie, timer, gestione delle informazioni contenute nel BIOS...).

    Il livello kernel corrisponderebbe al microkernel, infatti il progetto iniziale pre-vedeva una struttura di questo tipo ma le prestazioni misurate nei test spin-sero per una struttura ibrida.Lo schema rimasto a microkernel ma il funzionamento no, tutto il SO gira comunque in modo kernel per aumentarne le prestazioni (vengono tolti i Pro-cess switch in eccesso).Interessanti risultano alcune caratteristiche del livello executive che con-tiene i vari moduli del SO. Si pensato di applicare la filosofia ad oggetti nel programmare il SO nonostante il linguaggio utilizzato non supporti la OOP

  • (Objec Oriented Programming).Il blocco WIN32 GDI il modulo che gestisce l'interfaccia grafica, compito che concettualmente esula dal SO. La particolarit sta nel fatto che, nono-stante sia la parte pi voluminosa come numero di righe di codice ( di con-seguenza la pi sensibile ad errori), gira in modo kernel e scavalca HAL appoggiandosi direttamente all'HW. Questo permette un grosso aumento delle prestazioni a scapito di un aumento dell'instabilit del sistema (se si pianta una finestra, evento tra i pi probabili per la dimensione e le caratte-ristiche del codice, si pianta il kernel); inoltre devo per forza caricare tutta la grafica anche se non mi serve.

    SYSTEM SERVICES il nome di Microsoft delle System Call, questo livello non documentato, invece documentata la SYSTEM INTERFACE che fornisce all'utente le librerie (DLL, Dynamic Link Library, collettivamente chiamate WIN32 API, Application Process Interface) necessarie per implementare le chiamate di sistema.Un altro obiettivo del progetto iniziale, oltre alla portabilit del SO su architet-ture HW diverse, la portabilit d i applicazioni scritte per altri SO (UNIX, OS/2 in particolare), implementate con dei Subsystem i n grado di ricevere le chiamate da applicazioni UNIX e OS/2 e convertirle in chiamate WIN32. In questo modo una chiamata diventa un processo molto articolato.

    Per motivi di velocit i subsystem sono stati eliminati, per motivi economici sono stati eliminati gli HAL multipiattaforma (il livello logico invece rimasto). La compatibilit DOS stata invece eliminata per ragioni di sicurezza; statamantenuta un'emulazione DOS per i vecchi software scritti in modo migliore rispetto agli standard dell'epoca.

    Il simulatore in Windows crea un processo chiamato NTVDM-NT Virtual Dos Machine che controlla il processo DOS. Finch il programma fa chiamate con-sentite esse vengono passate direttamente al SO, appena viene effettua-ta una chiamata privilegiata essa viene intercettata e inviata alla NTVDM che, se possibile, traduce la chiamata privilegiata nella corretta Syscall di Windows altrimenti genera un eccezione (trap) e termina il programma DOS.

  • 3 - Input/OutputPassiamo ora ad analizzare come un SO gestisce le richieste di I/O.Come si pu notare dalla figura esiste una chiara architettura a strati. Ogni processo utente si appoggia ad un livello (I/O livello utente) che contiene lefunzioni di libreria per effettuare le syscall e l'implementazione di servi-zi di supporto come lo spooling (Simultaneous Periferical Operation On Line):

    ES: Eseguo la stampa d i un file di 200 pagine, una volta dato il coman-do di stampa esso termina prima della fine della stampa delle 200 pagine. Un altro processo comunica con la stampante mentre l'utente pu continua-re a fare altro.

    Appena fuori dal modo utente, troviamo il primo strato eseguito in modo kernel, del SW indipendente dal dispositivo.Questo livello implementa un'interfaccia uniforme con i driver (SW fornito dal progetti-sta del HW), permette la denominazione deidispositivi e ne gestisce la protezione (per-messi), implementa le necessarie allocazionidi memoria e permette l'allocazione dei di-spositivi dedicati.I dispositivi dedicati sono particolari peri-feriche che possono fare una cosa per volta (la stampante, per esempio, bene che stampi un documento alla volta), il sistema, quindi, gestisce una tabella di stato dei di-spositivi dove controlla se il determinato dispositivo libero (quindi lo occupa e ese-gue l'operazione richiesta) o occupato (quindi genera una coda delle richieste).Il trasferimento dei dati da/per la periferica av-viene in unit minime che possono essere ca-ratteri nel caso della tastiera o blocchi nel caso delle stampati o del HDD.Esiste perci il problema di archiviare i dati in transito e ci viene effettuato tramite il Bufe-ring (implementato in questo livello).

    Esso consiste in una memoria condivisa tra produttore e consumatore del dato.Il produttore (es la scheda di rete o la tastiera), invece di inviare le informa-zioni immediatamente al programma utente le invia al sistema operativo i lquale le memorizza in un bufer aspettando che i l programma utente sia pronto per processarle.

    Se i buffer sono pi di uno il produttore pu permettersi d i produrre dati

  • mentre il consumatore (l'applicazione che ne ha fatto richiesta) sta anco-ra leggendo i dati vecchi. Questo fa si che le operazioni d i lettura/scrittu-ra possano essere molto ottimizzate come mostra il prossimo grafico:

    In questo modo si slega produttore e consumatore e ci si traduce nel avanza-mento dell'applicazione, dopo aver dato il comando di stampa, senza che essa sia stata realmente effettuata.

    3.1 I /O con l 'utentePer I/O con l'utente si intendono quelle operazioni che la macchina svolge per ricevere istruzioni dall'utente umano.Usualmente si distinguono 2 contesti:

    Tastiera con video alfanumerico (CLI) Grafica (GUI)

    3.1.1 Tastiera con video alfanumericoMetodo di I/O con l'utente pi tradizionale. Prevede l'utilizzo della sola ta-stiera e di un'interfaccia alfanumerica (come il terminale di Linux o del MS-DOS, senza finestre o altri artifici grafici).Questo metodo pu essere collegato al software in due modi:

    RAW (letteralmente Crudo): tutti i tasti premuti vengono passati, uno alla volta, direttamente all'applicazione, la quale avr l'onere di tra-durli nei rispettivi caratteri/comandi.

    COOKED (letteralmente cotto, cucinato): viene demandato al SO il compito di effettuare l'editing di linea (traduzione e formattazione) solodopo il SO invia la linea all'applicazione.

    3.1.2 - GraficaQuesto metodo si basa sulla presenza di oggetti grafici con i quali l'utente interagi-sce.L'applicazione utente chiede al SO se avve-nuto qualche evento (un click del mouse per esempio), il SO, come risposta passa all'appli-cazione un messaggio che definisce l'azionesvolta dall'utente.Con la chiamata l'applicazione utente viene interrotta e riprende-r solo dopo che il SO abbia rispo-sto con l'apposito messaggio.Con la callback il SO invia il mes-saggio ma l'applicazione non ripren-de da dove era stata interrotta, ma dove punta la callback, cio a quella parte di codice scritto per ge-stire il determinato evento.

  • Esistono vari approcci per la gestione di questo meccanismo, analizziamo come esempio l'approccio UN1X (pi flessibile e logico di altri).L'X Terminal (ci che si vede a video) intercetta gli input dell'utente e li in-via al Server X, i l quale gestisce la grafica e chiama le componenti del SO adatte per eseguire la risposta all'evento generato dall'utente.

    L'X server un'applicazione che gira in modo utente, questa soluzione hatutti i vantaggi della modularit (se non mi serve non lo carico, se si piantasi blocca solo l'applicazione utente e non l'intero sistema).

    4 - ProcessiUno degli obiettivi di un SO fare in modo che i programmi diventino pro-cessi, cio diventino un'entit ben definita e fisica che evolve in un calco-latore.Ma come nascono i processi?Tutti i processi attivi derivano da un processo padre, il quale li crea (il pri-mo creato in fase di Boot a seguito del Bootstrap), tuttavia possibile di-stinguere due meccanismi che determinano la creazione di un nuovo pro-cesso:

    Per un comando dell'utente (es: avvio un programma). Generati direttamente dal SO (es: servizi di sistema).

    In generale possibile descrivere la genealogia dei processi con una struttura ad albero:

    La situazione opposta, cio la morte di un processo, di solito avviene per suici-dio, dove il processo comunica al SO: ho finito il mio compito quindi elimi-nami".Esistono, per, altri due casi in cui un processo pu morire:

    per incidente a seguito di una trap uccisi (kill) dall'operatore o dal SO (es nel caso di attesa infinita o

    quando c' esigenza di liberare risorse) o per la morte del processo pa-dre (non possono esistere senza il padre, quando si elimina un proces-

  • so si eliminano anche tutti quelli a lui riconducibili).

    4.1-Ciclo di vita di un ProcessoUna syscall ( o il bootstrap) fa nascere un nuovo processo. A seconda dell'algoritmo di scheduling a lungo termine il passaggio allo stato ready (pronto per essere eseguito) pu essere ritardato o meno (so-prattutto nel caso che le risorse siano limitate).Una volta che il processo si trova nello stato ready dopo un certo lasso ditempo (nell'ordine dei ms), determinato dell'algoritmo di scheduling a bre-ve termine, passer nello stato running e i l suo codice comincer ad essere eseguito.

    Ora se il processo viene eseguito completamente passa nello stato termi-nated e cos muore, se invece, deve effettuare un'operazione d i I / O o aspettare un evento passer nello stato blocked, oppure, se semplice-mente scade i l suo quanto d i tempo ritorna nello stato ready e aspetta che g l i venga riassegnata la CPU.Nello stato blocked il processo aspetta fino alla fine dell'operazione di I/Oo fino al verificarsi d i un determinato evento ( es: click del mouse) dopodi-ch si rimette nella coda dei processi pronti.Seguendo un determinato algoritmo, detto di scheduling a medio termi-ne, viene deciso quale processo abbia o meno il diritto di restare in memo-ria centrale o quale debba essere momentaneamente spostato su disco perfar spazio in RAM ad altri (se necessario).Quando un processo viene eletto per essere messo su disco (sospeso) si passa dagli stati rispettivamente ready e blocked ai corrispettivi su-spended ready e suspended blocked. Immaginiamo un processo che mentre viene eseguito faccia una richiesta

  • di I/O, automaticamente passa nello stato blocked e cede il controllo della CPU ad un altro. Ad un certo punto viene eletto per la sospensione, vi-sto che l'operazione di I/O non ancora finita, lo scheduler lo mette nello stato suspended blocked.Ora se l'operazione di I/O termina prima che i l processo venga rimesso in memoria centrale, esso torner pronto ma sospeso (suspended rea-dy), se invece viene prima rimesso in RAM (resume) torner blocked fino alla fine dell'operazione di I/O.Lo schema di vita di un processo nei sistemi UNIX ha la stessa forma genera-le ma introduce una particolarit che vale la pena di osservare nello sche-ma seguente:

    Presenta gli stessi elementi fondamentali del ciclo di vita classico, la parti-colarit si trova nello sdoppiamento dello stato ready in due code: una peri processi utente ed una per i processi del super-utente con priorit maggiore.

    4.2 - Struttura in memoria di un ProcessoVediamo ora come un processo strutturato in memoria.L'area di memoria assegnata ad un processo suddivisain 4 aree:

    User program o campo Text: contiene il codice eseguibile caricato in memoria, campo non modificabi-le. User data: area che contiene i dati del proces-so. Area modificabile quindi la sua dimensione po-trebbe variare nel tempo. Stack: pila delle chiamate di procedura fatte dal programma, struttura dati dinamica a volte supporta-ta da apposito HW.

  • 4.2.1 - Process Control Block (PCB)Process Control Block ( PCB): contiene una serie d i informazioni per la gestione del processo:

    Identificazione del processo: quando il processo nasce gli viene dato un numero chiamato PID (Process ID), vengono inoltre memorizza-ti il PID del processo padre e l'UID (User ID) dell'utente proprietario del processo.

    Stato del Processore: l'insieme di dati che vengono salvati durante un process switch (registri come il program counter ecc...)

    Process control info: questo campo del P C B contiene le informazio-ni sullo scheduling di breve termine (es lo stato del processo, gli eventi attesi, le priorit...); contiene informazioni sui privilegi del pro-cesso (sistema di protezione, cosa i l processo autorizzato a fare oppure no); contiene informazioni/puntatori sulle risorse assegnate (strutture dati per sapere quale memoria ha il processo, se sta su di-sco, i file aperti, strutture dati per la comunicazione come gli IPC: In-ter Process Communication, puntatori veri e propri ad altri proces-si/PCB).

    4.3 - ThreadOgni processo pu avere assegnati pi flussi di controllo, cio programmini separati che condividono lo stesso codice eseguibile (sia tra di loro che con il processo) e servono per una migliore gestione dello stesso.Per capire meglio, iniziamo questo paragrafo, con due esempi di struttura di un moderno programma:

    Text Editor: Il codice di un applicazione di text editing (MS- Word o OOWriter per esem-pio) in realt un insieme di funzionalit di-verse a volte separate l'una dalle altre. Per esempio il codice che gestisce l'interazione con l'utente separato dall'impaginatore che separato dal controllo automatico dell'ortografia.

    Sono flussi di esecuzione diversi che: lavorano sugli stessi dati fanno parte dello stesso programma hanno priorit diverse

    Web Server: Il funzionamento di un web server ha un problema generale, il server non fa altro che aspettare l'arrivo delle richieste dalla rete, e una volta ricevute, le legge, le elabora (es cerca sul disco le informazioni richie-ste) e le restituisce. Il problema nasce dal fatto che un solo processo (web server) potrebbe avere molte richieste contemporaneamente, e ogni richiesta genera un flusso di controllo separato.

    Se questi flussi d i controllo venissero eseguiti uno alla volta le prestazionidi questi sistemi sarebbero inaccettabili per la commercializzazione (ad esempio, nel caso dell'impaginatore del Text editor, se nell'aggiungere 20 righenella prima pagina di un documento, per ogni carattere digitato si dovesseaspettare che tutto i l documento venga rii-impaginato, l'uso dell'applica-zione sarebbe estremamente lento e laborioso).

  • La soluzione viene dallo spezzamento della gestione delle richieste i n User Space, consistenti in pi fussi separati chiamati Thread chelavorano in "Parallelo", sfruttando i tempi morti dell'esecuzione.

    Esistono due tipi di Thread: User Level Thread (ULT): sono librerie/VM che permettono di imple-

    mentare la gestione dei flussi di esecuzione (alternanza tra i flussi, scheduling, comunicazione/sincronizzazione tra threads), anche su SO a processi tradizionali. I thread esistono solo in user mode, quindi il SO non sa che esistono e perci non li distingue.

    Kernel Level Treaeds (KLT): i l SO che crea thread a l ive l lo kernel, quindi chi cambia stato (ready, blocked ecc...) non pi il pro-cesso intero ma il thread. In questo modo se si blocca il thread non si impianta pi l'intero processo. Creare questi threads costa un po' di pidi quelli user, perch si deve fare un syscall e deve intervenire il SO, tuttavia costa sicuramente meno che creare un nuovo processo.

    Generalmente, si ha la corrispondenza 1:1 tra User e Kernel Thread (caso P1),tuttavia possibile assegnare pi User a un solo Kernel Thread (caso P2) o vi-ceversa (meno comune).

    4.3.1 - Ciclo di vita di un ULTLo stato Runnable il corrispondente dello stato ready per i processi. Si entra nello stato stopped a seguito di un comando utente o di un altrothread (es: finestra minimizzata), mentre si entra nello stato sleeping con uno stop volontario (usualmente quando si si sta aspettando qualcosada un altro thread). Nello stato active l'ULT viene associato ad un KLT ottenendo quindi la

  • potenzialit di essere eseguito se lo il KLT associato ( i l quale pu essere a sua volta in qualunque dei sui stati.

    4.3.2 - Stati di un KLTGli stati di un KLT sono simili a quelli di un ULT con la differenza che ora viene eseguito se si trova nello stato running.

    Da notare che il passaggio tra stati dei thread avviene solo se il processo in esecuzione tuttavia certe operazioni riguardanti le risorse ( come lo Swap su disco) vengono eseguite sull'intero processo mentre il flusso di controllo (sta-ti del processo attivo o scheduling d i breve termine) viene gestito per th-read. Il numero di thread lo decide il programmatore.

  • 4.4 - Generazione di processi figli in UNIXIn UNIX la syscall per generare un processo figlio fork(); con essa il padre crea un doppione di se tramite il meccanismo di copy on write.Dopo la fork() padre e figlio sono identici (tanto da essere nello stesso punto di esecuzione); per distinguerli si fa riferimento al valore ritornato dalla fork(). La chiamata sar quindi: var=fork().A questo punto si ha una struttura (nel codice) del tipo:

    if (fork()==0)then caso padreelse caso figlio

    Il meccanismo copy on write prevede che il figlio non sia altro che un pun-tatore al codice del padre fino al momento in cui non viene effettuata una modifica (per esempio il byte 1000), a quel punto copio solo un intorno del padre a cavallo del byte 1000 e lo modifico (nella copia che fa parte del pro-cesso figlio).A questo punto creo un meccanismo tale che il processo figlio uguale al padre dal byte 0 al byte 1000, modifico il 1000 e dal byte 1001 fino alla fine ancora uguale al padre.

    Esiste una libreria standard in UNIX per creare i thread: pthread. Essa, per, non distingue tra ULT e KLT, quindi per ovviare alla limitazione in Linux hanno creato clone(), la quale sostanzialmente una fork() che ag-giunge al figlio informazioni sulla memoria, sui file aperti e sulle comunicazionidi inter-processo (IPC Inter Process Communication).Gli UTL vengono anche chiamati Fiber, mentre il termine Thread indica solo i KLT.Con process si indica l'unit di assegnazione delle risorse (contenitore di thread) mentre con Job si indica un insieme di processi.

    4.5 - Scheduling (a breve termine)Lo scheduling la modalit di individuazione del processo (o thread di li-vello kernel) che abbia diritto di prendere il controllo della CPU e per quan-to tempo mantenerlo. un concetto fondamentale per le prestazioni di un calcolatore.

    Possiamo dividere il tempo di progressione di un processo in due fasi:

    CPU Burst: tempo utilizzato per fare calcoli sulla CPU: I/O Burst: tempo utilizzato per operazioni di I/O.

    Nel caso che esiste un solo processo (tipico dei sistemi embedded) la vita diun calcolatore potrebbe essere schematizzata come una sequenza alterna-ta di CPU Burst e I/O Burst.In base a questo possibile suddividere i processi in due categorie:

    Processi CPU Bound: Sono processi legati alla CPU, passano la mag-gior parte della loro vita a caricare di calcoli la CPU. Le operazioni di I/O sono ridotte al minimo (es codice Matlab/Octave)

    Processi I/O Bound: Sono processi interattivi, passano la maggior par-te della loro vita ad aspettare operazioni di I/O. I calcoli richiesti alla CPU sono minimi.

    L'obiettivo di un algoritmo di scheduling l'ottimizzazione della ge-

  • stione dei processi e delle risorse HW messe a disosizione. Per confrontare gli algoritmi che venivano sviluppati, si sono cercati dei parame-tri suddivisi in categorie considerando i diversi ambienti in cui il sistema viene ad operare, quindi considerando il punto di vista dell'utente (user) o del siste-ma (system), in base all'ottimizzazione delle performance o altro (other).Ecco i criteri raggruppati secondo questa classificazione:

    User/Performance: analizza il sistema dal punto di vista delle prestazioni avvertite dall'utente. R e s po n s e T i m e : Tempo di risposta percepito dall'utente. Applicabile ai

    sistemi spiccatamente interattivi, tuttavia fornisce un' indicazionetroppo grossolana.

    T o u r n a ro u n d T i m e : Misura i l tempo di completamento di ciascun processo. Solitamente normalizzato alla dimensione del compito dasvolgere; inadeguato per i processi interattivi nei quali il tempo di completamento dipende pesantemente dall'utente.

    Scandenze e p r i o r i t u t e n te: esistono utenti pi importanti di altri (es amministratore) e processi pi critici di altri (es antivirus).

    User/Other: Analizza aspetti importanti dal punto di vista dell'utente. P r e dict a bilit y : Predicibilit. Indica "pi o meno il tempo di esecuzione"

    al posto del tempo assoluto, infatti pi utile sapere che un proces-so impiega sempre mezz'ora, al posto di, una volta finisce in 10 minu-ti, la volta dopo in un'ora.

    System/Performance: Analizza il valore delle prestazioni dal punto divista della velocit reale del sistema. T h ro u g h tp u t: Numero di processi completati per unit di tempo. U tili z z o dell CP U : ottenere un uso della CPU vicino al 100%.

    System/Other: analizza altri aspetti importanti per l'ottimo funziona-mento del sistema. Eq u it : Gestione paritaria dei processi (no starvation, morte di fame) P r i o r i t Inter n e : U s o delle R i s or s e: Inteso come bilanciamento complessivo e non solo

    CPU, quindi anche RAM, GPU, HDD ecc...

    4.5.1 - AlgoritmiFIFO/FCFS (First In First Out/First Come First Served)

    Questo algoritmo prevede che i l primo che arriva occupa la CPU per tutto il tempo necessario alle sue esigenze, e se nel frattempo arriva qualcun altro, non importa chi sia e che priorit abbia, aspetta pazientemente i l suo tur-no. un algoritmo senza prelazione cio chi prende il controllo della CPU non pu essere costretto a rilasciarla; pu solo farlo spontaneamente.

    Esempio:

  • Come si pu notare la percentuale di uso della CPU non ottimale (ci sono dei momenti in cui non fa nulla), inoltre P2 (principalmente I/O Bound) vie-ne spesso scavalcato da P1 dando una sensazione di lentezza all'utente. In generale il FIFO abbatte tutti i parametri presi in considerazione ed inoltre hail grosso svantaggio che se un programma si blocca, si incioda l'intero siste-ma senza possibilit di recupero (senza spegnerlo), tuttavia esiste un solo caso in cui l'algoritmo FIFO risulta ragionevole: quando tutti i processi sono CPU Bound:

    Round Robin con Time Slice

    Il principio base il FIFO, ma la CPU viene assegnata ad ogni processo, a tur-no, dopo lo scadere di un quanto di tempo. Questo un algoritmo con pre-lazione.

    Esempio:

    Migliora la percentuale di uso della CPU migliore rispetto al FIFO puro, inol-tre i processi interattivi aspettano, in media, meno rispetto a prima, e i CPUBound non vengono penalizzati t roppo .In questo caso diventa per fondamentale la durata del quanto di tempo: se troppo lungo cado nel caso FIFO, se troppo corto la percentuale di tempo usata per i Process switch ed i mode switch aumenta (tempo spreca-to perch non faccio calcoli utili, ma solo spostamenti di registri).Il problema nasce nel caso di processi molto interattivi (es servizio di mes-saggistica istantanea), dato che il tempo complessivo speso in coda sar molto maggiore di quello in cui processo viene eseguito, quindi, per ristabilireun po' di equit tra i processi, si sono introdotti meccanismi a priorit (sia esterne, assegnate dall'utente, che interne).Le priorit possono essere:

    Non Preemptive ( senza prelazione). Il processo pi prioritario, quan-do torna pronto, viene messo in testa alla coda ma non interrompe il processo in esecuzione.

    Preeemptive: ( con Prelazione). Il processo pi prioritario ruba la CPU a quello che sta girando.

    Shortest Job FirstImmaginiamo un bambino con un pacchetto di caramelle dietro a un signorecon il carrello pieno, entrambi in coda alla cassa di un negozio, usualmente,

  • per gentilezza, il bambino viene lasciato passare.Questo algoritmo tenta di fare la stessa cosa: il processo che occupa meno la CPU viene fatto passare prima di un altro.

    Esistono due possibili versioni:

    Senza prelazione (SPN) Con Prelazione (SPT)

    Il problema interpretare la frase "ha meno da fare" visto che in un norma-le funzionamento impossibile stabilire a priori, a meno di conoscere il futu-ro, per quanto tempo i processi in coda devono usare la CPU singolarmente.Nella realt si usa un algoritmo per stimare il tempo del prossimo CPU Burst in base all'utilizzo attuale e pregresso della CPU:

    Tale formula ricorsiva quindi espandendo e facendo i calcoli si ottenono una serie di esponenziali. Nella formula compare un coefficiente alfa, che pu assumere valori tra 0 e 1, il quale indica il peso dato al presente (misurato) e al passato (stima precedente).Di solito ha valori vicino a 0,9, infatti le precedenti stime sono meno importan-ti di quella attuale (il comportamento dinamico di un processo pu variare molto nel tempo).Il problema nasce quando si hanno troppi processi molto interattivi, perch si rischia la starvation (morte di fame), per quelli CPU Bound, perch non pro-grediscono mai (si pu verificare anche con le priorit per i processi meno prioritari).

    Per evitare ci si introduce il concetto di Aging (letteralmente "invecchia-mento", nonnismo): dopo un po' che un processo viene scavalcato, esso vie-ne promosso e posto in esecuzione. Il SO premia i processi che hanno aspet-tato e li fa avanzare anche se hanno un CPU Brust alto.

    Tale meccanismo implementato con l'HRRN (Higest Response Ratio Next), il quale fornisce il livello di priorit di ogni processo:

    HRRN=W+SS

    dove W il tempo passato in coda ed S il prossimo CPU Brust stimato.

    Finora abbiamo ragionato con una coda sola ma in realt le code sono pi di una, e nel caso dell'utilizzo di priorit, solitamente si assegna ad ogni coda un livello e si utilizzano sistemi a prelazione per assegnare il proces-so alla giusta coda (in combinazione con l'aging per evitare starvation).

    Inizialmente tutti i processi sono nella primacoda ed passeranno nelle altre se utilizzano tutto il loro quanto di tempo. possibile assegnare ad ogni coda un valo-re di quanto di tempo diverso (solitamente si incrementa all'aumentare dei livelli).Nel caso che un processo venga promosso alla coda pi prioritaria, tramite il meccani-smo di aging, il quando di tempo assegnato-gli rimane invariato.

  • Esistono diversi possibili criteri per costruire le code multiple, in base a vari criteri (priorit, con o senza prelazione ecc...), inoltre sono stati ideati anchealgoritmi basati sull'estrazione a sorte (Lottery Scheduling), dove ogniprocesso ha un range di numeri associati, e viene eseguito il processo che possiede, nel suo range, il numero estratto dal SO.

    4.6 - Sistemi Real-TimeIl termine Real Time, usualmente usato come sinonimo di in tempo rea-le/ in diretta", tuttavia nel caso di SO si parla di problema Real Time per in-dicare sistemi in cui la correttezza dell'output dipende dal momento in cui sono prodotti.In un sistema RT (Real Time), spesso di fondamentale importanza, il momento in cui sono prodotti i dati di uscita (es quando e come azionare i freni della mac-china da parte dell'ABS). I problemi possono essere:

    Soft real time: la perdita di qualche scadenza accettabile Hard real Time: la perdita di una sola scadenza inaccettabile

    La distinzione si basa sulla gravit delle conseguenze, sulla funzione di costo (continua per i soft, la perdita aumenta perlopi gradualmente con il numero di miss, discontinua per gli hard, dove il costo aumenta drasticamen-te dopo un certo numero di miss) e sulla probabilit di miss accettata.

    Una seconda classificazione in base al tipo di compito/ambiente in cui il si-stema si ritrova ad operare:

    Purely Cyclic: Completamente Ciclici (tipicamente monitoraggio/con-trollo es telecamere)

    Mostly Cyclic: Quasi completamente ciclico,con qualche evento asin-crono (es. Allarmi, comandi, PLC)

    Asincronous and somewath predictable: Sistema con eventi asin-croni, ma generalmente prevedibili (es. controllore aereo)

    Asyncronous and unpredictable: Sistema predominato da eventi asincroni e non predicibili (es. sistema di guida automatico di un'auto-mobile).

    Una definizione di SO real time: sistema capace di garantire determini-sticamente il rispetto dei requisiti HARD REAL TIME dei processi utente", in pratica ho la garanzia di risposta a certi interrupt in un tempo prefissato.

    Il SO pu garantire l'ampiezza massima del periodo di latenza compreso tra il rilevamento dell'interrupt e l'inizio della routine di gestione specifica.Nascono, perci una serie di problematiche specifiche riguardo allo schedu-ling e all'architettura interna.

    4.6.1 - Caratteristiche dei sistemi Real TimeLa maggiore caratteristica di un SO Real Time la certezza della latenza tra riconoscimento dell'interrupt e l'avvio della routine di gestione.Per contenere il pi possibile anche il valore temporale di questa latenza oc-corre progettare un sistema con alcune caratteristiche specifiche:

  • Deve lavorare quasi mai ad interrupt disabilitati; Le sezioni del Kernel ad interrupt disabilitati devono essere "brevi" e po-

    che, Il Kernel scritto in modo prelazionabile dagli interrupt dei problemi

    real time (se viene sollevata un istanza real time essa ruba la CPU al kernel).

    Implementa lo split interrupt handling, la gestione divisa degli in-terrupt in parte urgente e meno urgente.

    Per determinare in modo preciso le scadenze delle applicazioni Real Time si ha bisogno di orologi ad alta risoluzione (hight resolution clock, clock di millisecondi sono insufficienti in alcuni casi). Questo comporta interrupt di clock pi frequenti (che aumentano il costo in termini di mode switch ecc...), inoltre le normali system call sono imprecise per questi conte-sti (tra la lettura dell'ora e l'uso del dato da parte dell'applicazione pu passare un tempo non trascurabile), quindi si sono implementati meccani-smi che portano a scavalcare il SO nella lettura del ora. Questa una pale-se violazione delle regole generali dei SO resa obbligatoria dalle esigenza implementative.

    Oltre all'HW specifico, vengono anche fatte aggiunte di tipo software come le real time API (POSIX Extensions) che aggiungono i timers e i relativi wat-chdog software, le quali sono specifi che syscall che controllano che una certa operazione sia eseguita entro un determinato lasso di tempo. Se ci non avviene (solitamente il timer non viene spento in tempo) sollevato un evento che avvia delle routine specifiche (usualmente scatta la routine che porta alla posizione di massima sicurezza).Le RTAPI aggiungono anche la gestione di priorit a 32 livelli, delle ap-posite chiamate per la sincronizzazione dei processi real time (cio messaggi tra processi con ricezione e latenza garantiti).Altra caratteristica il Lock della Memoria dei processi RT, la quale non per-mette di spostarli su disco fisso (swap).

    Per risolvere i tappi dovuti a processi a bassa priorit che bloccano una ri-sorsa necessaria ad un processo a priorit pi alta, si fa ereditare da quest'ultimo, temporaneamente, lo stesso livello di priorit (fenomeno dell'inversione di priorit o priority inheritance).

    4.6.2 - Scheduling Real TimeDate le criticit del problema real time lo scheduling deve implementare per forza la gestione delle priorit e della prelazione.

    Per gli eventi periodici va fatta un'analisi di schedulabilit dati: Se N il numero di eventi periodici e Ti il periodo dell'evento i-esimo e Ci il costo (in tempo) della routine specificata dell'evento i-esimo la frazione di tempo diCPU definita come:

    C iT i

    In particolare se N maggiore di 1 deve essere:

    i=1

    N CiT i1

    Una volta che l'analisi ha dato risultati positivi esistono vari algoritmi che ven-gono implementati per gestire problemi real time:

    Rate Monolitic

  • La priorit maggiore viene data alla routine dell'evento con frequenza mag-giore.

    Il tasso di utilizzo di questo scheduler di i=1

    N CiT in (2

    1n1) ; inoltre possibile

    che non garantisce assolutamente le dead-line.

    EDF: Erliest Deadline First:Viene assegnata la priorit in base alla vicinanza con la scadenza (pi vicina la scadenza pi la priorit maggiore).In questo caso possibile ottenere tassi di utilizzo del sistema vicini al limite teorico di 1.

    4.6.3 - Struttura generale interna dei SO Real Time

    In generale un SO Real Time ha un'architettura a microkernel in quanto essendo usualmente SO embedded le caratteristiche di modularit sono molto pi importanti degli svantaggi in termini di pre-stazioni.Per tali sistemi si predilige la stabilit e la velocit delsistema, inoltre, molte componenti di un kernel nor-male non sono necessarie per questi sistemi, quindi vengono eliminate per ridurre al minimo la possibilit di avere bug nel kernel.Generalmente sono sistemi dedicati e molto ottimiz-

    zati.

    L' architettura doppio kernel, prevede l'uso di un SO tradizionale (Windows/Linux) il cui kernel non si appoggia direttamente sull'HW ma su una patch del kernel real time permettendo l'estensione de l le funzionalit del sistema anche alle applica-zioni utente.Il SO tradizionale si trova cos a girare come se fosse un'applicazione utente a priorit pi bas-

  • sa.Se arriva un interrupt real-time il SO usa i criteri ad essi assegnati, nel caso opposto (per es. una lettura su disco) viene degradato a pi bassa priorit al SO tradizionale (Linux/Win).In questo modo possono funzionare alcune garanzie real-time anche su siste-mi tradizionali.

    4.7 - Comunicazione tra ProcessiEsistono due modi possibili per ottenere la comunicazione tra processi:

    Memoria condivisa: si basa sulla modifica degli stessi dati. Comunica-zione implicita semplice ed efficiente. Funziona solo per processi che ri-siedono sulla stessa macchina ed esistono alcune complicazioni imple-mentative dovute alle race condition.

    Scambio di messaggi: un meccanismo esplicito per cui esistono strutture dati apposite, protocolli ecc. Introduce dei costi di elaborazio-ne maggiori ma semplifica il controllo della correttezza della trasmis-sione dei dati.

    4.7.1 - Comunicazione con memoria condivisa.Cominciamo l'analisi introducendo il problema del produttore/consuma-tore dove una sorgente di dati (pro-duttore) deve scrivere all'interno di un buffer (memoria, spesso intesa come circolare) mentre un processo (consumatore) deve leggere i dati in-seriti dal produttore.I vincoli di questa struttura sono due:il produttore non deve sovrascrivere i dati prima che il consumatore li abbiausati (altrimenti vengono persi) e il consumatore non deve leggere i dati prima che il produttore li abbia ag-giornati (altrimenti legge dati non va-lidi).

    In pratica i due puntatori, mostrati in figura (IN e OUT) non devono mai supe-rarsi.Un primo esempio di codice relativo alla gestione di tale memoria il seguente:produttore...WHILE(TRUE){produce il dato;WHILE(contatore == dimArray){ no-operazioni; } buffer[in]=DATO;in=(in+1)%dimArray; //fa il girocounter++; }

    consumatore... WHILE(TRUE){WHILE(counter == 0){no-operazioni};lettura=buffer[out]; out=(out +1);counter ;usalettura();}

    La variabile "counter" tiene conto dei dati da leggere, se 0 non c' nes-sun dato da leggere e non si deve leggere nulla.

    Questo codice sbagliato perch contiene un problema non immediata-mente individuabile. Dobbiamo ricordarci che il produttore e il consumatore sono due processi diversi, quindi l'interruzione avviene in un'istante casuale che dipende

  • dall'algoritmo di scheduling, inoltre accade sulle istruzioni in linguaggio macchina non su quelle ad alto livello scritte di sopra.Il problema si concentra sulle istruzioni "counter ++" e "counter ", le quali potrebbero non essere aggiornate in tempo (ci prima che venga eseguito il proces-so duale), questo tipo di problema si chiama race contition consiste in errori checompaiono durante l'esecuzione del codice (secondo problema, si possono non pre-sentare per 1000 esecuzioni diverse dello stesso tipo e comparire alla 1001, quindi sono molto difficili da trovare e correggere).

    Ognuna delle istruzioni ad alto livello in realt una sequenza istruzioni a bassolivello, ad esempio nel caso delle intruzioni count ++ e count -- si hanno tre istruzioni a basso livello.Immaginiamo di lavorare su un calcolatore con architettura "load/store" in assembler le due operazioni critiche risultano:

    COUNTER ++

    LOAD RX COUNTERINC RX

    STORE RX COUNTER

    COUNTER --

    LOAD RY COUNTER DEC RY

    STORE RY COUNTER

    Si prenda come esempio questo codice assembly, con valore iniziale di counter pari a 3 (P1 indica valore della variabile counter nei registri della CPU; mem il valore scritto in memoria RAM).

    P1 LOAD RX COUNTER //Carica counter i n RX; P1=3 mem=3P1 INC RX //Incrementa RX; P1=4 mem=3interruptP2 LOAD RY COUNTER //Si passa a P2 e legge mem=3 (dovrebbe essere 4)P2 DEC RY //Decrementa RY, P2=2 mem=3P2 STORE RY //Carica counter i n RX; P2=2 mem=2P1 STORE RX //Finito P2, conclude P1. P1=4 mem=4

    In memoria il valore di counter 3+1-1=4.Questi casi vengono chiamati race conditions e sono situazioni condiziona-te da fattori esterni.Per eliminarli, gi in fase di progetto bisogna individuare nei due processi del le sezioni critiche, cio parti di codice dove possono incorrere race con-ditions, in cui vado a garantire l'atomicit estesa.La propriet dell'atomicit estesa consiste nel rendere non interrompibili del-le parti di codice (a livello macchina) alto livello. In questo modo gli incrementi si concludono ed il problema risolto.

    Per risolvere il problema delle race conditions dobbiamo fare alcune ipotesi: Qualunque processo ha velocit maggiore di 0 (prima o poi verr ese-

    guito. Non si sa nulla sui "valori" di velocit (hanno priorit diverse). SI considera qualunque combinazione di eventi (anche quelle pi im-

    probabili)

    Inoltre la gestione delle race condition, per essere corretta, deve soddisfare alcuni requisiti:

    Mutua esclusione. Se P1 viene interrotto nella sezione critica, P2 viene eseguito fino a subito fuori la sua sezione critica.

    Progresso: Se la sezione critica libera posso eseguire quella di un

  • qualunque processo in coda. Attesa limitata: non ci deve essere Starvation di nessun processo,

    e non devono esserci attese circolari (Deadlock); necessario quindi un meccanismo di sincronizzazione tra processi.

    Esempi di meccanismi errati per la gestione delle race condition.Gli esempi di seguito sono errati al lo scopo di sottolineare meglio le difficol-t esistenti nella gestione delle sezioni critiche. Si Considera ancora il caso produttore/consumatore.

    Esempio 1:Var condivisa (in memoria): Int Turn=0;

    P0while (turn!=0)

    {no operazioni;}Sezione critica

    Turn=1;

    P1while (turn!=1)

    {no operazioni;}Sezione critica

    Turn=0;

    Questo esempio viola il requisito di progresso; se un processo utilizza la sezio-ne critica deve per forza aspettare che l'altro finisca, e non importa in che punto della coda di scheduling esso si trovi. Questo porta alla possibilit di dover aspettare un tempo potenzialmente infinito per la conclusione dell'ela-borazione.

    Esempio 2:Var condivisa (in memoria): Int critica[]=0; //Vettore

    P0critica[0]=1;

    while (critica[1]){no operazioni;}Sezione criticacritica[0]=0;

    P1critica[1]=1;

    while (critica[0]){no operazioni;}Sezione criticacritica[1]=0;

    In questo caso la propriet di progresso garantita, ma se dovesse occor-rere un interrupt tra la prima e a seconda istruzione:

    critica[0]=1interruptcritica[1]=1se critica[O]==1 allora non fare nullainterrupt(riprende PO) se critica[1]==1 allora non far nullaIn questo caso si ha una deadlock, dove P0 aspetta che P1 esca dalla sua sezione critica mentre P1 aspetta P0. Se scambiamo l'ordine delle prime due istruzioni risolviamo il problema della deadlock ma violiamo il principio di mutua esclusione: superato il while P1 entra nella sezione critica e P0 pure.

    Esempio 3:Var condivisa (in memoria): Int critica[]=0; //Vettore

  • P0while (critica[1])

    critica[0]=1;{no operazioni;}Sezione criticacritica[0]=0;

    P1critica[1]=1;

    while (critica[0]){no operazioni;}Sezione criticacritica[1]=0;

    Se avviene un interrupt come nel caso precedente (tra la prima e la secon-da istruzione di P1) il "gioco" sembra funzionare, l'unico problema che si pu riscontrare che, in questo caso, uno dei due processi viene trattato con poca equit, c' il rischio di starvation.

    Esiste una soluzione ottimale ed :

    4.7.2 - L'algoritmo di PetersonVar condivise (in memoria): Int turn=0; Int critica[]=0; //Vettore

    P0critica[0]=1;

    turn=1;while (critica[1] & turn==1)

    {no operazioni;}Sezione criticacritica[0]=0;

    P1critica[1]=1;

    turn=0;while (critica[0] & turn==0)

    {no operazioni;}Sezione criticacritica[1]=0;

    Tutti i requisisti sono soddisfatti in quanto se esiste solo P0 il requisito di progressione garantito.Se esistono PO e P1 ed entrambi arrivano alla sezione critica, inizialmente P0 resta in attesa, poi P1 si ferma, e solo allora P0 entra nella sezione, quindi garantita la mutua esclusione, cosi come il requisito di attesa li-mitata (prima o poi verranno eseguiti tutti e due, non c' rischio di starva-tion).Esiste comunque un limite: non estendibile a pi di due processi.

    Il problema sembra non risolvibile a livello puramente software, quindi sono state trovate delle soluzioni che sfruttano l'HW per aggirare il problema.

    4.7.3 - Istruzioni HW ad atomicit estesaTest and Set: legge e setta (mette a 1) la variabile indicata; ci elimina una race condition molto frequente.

    Esempio:Int Lock=0; //Assumer solo due valori 0 e 1while (T&S(Lock)){no operazioni;}Sezione criticaLock=0;

    La garanzia di atomicit ci viene data dal processore, quindi tale implemen-tazione rende la soluzione valida per un numero indefinito di processi ma re-sta invariato il problema della mancanza di equit (starvation) in quanto non possibile prenotare l'uso della sezione critica (meccanismo di code).Tutti questi ultimi algoritmi, se pur funzionanti, hanno un piccolo difetto in co-mune: quando un processo aspetta che si liberi la risorsa, occupano la CPU,

  • con delle operazioni di vuote, creando un fenomeno chiamato busy waitinig; inoltre non sono algoritmi immediati, perci i programmatori fanno fatica ad inserirli nei loro progetti.La soluzione pi semplice quella di avere un costrutto a livello di SO che permetta di "mettere a dormire i processi in attesa.

    4.7.3 - I SemaforiUn semaforo, a livello logico, un dispositivo con stato rappresentato da un numero (da O a n) e una coda:

    se 0 il semaforo rosso, in questo stato il semaforo pu avere una coda piena (processi in attesa).

    se 0 il semaforo verde, in questo stato il semaforo non ha una coda.

    Esistono due operazioni di base per i semafori: Wait: se il semaforo verde decrementa la variabile e il processo entra nella

    sezione critica, se rosso il processo viene accodato e passa dallo stato rea-dy a blocked.

    Signal: se verde incrementa lo stato, se rosso o fa tornare ready il pri-mo della coda o incrementa lo stato (e il semaforo diventa verde).

    Quando il processo che chiama un wait legge lo stato del semaforo, nel caso che sia verde (la variabile 0) continua l'esecuzione altrimenti aspet-ta un signal che lo svegli. Se arriva un signal senza nessuno in coda resta in attesa di qualcuno.

    Esistono tre implementazioni alternative.

    Semaforo inizializzato ad 1: serve per gestire la mutua esclusione in accesso ad una sezione critica (semaforo mutex)

    wait(MUTEX) S ez io n e C r i t i ca signal(MUTEX)

    Semaforo inizializzato a 0: serve quando si vuole una relazione di prece-denza stretta tra due eventi (uno deve avvenire prima dell'altro)

    P0

    wait (sync)

    P1

    signal (sync)

    ...In questo esempio ci sono due processi, in cui una parte del codice di P0 non pu essere eseguita prima che P1 arrivi ad un punto stabilito (parte sopra il signal).In questo modo P0 si ferma se prima dell'istruzione wait P1 non ha eseguito l'istruzione signal liberando il semaforo.

    Semaforo inizializzato ad n (n=>2): serve per gestire n risorse equiva-lenti (per esempio viene utilizzato nel caso di pi produttori e consumatori che accedono alla stessa memoria condivisa).

    Esempio: Buffer circolare con i semaforiInt empty=n; full=0; mutex=1; //Semafori.

  • PRODUTTORE

    Produci dato();wait(empty);wait (mutex);

    Sezione Critica (Buffer)signal (mutex);

    signal (full);

    CONSUMATORE

    wait(full);wait(mutex);

    Sezione Critica (Buffer)signal(mutex);signal(empty);

    I semafori vengono gestiti dal SO (sono infatti costrutti di sistema), tuttavia nelle applicazioni utente viene utilizzato il concetto di Monitor.

    4.7.4- I MonitorUn monitor un tipo di dato astratto, un insieme di variabili e di funzioni che operano sulle varia-bili secondo le convenzioni della OOP (Object Orientig Programming).

    Nei monitor, i metodi/funzioni che scrivono/leggono le variabili sono condivisi; in questo modo si garanti-sce che un solo processo alla volta possa entrare nel monitor. Permettedi bloccare tutto il monitor con unacoppia wait-signal senza doverne usare molte in tutto il codice (mu-tua esclusione).

    Questa struttura da sola non sufficiente, perci si aggiungono all'oggetto monitor delle variabili condition sulle quali agiscono due metodi:

    wait(): fa si che un processo si "addormenti" dentro il monitor in atte-sa del verificarsi di una condizione liberando il monitor per l'accesso di un altro processo

    signal(): sveglia un processo "addormentato" nel monitor. Nel caso cifosse un altro processo mentre lo svegliato dormiva, uno dei due deve essere "sbattuto fuori" immediatamente per non causare race conditions. La precedenza dei processi questo caso non a priori.

    Esistono due tipi di monitor, differenziati sulla gestione delle priorit: Monitor di Hoare: La precedenza va allo svegliato, lo svegliante viene

    "sbattuto fuori" in una coda d'attesa. Monitor di Lampson-Redell: La precedenza va allo svegliante ( lo sve-

    glaito viene messo in coda).Generalmente implementato il monitor di Hoare, tuttavia possibile ottenere l'altra tipologia inserendo un signal() alla fine (analogo nel caso opposto). In Java ogni oggetto un potenziale monitor e si utilizza la parola chiave syn-cronized per sincronizzare gli oggetti.

    4.7.5 - Comunicazione con scambio di messaggiLo scambio di messaggi offre meno problemi rispetto alla memoria condivisa grazie alla propriet di sincronizzazione implicita:"Un messaggio non puessere ricevuto prima di essere inviato".

  • Esistono vari meccanismi per implementare la comunicazione con scambio di messaggi, ciascuno dei quali definisce:

    chi e come crea il canale (logico) di comunicazione; definisce se il canale per due o pi processi; definisce se il canale bufferizzato (qual' la capacit del canale).

    Comunicazione direttaLe primitive per la comunicazione saranno:

    Send(processo, messaggio), dove processo il destinatario e mes-saggio il messaggio da inviare;

    Recive(processo2, messaggio), dove, processo2 indica il mittente au-torizzato.

    un tipo di comunicazione simmetrica che pu coinvolgere solo due pro-cessi, in alternativa, la comunicazione asimmetrica, con primitive simili a:

    Send(processo, messaggio), come nel caso simmetrico Recive(processo2,messaggio), dove processo2 indica qualunque pro-

    cesso.

    Il canale creato dal SO e deve essere per forza tra due processi (un canale per ogni coppia processo/processo2).

    La comunicazione diretta non permette la definizione di priorit (c' un solo canale e non si pu sorpassare) inoltre non posso mandare messaggi a qualcuno che deve ancora nascere (il destinatario deve essere conosciuto).

    Per questi motivi i meccanismi pi usati si basano su una comunicazione in-diretta.

    Comunicazione indirettaIn questo caso esiste una struttura dati che fa da tramite per i messaggi (una casella postale, mailbox che non c'entra nulla con la posta elettronica).Le primitive diventano.

    Send(casella, messaggio) Recive(casella, messaggio)

    Quindi esistono syscall per creare la casella, inoltre il rapporto tra utenti deve essere n:n (n caselle per n utenti).Con questo metodo possiamo incanalare un messaggio a priorit pi alta in determinate caselle.

    In questo caso si hanno tre tipologie di canali:

    a capacit O: il messaggio non pu rimanere nel canale, ci implica che non posso fare send se non ho il recived del destinatario (esempio telefono). La send una chiamata bloccante.

    canale a capacit n: (esempio casetta della posta) la send non una chiamata bloccante finch la casella non piena, lo diventa con la cassetta piena.

    canale a capacit infinita: (non esiste in realt ma posso compor-tarmi come se lo fosse semplicemente accettando di perdere i mes-saggi o sovrascriverli una volta piena la memoria messa a disposizione).

  • Con lo scambio di messaggi il problema produttore/consumatore, grazie alla propriet di sincronizzazione implicita ha una soluzione banale:

    recived(mutex)s e z i o n e cr it i casend(mutex)

    La casella inizializzata con un messaggio, alla prima recived si svuota; tutti i processi che tentano una recived per entrare nella loro sezione cri-tica devono fermarsi e aspettare che chi occupa la propria sezione critica laliberi con un send. A questo punto la casella conterr almeno un messaggio da essere ritirato e sbloccher il prossimo processo.

    Esistono anche dei messaggi specifici di interruzione e segnalazione (che in Linux si chiamano signals),

    Uno dei sistemi pi usati per scambiare messaggi il socket.Tra due processi si crea un canale di comunicazione, le chiamate send e re-cive fatte dai processi vengono fatte verso il socket il quale viene visto come un flusso di byte. Questo modo di interpretare il socket necessario in quanto i dati vengono letti nell'ordine in cui sono stati inviati ma non neces-sariamente con gli stessi blocchi con cui sono stati inviati.

    Un altro sistema sono le pipe, che sono utilizzabili solo su processi che ri-siedono sulla stessa macchina ma possono essere gestite via codice in un'applicazione perch basta creare un processo padre che le condivide per ereditariet.

    Un altro metodo usato in UNIX sono i signal, che sono dei messaggi speci-fici di interruzione e segnalazione che il processo pu ricevere e ricono-scere agendo di conseguenza (in caso contrario verr ucciso). Altri metodi sono le mailbox, la shared memory e i semafori.

    4.7.6 DeadlockLa deadlock una attesa illimitata da parte dei processi a seguito di un bloc-co complessivo del sistema.Un esempio lo spooling di stampa, dove presente una stampante con 100MB di spazio per la coda, e voglio lanciare 2 stampe da 6OMB contempora-neamente, tuttavia quando il trasferimento ha raggiunto i 50MB per documen-to, infatti si ferma tutto perch la memoria esaurita.

    Le condizioni necessarie affinch si possa verificare la deadlock sono:

    Mutua esclusione: un processo esclude l'altro e non vengono mai ese-guiti

    Hold & wait No preemption (prela-

    zione) attesa circolare

    Esiste un algoritmo che permet-te di evitare i deadlock:

    Algoritmo dello Struzzo

    Consiste nell'idea generale della speranza che non ci siano e nel contempo

  • si cerca di renderle poco probabili.Siccome a volte succedono, per evitarli si pu:

    renderli impossibili: bisogna fare in modo di gestire bene le 4 condizio-ni per il deadlock (si cerca di eliminare l'attesa circolare). renderli impossibili in corso d'opera: il SO esegue dei controlli pre-ventivi sull'accesso alle risorse ed eventualmente aumenta i tempi d'attesa.Questo metodo per gravoso in termini di utilizzo delle risorse e ha il soli-to problema della necessit di conoscere in anticipo quello che il sistema do-vr fare. rilevamento automatico dei deadlock: (onerosa da un punto di vistadi risorse richieste)

    In conclusione la soluzione migliore ancora usare l'algoritmo dello struzzo (letteralmente far finta di nulla) effettuando ogni tanto dei controlli.

    5 Gestione della memoria (RAM)Durante il normale funzionamento di un sistema di elaborazione risiedono in memoria centrale pi programmi contemporaneamente e il meccanismo chepermette a ogni programma di non confondere i suoi pezzi con altri abba-stanza articolato.

    5.1 - Binding degli indirizziSe consideriamo un frammento di codice in cui, ad esempio, si definisce una variabile, durante l'esecuzione ci si trover nella condizione di dover passare dall'entit astratta, ad una cella di memoria concreta, nella quale memo-rizzata l'informazione che desideriamo.Questa procedura (i passaggi di label di una variabile a indirizzo fisico) detto: Binding degli indirizzi.

    Il Binding degli indirizzi pu essere implementato in modi diversi: Bindig in compilazione: Il limite evidente, la rigidit di questo

    tipo di binding impedisce l'esecuzione di pi programmi diversi in con-temporanea.

    Binding in fase di caricamento: Un programma C viene compilatogenerando un file .out. A questo punto, durante il caricamento il lin-ker si preoccupa di trasformare i riferimenti mnemonici in riferimenti nu-merici. Questo tipo di binding consiste, nel riscrivere parte dell'esegui-bile sostituendo i numeri. Non ci sarebbero controindicazioni, se tutti i programmi fossero sempre caricati in memoria centrale, purtroppo per non cos, perch spesso vengono ributtati sul disco quando sono inattivi. Con questo metodo, per risvegliarli si dovrebbe attende-re che sia libera una quantit di memoria (contigua) pari alla dimensio-ne del programma compilato, pi quella dei dati.

    Binding in runtime (esecuzione): Il metodo utilizzato in pratica. Esso sfrutta il concetto di Memoria Virtuale.

    5.2 - La memoria virtualeOgni processo vede una sua memoria dedicata che si estende dal byte 0 albyte n, ma solo un'illusione. In questo modo la CPU esegue le operazioni fa-cendo riferimento alla memoria virtuale, che verranno successivamente tra-

  • dotti in indirizzi fisici.

    Praticamente esistono diversi modi per allocare la memoria su un calcolato-re:

    5.2.1 - Allocazione contiguaConsiste nel caricare tutto il programma in celle di memoria adiacenti.Poniamo il caso di avere un programma P1, che ha una memoria virtuale che si estende dal byte 0 a D1.In memoria centrale, dopo una parte dedicata al SO, che termina per esempio al byte 3000, avremo una corrispondente area di memoria (da 3001 a 3001+D1) contenente le informazioni di P1. Durante la vita di P1 probabile che verr tolto dalla memoria centrale (RAM) e poi ricaricato. Per ricaricarlo, per esempio, nella locazione 5000 devo allocare in memoria centrale le celle dalla 5000 alla 5000+D1.

    Questo meccanismo ha dei grossi problemi: il processo o tutto in memoria centrale o su disco; non posso caricarlo solo in parte.Quindi anche se ho della memoria libera, non detto che possa utilizzar-la. Questo genera il fenomeno della frammentazione interna: si creano degli spazi liberi non contigui e non sfruttabili al meglio (se troppo piccoli per qua-lunque processo sono addirittura inutilizzabili).

    Una possibile soluzione sarebbe quella di accorpare gli spazi liberi, ma per fare ci devo spostare i dati contenuti nella RAM; operazione che richiede cal-coli anche onerosi con un forte spreco di risorse.

    Un'analisi del fenomeno ha portato alla creazione della regola del 50% se-condo la quale, dopo un po' che il sistema in funzione con un numero n di processi si hanno n/2 buchi.

    Questi sono i risultati a cui si giunti sfruttando l'algoritmo First-Fit. Visti i deludenti risultati si provveduto allo studio di un altro algoritmo chiamato Best-Fit il quale agisce scegliendo, tra tutti gli spazi disponibili, il pi picco-

  • lo utile. Cos per, oltre a buttare un sacco di tempo facendo passare tutti gli spazi, s i creano dei rimasugli piccolissimi che non verranno mai usati.

    5.2.2 - La PaginazioneIn questo caso si suddivide sia lamemoria virtuale che quella fisi-ca in pagine di uguale dimensio-ne (con pagine chiamate fra-mes). Qualunque pagina liberapu essere utilizzata, inoltre la dimensione della memoria fisica svincolata da quella della memoria virtuale; la memoria virtuale pu essere anche mag-giore di quella fisica.Inoltre possono esistere pro-cessi in parte in memoria cen-trale e in parte sul disco.L'unico prezzo da pagare per una cos alta flessibilit la necessit di tabelle che mappi-no in questo modo la memoria virtuale in quella fisica.Per ogni processo esiste in me-moria una tabella detta tabelladelle pagine nelle cui righe vengono memorizzate l'indirizzofisico di ogni pagina.

    La traduzione dall'indirizzo virtuale a quello fisico, viene effettuato somman-do l'indirizzo della pagina fisica (che corrisponde al primo byte della pagina) all'indirizzo virtuale fornito (in questo modo modifico solo la parte pi signifi-cativa dell'indirizzo mentre il resto passa inalterato o quasi).

    Questo metodo vienechiamato indirizza-mento per direct mapping.Il problema della frammentazione non completamente ri-solto perch allocare solo spazi multiplidella dimensione del-la pagina, perci molto probabile che qualche pagina non sar completamente riempita ma l'entit del fenomeno co-munque minore ri-spetto al caso con al-locazione contigua.Inoltre sorgono ancheproblemi di prestazio-ni dovuti alla serie dicalcoli necessari al mapping, ridotti attraverso un'opportuna circuiteria hard-ware, che scarica tale mansione dalla CPU.Il problema risiede nel fatto che qualunque accesso in memoria necessita di

  • un altro accesso per la tabella delle pagine e quindi si osserva il raddoppiodegli accessi in memoria (e il conseguente dimezzamento della veloci-t della RAM dal punto di vista della CPU).Per limitare i danni degli accessi in sovra numero stato introdotto un livel-lo di cache (una memoria aggiuntiva di piccole dimensioni ma molto veloce) chiamata TLB (Translation Lookaside Buffer) che memorizza le associazioni gi fatte. Il doppio accesso si ha solo quando si verifica un miss nel TLB.

    Il costo medio di accesso alla RAM viene misurato con hr (hit radio, quante cor-rispondenze ho nella TLB in media) hr(tTLB+tMEM)+(1-hr)(tTLB+2tMEM) che in media maggiore del 90% (considerando tTLB molto minore di tMEM, che sono i tempi di accesso alle due memorie).

    Bisogna tenere conto del-la memoria occupata dal-le tabelle delle pagine, che non sono altro degli array, e perci risentono di tutti i problemi di alloca-zione contigua, inoltre molte delle pagine nonsono utilizzate.L'unica soluzione trovata per gestire meglio la tabel-la quella della reiterazio-ne dell'algoritmo (si pagi-na la tabella delle pagi-ne).

    Il costo in questo caso diventa: hr(tTLB+tMEM)+(1-hr)(tTLB+(n+1)tMEM), dove n li-vello di paginazione (inteso come numeri di accessi necessari per trovare l'indirizzo fisico della pagina cercata).

    Una soluzione alternativa stata quella di creare una tabella delle pagine invertita, dove si passa da una situazione con una tabella per ogni processoa una di dimensione fissa (data dalla dimensione della RAM) dove ogni riga contiene il corrispondete numero della pagina memorizzata.La tabella risulta cos molto pi piccola ma con il problema di avere una rap-presentazione diversa da quella che naturalmente si vuole leggere.

    La traduzione dell'indirizzo virtuale - PID (Pagina virtuale), avviene cercando la giusta riga (che indica il n di pagina fisica corrispondente) e perci si deve far passare tutta la tabella, con un infinit di calcoli; inoltre questa ta-bella contiene solo le pagine della memoria centrale.

    La soluzione resta quindi la paginazione della tabella.Il salvataggio di tutte le tabelle avviene durante il process switch.

    5.3 - SegmentazioneOgni processo posto in memoria centrale costituito da diverse parti (vedi capito 4.1) non omogenee tra di loro, a cui opportuno applicare dei permessi di lettura/scrittura, cosa difficile da implementare se utilizzo un unico blocco dimemoria virtuale.Consideriamo, per esempio, un blocco di memoria virtuale come quello del-la figura seguente, allocato con la tecnica della paginazione.

  • Ad esempio il campo text contiene solo il codice eseguibile, quindi avr permessi di sola lettura, inoltre la sua dimensione nel tempo rimarr fissa, all'opposto il campo data contiene i dati (conte-nuto delle variabili) di cui necessita il programma per funzionare, la cui dimensione spesso dinami-ca e pu cambiare nel tempo. Quindi oltre al fatto che non possibile mettere deipermessi, si ha anche il problema che cambia nel tempo il contenuto delle singole pagine a causa delallargamento o del restringimento di alcuni campi.Con la segmentazione, si assegna ad ogni campo una propria memoria virtuale indipendente da quella assegnata agli altri segmenti.Lo spazio di indirizzamento viene quindi scomposto in segmenti a livello logico, quindi anche l'indi-rizzo logico, sar diviso in due parti: il numero del segmento a cui fa riferimento e l'offset, cio la po-sizione del dato all'interno della memoria virtuale del segmento.In questo modo possibile applicare dei controlli, ad esempio, se un segmen-to dichiarato read-only non possibile modificarne il contenuto con opera-zioni di scrittura errate.

    Un altro vantaggio si apprezza durante la fase di collegamento (linking) con allocazione paginata, infatti se devo fare linking in un'unica sequenza di indi-rizzi e ho necessit di modificare una struttura dati, devo ripetere i colle-gamenti su tutto lo spazio di indirizzamento. Questo avviene a causa del fattoche le pagine inferiori si sono spostate (fig sx), mentre in una gestione a segmenti (fig dx) se allungo un segmento l'inizio degli altri non viene sposta-to l'indirizzo logico mantenendo validi i calcoli fatti precedentemente.

    I vari segmenti possono essere allocati con le tecniche viste precedentemen-te.

  • 5.3.1 Segmentazione ad allocazione contiguaIn questo caso i vari segmenti sono allocati in blocco nella memoria RAM e l'indirizzo logico viene tradotto in fisico, individuando prima il segmento a cui fa parte, tramite la tabella dei segmenti. Successivamente si utilizzano i valori di limite e base (contenuti nella tabella dei segmenti). Il valore di li-mite viene confrontato con il numero del segmento: se minore vengono affiancati offset e base trovando l'indirizzo del segmento fisico, altrimen-ti viene generato un errore di indirizzamento.

    Con questo metodo per si problemi dovuti alla frammentazione.

    5.3.2 Segmentazione PaginataSi combinano le due tecniche applicando la paginazione ai segmenti. In que-sto caso la tabella dei segmenti di un processo punta alle relative tabelle delle pagine.

    Quindi per risolvere l'indirizzo fisico si procede come gi visto: prima si con-trolla nel TLB se gi risolto questo indirizzo, in caso contrario il numero del segmento mi permette di trovare, nella tabella dei segmenti, l'indirizzo del-la tabella delle pagine a lui relativa. Fatto ci, con il numero di pagina virtua-le ottengo la relativa fisica che affiancato all'offset mi da l'indirizzo reale cer-cato.

  • Anche qui si possono essere pi livelli per i motivi gi visti.La condivisione delle librerie diviene ancora pi vantaggiosa in questo caso perch due segmenti diversi usano la stessa tabella delle pagine per acce-dere alla libreria condivisa.Questo metodo pu avere un supporto HW per facilitarlo.

    Esempio 386Ogni processo pu avere al massimo 16000 segmenti, di cui 8000 privati (LDT Local Descrptor Table) e 8000 condivisi tra tutti i processi e che con-tengono il SO a livello virtuale (GDT Global Descriptor Table). In questo modo ogni processo vede in modo separato, nelle sue tabelle lo stesso SO, e questofa si che i processi non debbano cambiare le proprie tabelle di riferimento neimode-switch (tranne durante i process-switch perch cambia il processo).

    5.3 - Gestione dei page faultAccade spesso che un processo, durante la sua esecuzione, salti, ad esempiodalla pagina 0 alla 5. Il salto perfettamente legale nella memoria virtuale ma non detto che lo sia in memoria centrale, infatti, visto che la memoria virtuale pu essere anche molto maggiore della memoria fisica, possibile che la pagina richiesta non si trova nella memoria centrale: si ha cosi un page fault. A questo punto avvengono una serie di operazioni:

    1) Il SO, con l'aiuto del processore, rileva il page fault (interrupt) e intervie-ne (esegue un process e mode switch).

    2) Recupera dalla tabella delle pagine la sua posizione sul disco (o verifica che l'indirizzo richiesto e illegale e si comporta di conseguenza).

    3) Decide dove mettere la nuova pagina in RAM (questo significa che se lamemoria centrale piena sceglie chi spostare su disco al posto suo) e avvia il trasferimento dal disco.

    4) Mette il processo nello stato di bloccato finch non termina il trasferi-mento dal disco.

    5) Finito l'I/O il processo torna pronto e verr messo in coda per poter essereeseguito dalla CPU.

    Tutto questo necessario per una accesso in memoria con page fault, quin-di il tempo medio di accesso in memoria diventa: PPFtPF+tMEM)+(1-PPF)tNoPFdove PPF indica la probabilit di page fault, tPF il tempo necessario a risolvere un page fault (di solito nell'ordine dei 10ms) e tNoPF il tempo per accede alla RAM in condizioni normali (calcolato dalla formula precedente e di solito nell'ordine dei 10ns).Da ci risulta evidente che per non degradare troppo le prestazione del siste-ma i page fault devono essere i meno possibili quindi le scelte delle pagi-ne da spostare sul disco e quelle caricate in RAM in media a ogni processo risultano essere critiche.Gi in fase di caricamento si possono delle scelte su cosa caricare per un processo per limitare i page fault e non riempire inutilmente la RAM:

    Demand Paging pura: Si carica, sempre, solo la pagina con la prima istruzione e le altre solo a richiesta da parte del processo, quindi dopo una prima serie di page fault il tutto si stabilizza. Non avviene nessun caricamento anticipato (viene caricato solo quel che serve ma produce molti page fault).

    Prefetching di alcune pagine: Il SO decide, in base al comporta-mento in passato del programma, quali pagine caricare all'inizio e il re-sto per richiesta.

  • Come detto prima, per limitare i page fault importante scegliere bene chi spostare eventualmente sul disco.Il criterio ottimale sarebbe quello di scegliere la pagina che verr usata pi tardi nel futuro, ma ci implica una conoscenza del futuro da parte del SO, e ci non possibile, tuttavia esistono degli algoritmi che permettono di ap-prossimare tale criterio.

    5.3.1 Algoritmi per la scelta della vittimaFIFO: Elimino la pagina pi vecchia nella memoria.Non ha nessuna relazione con l'algoritmo ottimo, perch non detto che la pagina caricata prima sia per forza quella pi inutile. Inoltre soffre anche dell'anomalia di Belady, cio quel fenomeno che porta ad avere pi page fault (in media) se si aumenta la cap