Corso di Sistemi Operativi Laurea triennale in Ingegneria Informatica Docente: Enzo Mumolo Obiettivi formativi: L'obiettivo del corso è di fornire una conoscenza dei meccanismi e delle politiche realizzate ad ogni livello di un Sistema Operativo e le principali problematiche relative alla interazione dell’utente con il sistema operativo UNIX/LINUX e della programmazione di sistema in Shell e Perl scripting. Programma del corso: Definizioni di base, servizi di un S.O., il S.O. come rete di code, come gestore risorse, come macchina astratta. Cenni storici; classificazione secondo la organizzazione interna; sistemi monolitici, stratificati, macchina virtuale, client/server. Nozioni di base: risorse, processi, PCB, thread, spooling, stati di un processo, variabili d’ambiente, protezione delle risorse, processi demoni, file system. Processi concorrenti in un ambiente a memoria condivisa. Mutua esclusione, soluzioni software, algoritmo di Peterson, soluzioni hardware, sleep/wakeup, semafori, soluzioni semaforiche di alcuni problemi tipici della concorrenza, alcun problemi codificati in Java. Sincronizzazione, alcun problemi codificati in Java. Stallo, prevenzione, rilevamento e recupero, evitare lo stallo, algoritmo del banchiere, rilevare lo stallo con piu’ risorse e piu’ istanze per risorsa. Gestione dell’unita’ centrale: schedulazione ottima, processi CPU-bound e I/O bound, predizione del tempo di elaborazione, schedulazione prioritaria, schedulazione Round Robin. Gestione memoria contigua e gestione memoria paginata, memoria virtuale. Gestione memoria di massa, alcuni file system. Strutture di Unix: file mode, PID, GID, ID effettivo, ereditarieta’, redirezione, processi in foregroud, background, struttura del file system, Inode, path, directory, protezione dei file, UFDT, tabella dei file. Unix come macchina astratta: comandi utente, shell, editors, link hard e simbolici, espressioni regolari, programmazione in shell, Perl, gestione file in Perl, generazione e gestione processi in Perl, esempi di programmazione, esercitazioni pratiche. Appunti di Sistemi Operativi Enzo Mumolo e-mail address :[email protected] web address :www.units.it/mumolo Indice 1 Introduzione 1.1 1 Struttura di un computer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1.1 Sistema operativo come macchina astratta . . . . . . . . . . . . . . . . . . . . 2 1.1.2 Sistema operativo come gestore di risorse . . . . . . . . . . . . . . . . . . . . 2 1.1.3 Sistema operativo come architettura . . . . . . . . . . . . . . . . . . . . . . . 2 1.2 Servizi e classicazione dei SO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.3 Storia e struttura dei sistemi operativi 4 Sistemi a strati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.4 Il kernel ed i processi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.3.1 1.5 1.6 . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.1 Macchine virtuali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.4.2 Modello client-server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.4.3 Sistemi monolitici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.4.4 Il kernel di Unix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Le risorse 1.5.1 La protezione delle risorse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.5.2 I thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Le strutture fondamentali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.6.1 PCB: Process Control Block . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.6.2 I le . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 1.7 I processi e la Comunicazione tra processi (Inter Process Communicatons o IPC) . . 16 1.8 Lo Spooling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 i Capitolo 1 Introduzione 1.1 Struttura di un computer Figura 1.1 Struttura semplicata di un calcolatore Esistono almeno due modi per poter denire e analizzare un moderno calcolatore: il primo dall'alto verso il basso, il secondo dal basso verso l'alto. Più precisamente: dall'alto verso il basso, ovvero vederlo come macchina astratta (all'utente interessa utilizzare consiste nel guardarlo 1. la macchina senza occuparsi dell'implementazione che c'è sotto); da questo punto di vista viene nascosta la complessità dei livelli sottostanti. 2. dal basso verso l'alto, ovvero come gestore di risorse: ci si pone dal punto di vista dell'hard- ware, ovvero si guarda a come possono essere gestiti i vari dispositivi. Fino ad alcuni anni fa dare una denizione di sistema operativo era tutto sommato facile: era quel particolare strato di software che gestiva l'hardware. Al giorno d'oggi invece è dicile tracciare rmware un solco netto tra HW e SW; si pensi ad esempio al (o microprogramma): esso è un microcodice solitamente allocato in una ROM che serve ad interpretare un insieme di istruzioni ed eseguirle attraverso una sequenza di passi elementari (l'insieme delle istruzioni interpretate denisce il linguaggio macchina): ora questo strato può considerarsi parte integrante dell'hardware. Per- tanto oggi si è più propensi a denire un sistema operativo come un sistema che ore servizi di macchina astratta o di gestione delle risorse. Esistono moltri altri punti di vista dai quali poter analizzare un computer: nel proseguio del architettura. corso verrà considerato anche una terza prospettiva: l' Vediamo ora in dettaglio cosa vuol dire studiare un sistema da questi tre diversi punti di vista. 1 1.2 Servizi e classicazione dei SO 2 1.1.1 Sistema operativo come macchina astratta Signica sostanzialmente studiare il modo in cui l'utente si approccia alla macchina: studiare la shell e i servizi che questa ore, ad esempio: - permette di spostare, copiare e rinominare le di varia natura; √ creare applicazioni: permette (e questo è un aspetto molto importante) di ovvero il sistema operativo crea, attraverso la macchina astratta, un ambiente per creare, eseguire e terminare programmi (o applicazioni). Qui dentro ci sono ad esempio i linguaggi compilati e quelli interpretati (questi ultimi sono i più numerosi: si pensi ad esempio ai linguaggi di shell, l'HTML, il perl. . . ) 1.1.2 Sistema operativo come gestore di risorse Studiare il sistema come gestore di risorse può signicare migliorare le prestazioni della macchina: - Facendo lo scheduling della CPU: quest'ultima infatti deve essere continuamente suddivisa tra i vari lavori. . . - La memoria dev'essere sempre utilizzata al meglio: bisogna evitare il più possibile la frammentazione, bisogna garantire una buona allocazione dei dati. . . - Bisogna gestire bene l'I\O in termini di tempi d'accesso e di adabilità. . . - Bisogna gestire bene le risorse (e si badi che queste non sono solo HW, ma anche SW: si pensi ad esempio ai processi). . . 1.1.3 Sistema operativo come architettura Figura 1.2 Possibile rete di code d'attesa. Dal punto di vista del progettista (ovvero colui che deve operativo può essere visto come una rete di code d'attesa: dimensionare la macchina) il sistema la gura a destra mostra una possibile rappresentazione (estremamente semplicata) delle code che un processore deve essere in grado di gestire in maniera eciente. 1.2 Servizi e classicazione dei SO I servizi fondamentali di un Sistema Operativo sono i seguenti: • Creazione, esecuzione e terminazione dei programmi 1.2 Servizi e classicazione dei SO • Operazioni di Ingresso/uscita • Manipolazione di le • Gestione degli errori: Hardware, I/O, errori di utente • Allocazione delle risorse: CPU, memoria, le, I/O • statistiche nell'uso delle risorse • Protezione e Sicurezza 3 Un sistema operativo può essere classicato in base al suo utilizzo da parte dell'utente: Generale: Batch: Uso secondo il quale non c'è una particolare utilizzazione prevista per i sistema. In questo caso il sistema non ha interazioni con l'utente: ovvero le interazioni con l'utente avvengono attraverso memorie di massa. Ogni processo parte da dati memorizzati esempio su nastro e arriva a dei risultati memorizzati ad esempio su scheda perforata. Interativo: Dedicato: È la shell: ovvero il sistema aspetta i comandi dell'utente. Sistemi progettati per eseguire solo determinate applicazioni (rientrano in questo campo i sistemi embedded, ad esempio, dei cellulari). Transazionali: Sistemi progettati per gestire enormi quantità di dati. Ogni sistema nasce quindi con delle modalità di utilizzo che hanno delle inuenze molto pesanti sul tipo di struttura di cui sarà dotato: non esiste pertanto un sistema che possa soddisfare tutte le diverse applicazioni. 1.3 Storia e struttura dei sistemi operativi 4 1.3 Storia e struttura dei sistemi operativi Un modo per classicare i sistemi operativi è quello di farlo in base alla loro struttura interna. 1.3.1 Sistemi a strati Il primo modo in cui si è iniziato a concepire un sistema operativo è stato mediante delle strutture a livello. Figura 1.3 Struttura onion skin. Questo tipo di struttura è chiamata onion skin (a buccia di cipolla): in questi modelli il sistema operativo è organizzato come una gerarchia a strati, ognuno costruito sopra il precedente; compito di ogni strato è quello di fornire servizi allo strato superiore (per esempio la memoria utilizza servizi che chiede al kernel). Figura 1.4 Interazione ai conni del nucleo Ogni strato interagisce col sottostante attraverso delle opportune interfacce (gura 1.4); questo avviene ad ogni livello: se per esempio la shell ha bisogno del nucleo deve passare diversi conni. Questo comporta che la struttura risulti molto pesante al ne delle prestazioni (infatti l'attraversamento dei vari conni si traduce in elevati tempi di risposta). In precedenza era stato sottolineato che il primo servizio di un sistema operativo è quello di creare, eseguire e terminare processi: questo signica che il sistema operativo deve orire un ambiente per l'esecuzione delle applicazioni che dev'essere il più eciente possibile: pertanto risulta immediato capire che le massime attenzioni saranno rivolte anché l'esecuzione sia più veloce possibile: tutto quello che si contrappone a questa esigenza è chiamato sovraccarico. 1 Ogni sistema operativo possiede un sovraccarico che deve tenere il più basso possibile . In una struttura del tipo onion skin il sovraccarico è massimizzato. Da questo discorso emergono due aspetti fondamentali della progettazione dei sistemi operativi: 1 Ed è anche il motivo per cui si cercano sempre algoritmi il più semplici possibile. 1.4 Il kernel ed i processi L'ecienza 5 (ovvero il sovraccarico); La protezione del sistema: le strutture del sistema operativo devono essere robuste rispetto al- l'esecuzione dei processi. Stiamo parlando ovviamente del caso in cui non ci sia malizia nell'esecuzione dei processi: ad esempio se viene fatto girare un programma bisogna assolutamente evitare che questo (per errori di programmazione) possa in alcun modo modicare alcune strutture del sistema operativo. Diverso è il caso della sicurezza, caso in cui bisogna difendere il sistema operativo da intrusioni esterne con scopi maliziosi. La protezione del sistema si realizza costruendo questi conni in maniera molto precisa: in genere vengono progettati in modo da poter essere attraversati solo con l'uso delle trap); trappole software (le ovvero se mi trovo in un livello e voglio accedere a quello sottostante devo volerlo fare e invoco le trappole: questo serve per evitare problemi accidentali. Ma cosa sono le trappole? Le trappole rappresentano il meccanismo principale attraverso cui si cambia di livello: esse non sono nient'altro che delle interruzioni software che provocano: a. il salvataggio sullo stack dello stato del processore (del program counter); b. il cambiamento della modalità di esecuzione del processore. I processori hanno almeno due modalità di esecuzione: user e system. Non in tutte le modalità si possono eseguire le stesse istruzioni: per esempio se il processore è in modalità user si hanno delle restrizioni su alcune funzioni assembler. Per quanto riguarda la struttura onion skin, questa prevede sei modalità di esecuzione come si può vedere dalla gura ?? e sono quelle consentite dal processore (ogni processore infatti può orire o meno determinate modalità). 1.4 Il kernel ed i processi Il compito fondamentale di ogni sistema operativo é quello di fornire un ambiente adatto per l'esecuzione dei programmi utente, e di fornire agli stessi tutte le caratteristiche richieste. I processi sono descritti mediante strutture dati chiamate in generale PCB, che rappresentano tutte le informazioni dei processi. Tali strutture dati sono accodate in una lista, come rappresentato nella seguente gura: Figura 1.5 Lista dei descrittori dei processi É compito del sistema operativo estrarre un descrittore attraverso gli algoritmi di schedulazione e farli eseguire assegnando loro la CPU. In questo modo, la funzione di un sistema operativo - quella di eseguire processi - puó essere riassunta dallo pseudocodice di g.1.2. Secondo questo schema, il sistema é gestito da una serie di interruzioni che producono la esecuzione a divisione di tempo: ad ogni interruzione del timer lo schedulatore seleziona un descrittore di processo al quale viene assegnata la CPU e che viene rimesso in coda una volta che l'esecuzione é terminata. Questa esecuzione puó essere descritta nella gura 1.3. interfaccia di libreria L'interfaccia per le chiamate di sistema e l' divisione tra i programmi utente ed applicativi ed il livello kernel. rappresentano le linee di L'interfaccia per le chiamate di sistema si occupa di cambiare la modalitá di esecuzione di un processo da modalita' utente 1.4 Il kernel ed i processi 6 Figura 1.6 Pseudocodice di un sistema operativo a divisione di tempo Figura 1.7 Esecuzione dei processi a divisione di tempo (running user mode) a modalita' sistema (running kernel mode) ogni volta che intercetta una chiamata di sistema (system call). Il meccanismo con il quale vengono attivate le chiamate di sistema é illustrato in g.1.2. Una chiamata di sistema genera una interruzione che mette il processore in modalitá sistema (kernel mode) nella quale vengono eseguite le chiamate di sistema. essere di tio software o di tipo hardware. In generale, le interruzioni possono Entrambe sono gestite con il meccanismo illustrato in g.1.2. In particolare, le chiamate di sistema sono prodotte dalle istruzioni 'trap' che producono iterruzioni software. Il kernel oltre a mettere a disposizione le interfacce per le chiamate di sistema possiede anche tutto il software in grado di controllare direttamente l'hardware del calcolatore, la gestione dell'I/O, la gestione del le system e la gestione dei processi. Il sottoinsieme di gestione del le system gestisce i le e la memoria secondaria, alloca lo spazio per i le, amministra lo spazio libero su sottoinsieme di controllo del hardware e' responsabile della gestione delle interruzioni (interrupt) e delle comunastro o disco, controlla l'accesso ai le e ricerca i dati per conto di ogni utente. Il nicazioni con la macchina. I device di I/O a blocchi, cioe' i nastri ed i dischi, possono interrompere la CPU durante l'esecuzione di un processo ed il kernel puo' riavviare il processo interrotto dopo 1.4 Il kernel ed i processi 7 Figura 1.8 Il meccanismo delle chiamate di sistema aver gestito l'interruzione. la Il sottoinsieme di gestione del le system puo' accedere ai dati contenuti nei le utilizzando buer cache cioe' un meccanismo di buering, che regola il usso dei dati tra il kernel ed i device di I/O a blocchi, cioe' i nastri ed i dischi. Il meccanismo di buering interagisce infatti con sottoinsieme di gestione del le system puo' interagire anche direttamente con i device di I/O a caratteri, i device di I/O a blocchi per avviare il trasferimento dei dati da e verso il kernel. Il cioe' le stampanti, lo schermo video del terminale, la tastiera del terminale e i dispositivi di rete, senza l'intervento di alcun meccanismo di buering. I device driver sono memorizzati, come gia' osservato, sempre nel direttorio /dev e quindi sono esterni al kernel. Il modulo dei processi di I/O e' un'istanza dei device driver in esecuzione e quindi controlla l'operato dei device di I/O. I processi interagiscono con il sottoinsieme di gestione del le system per mezzo di un insieme di particolari chiamate di sistema, come open( ) che apre un le in lettura oppure in scrittura, close( ) che chiude un le aperto, read( ) che legge da un le aperto, write( ) che scrive in un le aperto, stat( ) che richiede gli attributi di un le, chown( ) che cambia l'utente proprietario di un le e chmod( ) che cambia i permessi di accesso ad un le. Il sottoinsieme del kernel di gestione dei processi risulta diviso in tre moduli. Il modulo di schedulazione detto anche scheduler della CPU che decide a quali dei processi pronti in memoria centrale bisogna assegnare il controllo della CPU. Lo scheduler della CPU gestisce quindi la schedulazione dei processi in modo da eseguirli a turno nche' lasciano volontariamente libera la CPU in attesa di una risorsa oppure il kernel decide che il tempo loro concesso e' gia' passato. Lo scheduler della CPU scegliera' allora il processo a priorita' piu' alta e lo fara' eseguire. Il processo appena interrotto ripartira' invece quando tornera' ad essere il processo di priorita' piu' alta. L'algoritmo realizzato dallo scheduler della CPU e' comunque fortemente inuenzato dalle tecniche di gestione della memoria. Il modulo di comunicazione tra processi si occupa invece della sincronizzazione dei processi, della comunicazione tra i processi e della comunicazione tra i processi e la memoria centrale. Ci sono diverse forme di comunicazione tra i processi che vanno dalla segnalazione asincrona degli eventi alla trasmissione sincrona di messaggi tra i processi. Una trasmissione asincrona non aspetta un messagio di conferma, mentre una trasmissione sincrona attende sempre un messagio di conferma. Il modulo della gestione della memoria si occupa