• Non ci sono risultati.

SimulatorediCPU.SviluppodiunSistemaOperativosemplice:interazioneSistemaOperativo-Interruzioni Tesidilaurea

N/A
N/A
Protected

Academic year: 2021

Condividi "SimulatorediCPU.SviluppodiunSistemaOperativosemplice:interazioneSistemaOperativo-Interruzioni Tesidilaurea"

Copied!
127
0
0

Testo completo

(1)

UNIVERSITÀ DEGLI STUDI DI UDINE

Facoltà di Ingegneria

Corso di laurea in Ingegneria Elettronica

Tesi di laurea

Simulatore di CPU.

Sviluppo di un Sistema Operativo semplice: interazione Sistema

Operativo - Interruzioni

Relatore: Laureando:

Prof. Pier Luca Montessoro Dario Clocchiatti Correlatore:

Prof. Mirko Loghi

Anno Accademico 2009-2010

(2)
(3)

Sommario

La seguente trattazione si propone lo scopo di analizzare le problematiche dello sviluppo di un sistema operativo minimale in time sharing su piattaforma Sim- CPU con particolare riguardo nei confronti dell'interazione tra l'architettura e i dispositivi di input e di output. L'interazione viene realizzata mediante un sistema di gestione delle interruzioni e un sistema di gestione di trap e queste ul- time sono aancate dalla simulazione su thread paralleli dei principali dispositivi hardware esterni quali tastiera, orologio di temporizzazione e monitor. Il progetto intende orire inoltre un supporto didattico per quanto concerne lo studio delle principali problematiche riguardanti i sistemi operativi e il loro funzionamento.

iii

(4)
(5)

Indice

Introduzione 1

1 SimCPU - versione didattica 3

1.1 Macrostruttura di SimCPU . . . . 3

1.1.1 Struttura dei programmi . . . . 4

1.1.2 Assemblatore . . . . 5

1.1.3 SimCPU . . . . 6

1.2 Microstruttura di SimCPU . . . . 6

1.2.1 simcpu.h . . . . 6

1.2.2 executor.c e user_interface.c . . . . 7

1.2.2.1 user_intreface.c . . . . 8

1.2.2.2 executor.c . . . . 9

1.3 Metodologie di input e di output . . . 10

2 SimOS - generalità sui sistemi operativi 13 2.1 Denizione . . . 13

2.2 Caratteristiche . . . 13

2.2.1 Multiprogrammazione e multitasking . . . 13

2.2.2 Modalità kernel e modalità utente . . . 14

2.2.3 Gestione della memoria . . . 14

2.2.4 Gestione dei dispositivi . . . 15

2.2.4.1 Comunicazione Dispositivi a SO - Interruzioni . . 16

2.2.4.2 Comunicazione Utente a Dispositivi - Trap . . . . 17

2.2.5 Gestore della memoria secondaria . . . 17

2.3 Processi . . . 18

2.4 Booting, shell e sistema dinamico . . . 19

3 SimCPU - implementazione hardware 21 3.1 Macrostruttura di SimCPU - versione base evoluta . . . 21

3.1.1 Registri speciali, ag e istruzioni kernel . . . 22

3.1.2 MMU e paginazione . . . 23

3.1.3 Return Address . . . 25

3.1.4 Stack Pointer . . . 25

3.1.5 Interruzioni . . . 26

3.2 Macrostruttura di SimCPU - nuova versione . . . 26

3.2.1 Nuove metodologie di input e di output . . . 26

v

(6)

3.2.2 Nuova gestione dei ags . . . 29

3.2.3 Implementazione del sistema delle interruzioni . . . 29

3.2.4 Chiamate di sistema e trap . . . 31

3.2.5 Registri speciali . . . 33

3.2.6 Introduzione di nuovi comandi di simulazione . . . 34

3.3 Analisi del codice sorgente . . . 35

3.3.1 Implementazione del sistema di input e output . . . 36

3.3.1.1 Threads . . . 36

3.3.1.2 Semafori . . . 36

3.3.1.3 Inizializzazione dei dispositivi di input e di output 37 3.3.1.4 Monitor ed istruzioni di output . . . 39

3.3.1.5 Tastiera ed istruzioni di input . . . 42

3.3.1.6 timer (clock) . . . 51

3.3.2 Modiche generali . . . 53

3.3.2.1 Protezione del buer video . . . 53

3.3.2.2 Argomento NOTRACE per le funzioni più utilizzate 53 3.3.2.3 Nuova congurazione per le subroutine di lettura e scrittura dei ag . . . 54

3.3.2.4 Modalità di stampa a video per il thread del monitor 55 3.4 Sviluppi futuri . . . 56

4 SimOS - implementazione software 57 4.1 Il modello statico . . . 57

4.1.1 Tabelle delle pagine . . . 58

4.1.2 PCB . . . 59

4.1.3 Numero massimo di programmi . . . 59

4.1.4 Mappatura del kernel . . . 60

4.1.5 Mappatura dei programmi utente . . . 60

4.1.6 Stack . . . 60

4.2 Analisi del codice . . . 61

4.2.1 Scheduler . . . 61

4.2.2 Boot . . . 62

4.2.3 Routine di interruzione . . . 62

4.2.3.1 Interrupt Test . . . 62

4.2.3.2 Codice Assembly per la Routine di Interrupt . . . 65

4.2.4 Routine di trap . . . 74

4.2.4.1 L'istruzione di Trap . . . 74

4.2.4.2 Chiamata alla funzione error_trap() . . . 76

4.2.4.3 Gestione degli errori . . . 78

4.2.4.4 Routine di Trap . . . 79

4.3 Sviluppi futuri . . . 89

5 SimCPU & SimOS - testing 91 5.1 Considerazioni pre-simulazione . . . 91

5.1.1 Breakpoint . . . 92

5.2 Test trasparenza e di codice rientrante per trap ed interruzioni . . 92

(7)

INDICE vii

5.3 Test dei codici d'errore . . . 96 5.4 Test di scheduling . . . 99 5.5 Test della tastiera - programma echo . . . 100

Conclusioni 103

A Ambiente Cygwin 105

B Codice getkey.c 107

C Esecuzione dicotomica di SimCPU 111

D Comando Step-in-Kernel 113

Bibliograa 119

(8)
(9)

Introduzione

Il simulatore SimCPU, fornisce agli studenti un ecace modello didattico orienta- to alla simulazione dei principali meccanismi di funzionamento di un processore.

Il ne didattico di SimCPU si compie nel momento in cui lo studente viene sensi- bilizzato sui principi basilari, seppur volutamente trattati in maniera semplicata, sui quali si fonda l'architettura dei calcolatori e la programmazione a basso livello.

La denizione di un Instruction Set funzionalmente completo, nonché l'individua- zione delle fasi di elaborazione del codice macchina sono solo due tra gli esempi più importanti di quali siano gli aspetti su cui SimCPU ponga l'accento.

L'evoluzione temporale e la particolarizzazione di un software didattico quale quello di SimCPU volge naturalmente nella direzione di uno sviluppo prettamente

verticale dello stesso che prevede la posa uno sull'altro di vari livelli di astra- zione. Il livello più basso di astrazione è quello che viene denominato Livello logico digitale il quale fornisce una descrizione del sistema dal punto di vista delle porte logiche impiegate, dell'ampiezza e della dierenziazione dei bus e del- l'organizzazione delle memorie. Su di esso si appoggia il Livello di architettura

che denisce invece i formati delle istruzioni, i registri, i tipi di dati, i modi di indirizzamento, i tipi di istruzioni e le fasi di esecuzione delle stesse: SimCPU è il risultato della denizione e della simulazione del livello di architettura che si era interessati ad implementare. Lo scopo della presente tesi è quello di gettare le basi per la realizzazione del successivo livello di astrazione, il cosiddetto Livello macchina del sistema operativo.

Il Sistema Operativo, nella sua accezione più immediata, è un particolare programma che si occupa di rendere semplice e trasparente l'interazione tra i dispositivi hardware e l'utente che, mediante l'esecuzione dei suoi programmi, vuole potervi accedere per sfruttare le capacità di calcolo della CPU a scopi personali.

Questa semplicistica denizione comporta in realtà un numero enorme di con- seguenze, soprattutto se si considerano le primordiali congurazioni del simula- tore. L'obiettivo dello sviluppo di un sistema operativo funzionante a partire da quest'ultima, ha necessitato un numero notevole di modiche, tra le quali l'am- pliamento della memoria sica, l'implementazione del sistema di paginazione, l'introduzione di thread paralleli di simulazione dei dispositivi hardware di input e di output, l'ampliamento dell'Instruction Set, l'introduzione dell'inter- rupt test e del buer dei dispositivi, la scrittura di codice per la schedulazione dei programmi, la gestione delle interruzioni, delle trap e molte altre. Alcune di queste modiche erano già state implementate in versioni parallele di SimC-

1

(10)

PU, sviluppate in altre tesi in maniera del tutto indipendente l'una dall'altra; le restanti, tra le quali il pervenimento ad una versione funzionante di un sistema operativo, sono l'oggetto di due tesi svolte in stretta collaborazione e di cui una è la presente.

Lo sviluppo di un sistema operativo comporta dunque una complicazione dei meccanismi sui quali l'architettura stessa che lo supporta si basa. Tuttavia, nono- stante l'inasprimento e la particolarizzazione dei meccanismi, il ne didattico deve rimanere consistente: il sistema operativo deve esistere solamente nella misura in cui esso può essere facilmente compreso dagli studenti in sede di corso. Per garan- tire tale obiettivo sono stati introdotti ulteriori comandi di visualizzazione di aree di memoria di particolare interesse, oltre a modalità di funzionamento coerenti con un'esecuzione istruzione-per-istruzione: prerogativa principale di debugging.

La missione di SimOS (Simulated Operative System), così è stato chiamato il sistema operativo simulato, è dunque quella di orire un piccolo spunto di studio agli studenti, nonché una possibilità di comprensione nei confronti delle principali problematiche di funzionamento di un semplice sistema operativo su architettura simulata.

Nella prima parte dell'elaborato verrà arontata l'analisi del codice sorgente della versione didattica SimCPU al ne di acquisire la dimestichezza necessaria per apportare con sempre maggiore facilità, modiche via via più complicate ed importanti al codice. In seguito si valuteranno i principi di funzionamento e le caratteristiche speciche possedute da un sistema operativo, cercando di capire nel frattempo i vincoli di complessità entro i quali SimOS può essere realizzato e concluso. Verrà in seguito presentata la versione base evoluta del simulatore e su di essa verranno studiate e realizzate le modiche hardware proposte. Su questa nuova architettura verrà costruito inne il sistema operativo vero e proprio, ne verranno analizzate le routine di servizio e le metodologie di funzionamento e a suggello della bontà del lavoro realizzato ne verrà testato il corretto e robusto funzionamento.

Nell'elaborato si farà riferimento a due versioni di SimCPU, quella didattica, attualmente utilizzata nei corsi di architettura dei calcolatori, e quella base evolu- ta, costituita dall'unione di due modiche separatamente apportate alla versione didattica e riguardanti l'introduzione del dispositivo di traduzione degli indirizzi e della linea di interrupt.

(11)

Capitolo 1

SimCPU - versione didattica

Lo sviluppo di un sistema operativo è strettamente correlato ai meccanismi di funzionamento dell'architettura per la quale esso dovrà venire progettato. Ciò implica sostanzialmente uno studio profondo dei principi sui quali l'architettura stessa si basa, prerogativa che in questo caso si tradurrà in un'opera di profonda analisi del codice sorgente di SimCPU. In questo capitolo verrà arontata pro- prio questa problematica: si prenderà in considerazione la versione didattica di SimCPU (quella attualmente utilizzata nei corsi di architettura dei calcolatori), e di essa ne verranno individuate le routine e le caratteristiche fondamentali per iniziare a comprendere come orientarsi all'interno del codice.

1.1 Macrostruttura di SimCPU

Il pacchetto di sviluppo, esecuzione e debugging di codice in linguaggio assembly si presenta come costituito da due programmi, l'assemblatore e il le eseguibile di SimCPU. L'iter di creazione ed esecuzione di un programma prevede inizialmen- te la stesura di un codice in linguaggio assembly (mediante i più comuni editor di testo) con estensione *.axx. Questo codice deve venire in seguito compilato mediante l'assemblatore e tradotto dunque in linguaggio macchina. Il le risulta- to della compliazione corrisponderà al le eseguibile da SimCPU con estensione

*.exx. Esso conterrà la mappatura della memoria e l'informazione inerente l'in- dirizzo di partenza (starting address), ovvero l'indirizzo di memoria dal quale eseguire il fetch della prima istruzione del programma. Il modo più semplice per comprendere la struttura dei programmi .axx e dei rispettivi le eseguibili .exx è riportare un esempio semplice. Si eviterà la descrizione semantica di ogni singo- la istruzione in quanto questa fase viene ampiamente coperta dal relativo corso di architettura dei calcolatori. Ci si soermerà invece sull'utilizzo e la disposi- zione di una terna di elementi che assumeranno molta importanza nel proseguo dell'elaborato: le direttive, lo starting address e le label.

3

(12)

1.1.1 Struttura dei programmi

Il codice in linguaggio assembly che segue è un esempio di programma compilabile.

Tralasciando il signicato del codice e delle singole istruzioni che in questo caso è del tutto insignicante, quello che in questo esempio preme di più far mostrare è il grado di disordine che un programma può assumere rimanendo al contempo compilabile e perfettamente funzionante.

DATA: word 0000 LOOP: LDWI R0 10

MV R0 R1 JMP START CONT: word 0FFFF START: JMP LOOP NUMERO: byte 03

In particolare in questo codice si riconoscono:

DIRETTIVE: come byte e word, rappresentano il metodo per specicare dati all'interno del codice.

LABEL: (etichette) ovvero identicativi di indirizzi in memoria quali DA- TA, LOOP, CONT, START, NUMERO. Alcune, come DATA e NUMERO individuano dati, altre come LOOP, CONT e START identicano degli indirizzi specici del codice ai quali è possibile fare riferimento. La label START, inne, deve essere sempre presente all'interno di ogni programma perché individua lo starting address, ovvero l'indirizzo di partenza dal quale SimCPU dovrà eseguire la prima istruzione.

ISTRUZIONI: Non ci si dilungherà molto su questo aspetto, le istruzioni presenti in questo esempio sono LDWI, MV e JMP. Più avanti nel testo ver- ranno date delle descrizioni speciche di nuove istruzioni introdotte, nonché di alcune già presenti ma molto importanti.

Istruzioni, label e direttive, combinate opportunamente, contribuiscono a formare il programma nale in linguaggio assembly non compilato. Da notare che non c'è nessun vincolo sulla posizione relativa delle direttive rispetto alle istruzioni o della label di START. Le direttive (e quindi in sostanza la denizione dei dati) sono posizionabili in qualsiasi punto del programma, a patto che il usso di ese- cuzione non interceda nella loro area di memoria: in questo caso si otterrebbe come eetto il fetch di un dato (e non di un'istruzione) che nella maggior parte dei casi causerebbe un errore di OPCODE.

Il programma compilato in linguaggio macchina è interessante in quanto è rap- presentativo della mappatura della memoria e del funzionamento dello starting address.

000C00 | DATA: word 0000

(13)

1.1. MACROSTRUTTURA DI SIMCPU 5

00 |00 | LOOP: LDWI R0 10 10 |10 |

00 |01 | MV R0 R1 04 |02 | JMP START C3 |FF | CONT: word 0FFFF FF |F4 | START: JMP LOOP C3 |03 | NUMERO: byte 03

L'indirizzo di starting address è indicato nella prima riga del codice macchina.

All'indirizzo 000Ch è associata la label START. SimCPU caricherà in memoria la mappatura della stessa e leggerà lo starting address ottenendo l'indirizzo della memoria stessa dal quale eseguire la sua prima istruzione: in questo caso JMP LOOP.

All'indirizzo 0000h della memoria è presente il byte 00 che rappresenta il byte meno signicativo della direttiva word referenziata mediante l'etichetta DATA.

1.1.2 Assemblatore

L'assemblatore è il programma che consente la traduzione del codice scritto in linguaggio assembly nel relativo linguaggio macchina direttamente eseguibile da SimCPU. L'assemblatore riceve in ingresso il nome (senza estensione .axx) del programma scritto dall'utente e genera in uscita un le contenente la traduzione in linguaggio macchina (estensione .exx) del codice ricevuto in ingresso con l'ag- giunta nella prima riga dello starting address. Lo starting address, infatti, non è necessariamente la prima locazione di memoria, SimCPU deve preventivamente conoscere la locazione di memoria alla quale saltare in quanto in essa è presente la prima istruzione da eseguire.

Il codice sorgente dell'Assemblatore è costituito dal le di header simcpu.h, che verrà descritto più avanti nella trattazione e dal le assembler.c. Lo sviluppo del sistema operativo, ed in particolare della struttura hardware di SimCPU, ci ha condotto a modicare anche l'assemblatore. Da un lato, infatti, ogni introduzione di nuove istruzioni (con opcode già esistente), seppur richiedesse solamente la modica del le di header (come si vedrà tra poco), necessita ovviamente di una nuova compilazione dell'assemblatore. Dall'altro, l'introduzione dei registri speciali, ci ha condotto a referenziare gli stessi all'interno del linguaggio assembly mediante etichette speciche per ognuno di essi. L'introduzione di un nuovo OPCODE, che si è reso necessario per la denizione di una nuova istruzione, ha invece comportato la modica diretta del le assembler.c.

(14)

1.1.3 SimCPU

Il programma simcpu.exe rappresenta il simulatore di CPU vero e proprio. Il codice sorgente dell'attuale versione di SimCPU usata nella didattica del corso di architettura dei calcolatori è formato dall'header simcpu.h e dai le executor.c e user_interface.c. Comprendere le funzioni e la struttura di questo programma è di fondamentale importanza per capire come e dove modicare il codice quando ciò si renderà necessario. Nelle prossime sottosezioni verranno analizzate in ma- niera particolareggiata le caratteristiche dei vari codici sorgente ma a grandi linee il principio di funzionamento di SimCPU è il seguente. In user_interface.c è pre- sente la funzione main(), la quale, dopo essersi occupata delle consuete operazioni necessarie all'apertura e alla gestione del le contenente il linguaggio macchina, richiama le funzioni di inizializzazione della CPU che caricano il programma in memoria e recuperano lo starting address. In seguito viene chiamata la funzione user_control() che gestisce l'interfacciamento tra il funzionamento della CPU e l'utente attraverso specici comandi di visualizzazione delle risorse, debugging e run. Ogni run comporta l'esecuzione di almeno un ciclo macchina, richiamando la funzione executor() presente in executor.c alla quale sono demandati i compiti di fetching ed esecuzione dell'istruzione.

1.2 Microstruttura di SimCPU

In questa sezione si esamineranno le funzioni principali del codice sorgente che compone la versione base di SimCPU. Si partirà dall'header e si analizzerà in seguito l'executor.c e l'user_interface.c mettendo in evidenza gli aspetti che verranno utilizzati maggiormente nel proseguo della trattazione.

1.2.1 simcpu.h

simcpu.h è il le di header che contiene la maggior parte delle più comuni deni- zioni tra costanti, parametri, macro e strutture dati.

Tra le costanti si individuano:

PARALLELISM 16UL - Identica il parallelismo della macchina nonché lo spazio di memoria indirizzabile (65536 byte), la grandezza di una word (16 bit = 2 byte)

NREGISTER 16UL - Strettamente legato al parallelismo, individua il nu- mero di registri ad accesso diretto della ALU

NINSTRUCITONS 42 - Numero di istruzioni dell'Instruction Set in utilizzo

ENTRY POINT START - Identicativo per l'individuazione dello starting address

(15)

1.2. MICROSTRUTTURA DI SIMCPU 7

Tra i parametri si ricordano i tre trace_mode, che discriminano le modalità di esecuzione di un'istruzione in funzione del grado di debugging o controllo che si vuole mantenere nei confronti dell'esecuzione delle istruzioni:

TRACE_MODE_DISABLED 0

TRACE_MODE_INSTRUCTION 1

TRACE_MODE_EXTENDED 2 Tra le macro, importantissime sono:

OPCODE - Dato l'instruction register, questa macro restituisce l'opcode dell'istruzione

FIRST_REGISTER/SECOND_REGISTER - Dato l'instruction register e un'istruzione che opera sui registri, questa macro restituisce l'indice del primo/secondo registro caratteristico dell'istruzione appena eseguita.

OFFSET - Dato l'instruction register e un'istruzione di salto, questa macro restituisce l'oset da sommare al program counter per eettuare il salto.

Si giunge inne alle strutture dati tra le quali spicca indubbiamente machi- ne_language, un array di strutture ciascuna delle quali rappresenta una tra le istruzioni facenti parte dell'Instruction Set.

In simcpu.h è denita anche la struttura dati recante le direttive, nonché la denizione di tutti gli opcode per ogni istruzione.

1.2.2 executor.c e user_interface.c

Questi due le sono strettamente legati tra loro sebbene siano concettualmente molto dierenti l'uno dall'altro e espletino funzioni ortogonali.

Il listato executor.c rappresenta il cuore della CPU, ovvero l'eettivo iter di esecuzione delle singole istruzioni: la struttura del programma è di per sé molto semplice, nonostante le numerose righe di codice che lo compongono. L'inizializ- zazione della macchina consta in tre fasi ben distinte, individuate da altrettante funzioni.

Apertura dei les in ingresso - funzione main() in user_interface.c

Caricamento in memoria principale del programma - funzione loader() in executor.c

Inizializzazione dello stato della CPU - funzione initialize_executor() in executor.c

L'esecuzione passa dunque nuovamente alla funzione main() che a questo pun- to, dopo l'inizializzazione della CPU chiama la funzione di gestione della stessa:

user_control() che rappresenta l'interfaccia tra l'utente e la gestione/visualizza- zione delle risorse della CPU. L'utente può decidere le modalità di esecuzione

(16)

delle prossime istruzioni in maniera da poter controllare completamente il usso di esecuzione, soermandosi sui punti critici del programma e controllandone il corretto funzionamento mediante le funzioni di visualizzazione della memoria e della CPU, oltre che alle modalità di debugging quali trace_mode_instruction e trace_mode_extended. Qualora l'utente decidesse di eseguire la successiva istruzione viene chiamata la funzione executor() risiedente in executor.c. Tale funzione rappresenta l'iter di esecuzione del ciclo macchina della CPU, istruzione per istruzione. Si esamina ora nello specico le caratteristiche dei due listati di codice.

1.2.2.1 user_intreface.c

Considerato che lo sviluppo del sistema operativo ha comportato la necessità di adeguare gli strumenti di debugging alla sempre più crescente complessità del sistema introducendo nuovi comandi per la user_control(), si ritiene opportuno analizzare almeno grossolanamente sia il sistema di gestione dei comandi, sia i principali comandi del simulatore.

La denizione dei comandi disponibili è rappresentata nel relativo array di strutture denominato user_interface_commands. Il sistema di riconoscimento/e- strapolazione dei comandi digitati opera mediante TOKEN identicativi di ogni comando. La funzione get_command() restituisce il TOKEN relativo al comando individuato tra le opzioni a disposizione e in base a questo il codice determina quali operazioni svolgere.

Tra i comandi più importanti si ricordano run, set breakpoint, set tra- ce_mode e display.

RUN: Le modalità di run per l'esecuzione delle istruzioni sono due.

 FOREVER: si limita semplicemente a chiamare la funzione executor() presente in executor.c. Tale funzione entra in un while innito che esegue una istruzione dietro l'altra n quando non vi esce per qualche ragione. Tutte le modalità di uscita verranno esaminate quando sarà stato valutato più a fondo il codice executor.c, per ora basti sapere che la variabile globale single_step permette l'uscita dalla funzione se settata ad uno. Single_step è normalmente nulla e dunque l'esecuzione iterata di ogni istruzione viene eettuata normalmente senza settare la variabile globale a zero.

 NEXT: prima di chiamare executor, viene settata ad uno la variabile single_step. Dopo l'esecuzione della successiva istruzione, la funzio- ne executor() ritorna ad user_control(). Run next esegue dunque solamente una istruzione.

SET BREAKPOINT: Un altro utilissimo metodo per bloccare l'esecuzione della funzione executor(), è quella di settare un breakpoint ad una certa locazione di memoria. Ad ogni ciclo di esecuzione, alla ne dell'instruc- tion execute e prima dell'instruction fetch dell'istruzione successiva, viene

(17)

1.2. MICROSTRUTTURA DI SIMCPU 9

eseguito un controllo sia sul valore della variabile di single_step sia sulla presenza o meno di un breakpoint a quella locazione di memoria, in caso positivo, la funzione execute() ritorna.

SET TRACE_MODE: Esistono tre diverse modalità di esecuzione di una istruzione in funzione del grado di visualizzazione della traccia lasciata dalle istruzioni eseguite dalla CPU.

 TRACE_MODE_INSTRUCTION: In seguito all'esecuzione di una istruzione viene stampata a video l'area di memoria (compresa di commenti e di istruzioni) del programma exx in esecuzione.

 TRACE_MODE_EXTENDED: Ogni fase di esecuzione di ogni istru- zione lascia traccia (a video) del suo operato.

 TRACE_MODE_DISABLED: Non vi è nessun ritorno a video del tipo di istruzione eseguita, ne delle operazioni svolte.

DISPLAY: Permette la visualizzazione di particolari aree di memoria, non- ché di risorse importanti quali ad esempio lo stato della CPU.

1.2.2.2 executor.c

Come già accennato, la funzione executor() all'interno di executor.c è la princi- pale funzione del simulatore. Ad essa si demanda l'esecuzione vera e propria di ogni istruzione. Nella versione di SimCPU che è stata descritta nora, le fasi di esecuzione di una istruzione sono tre:

INSTRUCTION FETCH: L'istruzione viene prelevata dalla memoria e mes- sa nell'instruction register

INSTRUCTION DECODE: L'istruzione, in base al suo opcode viene iden- ticata

INSTRUCTION EXECUTE: L'istruzione viene eettivamente eseguita esse sono direttamente individuabili all'interno del codice della funzione execute(), anche grazie ai commenti presenti.

Le modalità di uscita dalla funzione executor(), sono dunque tre, due delle quali sono già state esaminate:

SINGLE_STEP=1

BREAKPOINT attivato alla locazione di memoria alla quale si trova il program counter

HLT: l'istruzione di halt conclude i programmi e permette di ritornare alla funzione chiamante user_control(), l'uscita eettiva da SimCPU avviene mediante i consueti comandi di quit o di exit.

(18)

L'esecuzione delle istruzioni viene implementata da funzioni di supporto per ga- rantire una maggiore riutilizzabilità del codice. Si possono riconoscere gruppi di funzioni che svolgono determinate operazioni, tra le quali:

funzioni demandate al settaggio o alla lettura dei ag del sistema che in questa versione si presentano come variabili globali;

funzioni impiegate per la lettura o la scrittura dei registri;

funzioni per l'accesso alla memoria in lettura e in scrittura;

funzioni per la realizzazione delle operazioni di input e di output;

Con le nuove versioni di SimCPU gran parte di queste funzioni sono state am- piamente modicate, con particolare riferimento per le istruzioni di lettura e di scrittura in memoria: l'implementazione della paginazione e quella della TLB (Translation Lookaside Buer) ha portato infatti ad una loro profonda revisione.

Numerosi altri aspetti sono stati rivisti sia in questo contesto che in virtù della futura implementazione del sistema operativo.

1.3 Metodologie di input e di output

Nella versione didattica di SimCPU, come anche nella versione base evoluta dello stesso, le operazioni di input e di output vengono simulate da e verso gli opportuni terminali mediante un sistema identicabile come polling, con bit di completa- mento dell'operazione. In realtà il concetto che sta alla base di questa scelta per la gestione degli input e degli output è dovuta al fatto che i dispositivi hardware, nonché le aree di memoria condivisa per la comunicazione con questi ultimi, non sono eettivamente simulate. A seguito di una istruzione i OUTX (B o W), il codice hardware si limita ad eseguire una printf sul terminale video, mentre in teoria dovrebbe poter impartire al dispositivo di output l'operazione da eseguire:

sarà poi il dispositivo hardware a eseguire la printf sul canale video.

Questa dierenziazione è fondamentale ai ni pratici: non essendo presente un canale di comunicazione tra il dispositivo hardware e la CPU, deve esistere un metodo con il quale il presunto dispositivo esterno informa la CPU dell'avvenuta conclusione della stampa (o in maniera speculare della presenza di un carattere disponibile nel buer della tastiera). Il procedimento mediante il quale la CPU viene a conoscenza del completamento dell'operazione è implementato mediante un particolare ag detto ag di completamento o completion_ag, presente sia per l'input che per l'output. A ne stampa o a conclusione dell'inserimento di almeno un carattere nel buer della tastiera, questo ag viene attivato. Com- pito della CPU, ed in particolare del linguaggio macchina, è quello di mettere a disposizione del programmatore una o più istruzioni in grado di leggere questo particolare ag. Le istruzioni alle quali si fa riferimento, presenti solo nella ver- sione didattica e nella versione base evoluta di SimCPU, sono denominate TSTI e TSTO e vengono utilizzate per realizzare un opportuno codice in grado di testare

(19)

1.3. METODOLOGIE DI INPUT E DI OUTPUT 11

in continuazione i ag di completamento con meccanismi di looping ampiamente noti dal corso di architettura dei calcolatori.

La struttura CPU-I\O in queste versioni di SimCPU è rappresentabile in uno schema secondo il quale la CPU comunica col Mondo Esterno [Fig. 1.1] inviando richieste di input o di output mediante le proprie istruzioni;

Figura 1.1: Modello della comunicazione nella versione didattica di SimCPU

il Mondo Esterno risponde alla CPU attraverso il bit di completamento della richiesta.

La nuova congurazione dell'input e dell'output di SimCPU stravolge questo concetto, portando direttamente all'eliminazione delle istruzioni di controllo dei

ag di completamento (nonché ai relativi ag) e alla revisione completa delle istruzioni di input e di output.

A titolo di completezza, è opportuno eettuare una breve descrizione sulla strut- tura buer sulla quale si appoggia il dispositivo di ingresso standard (tastiera) e il metodo di interfacciamento tra quest'ultimo e l'architettura.

Il dispositivo di ingresso è modellato mediante un array di elementi (byte) denominato input_buer[] e le operazioni di lettura dal dispositivo di ingresso mediante istruzioni, vengono eettuate in quest'area di memoria. L'unico metodo di accesso in scrittura all'input_buer[] è quello regolato dal comando di SimCPU

Load Input $ASCII che vi carica la stringa ASCII che segue il carattere riservato

$ (incluso il carattere '\n' e il carattere '\0'). Le operazioni di lettura eseguite dall'istruzione INB (INW non è stata abilitata perché la lettura di una word non è ancora stata prevista in quanto il dispositivo si suppone orientato a carattere) comportano un progressivo svuotamento del buer che viene realizzato attraverso l'avanzamento dell'indice di inizio stringa input_index; le letture sono permesse

no a quando non viene letto il carattere '\0' di terminazione stringa. Di fatti, ad ogni caricamento, la variabile input_index viene portata a zero e a seguito di ogni lettura, tale variabile viene incrementata di una unità. Il buer si considera vuoto quando input_index è tale da essersi portato a puntare al carattere di terminazione della stringa '\0'.

Nel proseguo della trattazione, la simulazione del dispositivo della tastiera, vedrà la necessità di rivedere questa trattazione del buer di ingresso, che a quel punto verrà sostituito da un buer di tipo circolare.

(20)
(21)

Capitolo 2

SimOS - generalità sui sistemi operativi

In questo capitolo si provvederà a descrivere a grandi linee i principi di fun- zionamento di un sistema operativo cercando nel contempo di individuare sin dall'inizio caratteristiche e limiti della versione da implementare, sulla base di vincoli di dicoltà e tempistiche di implementazione. La buona conoscenza del codice sorgente della versione base di SimCPU acquisita nel capitolo preceden- te, permetterà di vagliare in maniera quasi immediata necessità e possibilità di modica e ampliamento del codice.

2.1 Denizione

Il ruolo svolto dal sistema operativo è sostanzialmente quello di fornire un am- biente nel quale i programmi utente possono lavorare in modo utile. Il concetto di utilità appena richiamato implica automaticamente che i vari programmi utente devono poter accedere ai dispositivi hardware esterni senza avere alcuna possibilità di eseguire operazioni dannose. Il sistema operativo fornisce dunque una serie di funzioni che permettono al programma utente di operare sui disposi- tivi in maniera protetta. Compito del sistema operativo (o kernel), dunque, è la realizzazione del controllo dell'avvicendamento dei programmi utente nella CPU, garantendo al contempo un'interfaccia funzionale tra questi ultimi e i dispositivi di input e di output.

2.2 Caratteristiche

2.2.1 Multiprogrammazione e multitasking

Riprendendo la denizione dei sistemi operativi proposta nella sezione precedente, un sistema operativo garantisce un ambiente per l'esecuzione utile dei program- mi utente. Da qui si deduce immediatamente una caratteristica fondamentale dei sistemi operativi: la multiprogrammazione, ovvero la presenza di più program- mi in aree dierenti di memoria sica nello stesso momento e la loro esecuzione in

13

(22)

maniera lineare. Questo concetto di esecuzione e avvicendamento dei programmi nella CPU nuovo per SimCPU soprattutto se si fa riferimento alla versione di- dattica, dove la CPU veniva impegnata con solamente un programma alla volta.

La multiprogrammazione nasce dalla necessità di occupare in maniera eciente l'utilizzo della CPU, organizzando i lavori in maniera da mantenerla in continua attività. Una logica estensione della multiprogrammazione è il concetto di mul- titasking o sistema time sharing: la CPU esegue i programmi commutando le loro esecuzioni con una frequenza tale da permettere all'utente di non accor- gersi di tale cambiamento, ciò comporta automaticamente l'impressione che più programmi siano in esecuzione nello stesso istante. L'operazione di avvicenda- mento dei processi nella CPU è estremamente delicato e viene eseguito da una o più routine del sistema operativo che concorrono alla costituzione del cosiddetto scheduler.

2.2.2 Modalità kernel e modalità utente

Per garantire il corretto funzionamento del sistema, è necessario distinguere tra l'esecuzione di codice di sistema operativo e di codice denito dall'utente. Sono pertanto necessarie almeno due dierenti modalità: la modalità utente e la mo- dalità kernel. Per distinguere quale sia la modalità attiva, l'architettura della CPU deve essere dotata di un bit (ag) di modalità chiamato ag di kernel mo- de. La modalità kernel viene abilitata in fase di booting e durante l'esecuzione di particolari chiamate a sistema eettuate dai programmi utente, a causa di eventi di interrupt o ogni qual volta il sistema operativo richieda di eseguire determi- nate operazioni. La duplice modalità di funzionamento consente la protezione del sistema operativo rispetto al comportamento viziato dei programmi utente.

Il livello di protezione si garantisce denendo come istruzioni privilegiate quelle istruzioni che possono compromettere il corretto funzionamento del sistema.

2.2.3 Gestione della memoria

La necessità di far risiedere più programmi in memoria sica, spesso, comporta automaticamente il fatto che la memoria sica non sia più suciente ad ospitare tutti i programmi necessari. Si introduce dunque il concetto di memoria virtuale, una tecnica che consente l'esecuzione di lavori d'elaborazione anche non comple- tamente caricati in memoria sica. Il vantaggio principale è che i programmi possono avere anche dimensioni maggiori della memoria sica ma ad esso si asso- cia anche l'importantissima conseguenza che i programmatori vengono in questo modo sollevati dallo scrivere programmi vincolati alla memoria sica disponibile.

Tutti i programmi, dunque, vengono scritti supponendo di avere a disposizione l'intero spazio di indirizzamento: l'area di memoria nella quale viene sviluppato un programma è della memoria logica o segmento del programma o memoria virtuale.

Una delle principali tecniche per implementare questo genere di parziale cari- camento in memoria sica del programma è quella della paginazione. La memoria

(23)

2.2. CARATTERISTICHE 15

sica viene suddivisa in blocchi di memoria di dimensione costante detti frame, la memoria logica viene ugualmente suddivisa in blocchi di memoria di pari di- mensione detti pagine. Il caricamento dei programmi in memoria sica avviene una pagina alla volta (quando richiesta) e se la memoria è stata riempita, alcune pagine non più utilizzate da molto tempo possono venire sostituite con le nuove richieste. L'occupazione eettiva di spazio risulta notevolmente ridotta ma la gestione del complesso si fa più dicoltosa.

Un sistema siatto necessita sia di ulteriore supporto hardware che di ulterio- re supporto software. In generale la traduzione degli indirizzi virtuali in indirizzi

sici viene demandata ad un apposito dispositivo hardware denominato MMU (Memory Management Unit), mentre la gestione degli avvicendamenti delle pagi- ne in memoria sica è un problema che viene arontato direttamente via software, ad appannaggio del sistema operativo. La dicoltà di implementazione di questo complesso necessita di una sorta di comunicazione (scambio di dati, scambio di informazione) tra la parte hardware e la parte software. Questa comunicazione avviene su un'area di memoria condivisa facente parte del kernel e nella quale risiedono le cosiddette tabelle delle pagine necessarie alla traduzione da indi- rizzo virtuale ad indirizzo sico. Le tabelle delle pagine, una per ogni processo, contengono le informazioni necessarie alla MMU per la traduzione da indirizzo virtuale ad indirizzo sico. E' tuttavia il sistema operativo che gestisce le tabel- le delle pagine, la MMU si limita solamente a leggere i dati che le servono e a eettuare la corretta lettura o scrittura in memoria.

Se la MMU trova che la pagina di un processo non è mappata in memoria, allora avviene il cosiddetto errore di page fault: l'esecuzione viene demandata al sistema operativo che, mediante il suo gestore della memoria, provvede a cari- care in memoria sica la pagina richiesta e ad aggiornare la tabella delle pagine.

L'esecuzione del programma utente, successivamente, riprende dall'istruzione che ne ha causato il page fault, ma a questo punto troverà la pagina mappata in memoria sica e proseguirà nella sua esecuzione.

2.2.4 Gestione dei dispositivi

L'interfacciamento tra l'utente nale e i dispositivi hardware viene completamente gestito dal sistema operativo, da un lato per garantire un livello di astrazione suf-

cientemente elevato da risultare di facile comprensione e utilizzo, dall'altro per evitare che l'utente possa recare danno all'intero sistema utilizzando in maniera errata siatta comunicazione.

La gestione dei dispositivi, inoltre, deve essere di tipo bidirezionale. Il con- cetto di bidirezionalità è quello legato al fatto che la comunicazione tra utente e dispositivi può avvenire in entrambe le direzioni. Da un lato infatti l'utente deve poter comandare i dispositivi (o la cpu) nei limiti dei poteri concessogli dal sistema operativo, dall'altro, sono i dispositivi stessi che devono poter comunicare all'utente o alla cpu il loro stato. Il concetto di bidirezionalità è estremamente importante e la sua implementazione è chiara ed evidente in ogni sistema opera- tivo. In ogni architettura e in ogni sistema operativo si assiste alla distinzione

(24)

tra chiamate a sistema (trap) ed interruzioni: questa distinzione realizza proprio il concetto di bidirezionalità.

Nella versione didattica di SimCPU, al posto delle interruzioni, veniva utiliz- zato il cosiddetto metodo del polling: la CPU (attraverso opportuno codice) per determinare se l'operazione richiesta era stata portata a termine, si metteva in attesa mediante un loop, del completamento dell'operazione. Ciò comportava evi- denti problemi di sotto-sfruttamento della capacità di calcolo della CPU, che per periodi di tempo piuttosto consistenti (in termini di rapporto tempo/istruzione) si limitava ad attendere la conclusione dell'operazione da parte del dispositivo esterno. Nella nuova versione di SimCPU, come verrà fatto notare in seguito, l'introduzione del sistema delle interruzioni ha permesso di superare agevolmente questo problema, sia per il dispositivo di input, che per il dispositivo di output. Il sistema del polling, tuttavia, non è stato del tutto abbandonato nei sistemi reali:

esso viene utilizzato ancora ampiamente per la gestione di dispositivi che non necessitano il rispetto di particolari esigenze in termini di esecuzione. Si pensi ad esempio ad un mouse: se fosse gestito mediante il sistema delle interruzioni esso genererebbe un'interruzione ogni qual volta l'utente lo muove di almeno un elemento del suo potere risolutivo. Una gestione siatta di questo dispositivo di input potrebbe addirittura essere dannosa in quanto troppo dispendiosa in rap- porto all'utilità della stessa. Decisamente più allettante è l'idea di campionare

la posizione del mouse ad intervalli regolari, mediante appunto polling da parte della CPU.

2.2.4.1 Comunicazione Dispositivi a SO - Interruzioni

Nella quasi totalità dei sistemi a processore viene utilizzato il sistema delle interruzioni per permettere ai dispositivi hardware di comunicare alla CPU (al SO) che sono pronti a eseguire una certa operazione. I modi per implementare questo sistema sono essenzialmente due.

Il primo consiste nel predisporre una linea pura (ag) adibita alla segnala- zione della presenza di un'interruzione da parte di un dispositivo. Con questa gestione non vi è nessun modo di sapere a priori quale sia stato il dispositivo che ha generato l'interruzione: tale operazione sarà demandata al sistema operativo che, durante la gestione della stessa, provvederà ad interrogare ad uno ad uno i dispositivi di hardware per poi eseguire la routine di gestione corretta una volta individuato il/i dispositivi da servire.

L'altro metodo consiste nella vettorizzazione delle interruzioni: nell'esatto momento in cui avviene l'interruzione, il sistema operativo è già informato del dispositivo che ha causato l'interruzione. Per esempio, l'architettura può essere dotata di più linee di interruzione, una per ciascun dispositivo e l'individuazione della routine da eseguire per servire l'interruzione è dunque automatica. Equi- valentemente, nel momento in cui avviene l'interruzione, può essere inviato in un certo buer o area di memoria l'indirizzo sico del dispositivo che ha causato l'interruzione e anche in questo caso l'individuazione del dispositivo è istantanea.

Il pervenimento delle interruzioni, essendo causato da dispositivi esterni, è ovviamente asincrono rispetto al usso del programma in esecuzione sulla CPU.

(25)

2.2. CARATTERISTICHE 17

Ciò implica che il sistema di gestione delle interruzioni richieda una modica diretta dell'iter di esecuzione delle interruzioni con un test sul ag di interruzione prima di ogni instruction fetch.

2.2.4.2 Comunicazione Utente a Dispositivi - Trap

Quando invece è l'utente a voler disporre dei dispositivi di input e output, la gestione a basso livello degli stessi è demandata a routine pressate denominate chiamate a sistema o trap. In parole povere, una chiamata a sistema rappre- senta una chiamata (CALL) ad una particolare routine del sistema operativo che, in funzione del genere di chiamata eettuata, esegue una serie pressata di istru- zioni che spesso non possono venire singolarmente utilizzate in modalità utente in quanto potenzialmente dannose. Tuttavia, nei sistemi operativi complessi, le chiamate a sistema non si limitano a gestire la scrittura a video o la lettura dalla tastiera. Nella maggior parte dei sistemi operativi le chiamate a sistema sono rife- rite a livelli più elevati ed astratti di quelle che realizzano solamente le operazioni di input-output. Si fa riferimento a chiamate a sistema demandate alla gestione dei processi, degli errori, dei segnali, dei le, del le system etc. Nella versione di SimCPU sviluppata in questa tesi, i concetti di chiamate a sistema astratte come quelle che consentono la terminazione di un programma, si confondono con chiamate a sistema più a basso livello come quelle che gestiscono l'input e l'out- put, che a loro volta vengono poste sulla stessa base delle chiamate (eccezioni in questo caso, perché eseguite direttamente via hardware) per la gestione degli errori causati dai programmi utente.

2.2.5 Gestore della memoria secondaria

Fino ad ora, per memoria sica è stato sempre inteso la memoria RAM, ovvero quel livello di memoria sica nella quale vengono mappate le pagine dei programmi che entrano in esecuzione. In realtà i livelli di memoria in un sistema reale sono numerosi e alcuni di essi (memoria cache) si interpongono direttamente tra la memoria RAM e il processore. La strutturizzazione della memoria in livelli ai quali corrispondono memorie via via più capienti e lente in funzione di un

allontanamento dal processore, è realizzata in quanto i programmi, in generale, tendono a soddisfare ai principi di località spaziale e temporale.

Il disco rigido, è un dispositivo di input-output che rappresenta la memoria successiva alla memoria RAM (più lenta e più capiente) denominata memoria secondaria. La gestione ecace di questa area di memoria si realizza mediante una organizzazione della stessa con costrutti astratti denominati les, ciascuno dei quali contenenti dati correlati, organizzati in una struttura di directory che contribuisce anche a fornire le informazioni relative ad ogni singolo le. Questo genere di suddivisione e organizzazione dei dati viene detto le system. Esso, inoltre, si occupa di fornire il meccanismo per la registrazione e l'accesso a dati e programmi appartenenti al sistema operativo e agli utenti.

Il sistema operativo, mediante il gestore della memoria secondaria (o del le system) fornisce ai programmi utente gli strumenti (non potenzialmente dannosi)

(26)

per poter agire sull'organizzazione del le system senza compromettere la stabilità del sistema. Come già accennato, anche in questi contesti, il le system viene gestito dal programma utente mediante l'esecuzione di opportune chiamate a sistema che richiamano a routine di codice del sistema operativo demandate allo svolgimento di tali operazioni.

2.3 Processi

Una volta acquisiti i concetti principali su cui si basa il funzionamento di un si- stema operativo (memoria virtuale, paginazione, le system, trap, interruzioni), è possibile introdurre il concetto di processo, nonostante esso sia già stato utilizzato durante la trattazione.

La denizione più semplicistica, nonché più corretta di processo è quella che sostiene che un processo non è altro che, un programma in esecuzione. Tuttavia, in virtù di una struttura di tipo time sharing del sistema operativo, il signicato di processo assume una valenza più ampia di quella che non assumerebbe in un sistema multiprogrammato. Come già esposto, in un sistema multitasking (time sharing), la necessità di schedulare la CPU con numerosi programmi (processi in esecuzione, e quindi non già conclusi come in un sistema multiprogrammato), porta all'individuazione di una serie di problematiche che costringono ad ampliare il complesso di risorse attribuite ad un programma in esecuzione. Un processo in un sistema multitasking non è solamente un programma in esecuzione, ma anche una determinata quantità di memoria allocata a tale processo per poter garantire la sua corretta esecuzione e schedulazione.

Il problema principale, che risulta evidente, in un sistema multitasking, è co- me poter garantire un avvicendamento ecace nella CPU dei processi, ovvero come fermare un processo e come riprenderlo correttamente quanto verrà nuo- vamente schedulato. L'informazione necessaria al processo, per poter riprendere correttamente deve venire dunque salvata in memoria principale, nel segmento del kernel (in quanto è il kernel, attraverso le sue routine a gestire l'avvicen- damento dei processi nella CPU): l'area di memoria demandata a tale scopo, o meglio, la struttura dati che contiene tutte le informazioni necessarie alla ripresa del programma è denominata PCB (Process Control Block).

In un sistema multitasking a time sharing, il tempo di utilizzo della CPU concesso ad ogni processo prima di eettuare un cambio di programma, è det- to TIME_SLICE. La ne della TIME_SLICE viene segnalata da un dispositivo esterno detto timer (orologio o clock, nel codice sviluppato questo dispositivo verrà indicato come clock) che genera una interruzione ogni qual volta passa una quantità di tempo pari ad una TIME_SLICE. Il sistema operativo, in virtù del- l'avvento dell'interruzione, passa dunque all'esecuzione della routine di gestione relativa al dispositivo che l'ha generata, in questo caso il timer, e decide se è giun- to il momento o meno di eettuare un context switching. Il context switching, o cambio di contesto, è appunto l'operazione che sottrae le risorse della CPU al programma corrente e le attribuisce ad un altro programma scelto in base ad un algoritmo particolare.

(27)

2.4. BOOTING, SHELL E SISTEMA DINAMICO 19

Il context switching si compone di tre fasi fondamentali. La prima fase ri- guarda il salvataggio dello stato della CPU nella PCB del processo in esecuzione.

La seconda fase riguarda l'esecuzione di un algoritmo per la scelta del succes- sivo programma da eseguire e la terza fase comporta il caricamento della PCB del programma scelto nelle risorse della CPU, ovvero il ripristino dello stato del processo scelto.

2.4 Booting, shell e sistema dinamico

In qualsiasi macchina sica munita di sistema operativo, la fase di inizializzazione del sistema viene denominata fase di booting. Il programma di Bios, risiedente in una memoria programmabile separata, si occupa innanzitutto di vericare i dispositivi hardware collegati al computer, ed in seguito carica in memoria prin- cipale il primo blocco di byte della memoria secondaria. Quest'area di memoria è in genere occupata dalla prima routine eseguita dal sistema operativo, la routine di boot, mediante la quale vengono caricate in memoria principale altre routine di sistema no a raggiungere una mappatura della memoria ottimale. Il primo programma caricato in memoria, se si esclude il kernel stesso, è il programma che realizzata l'interfaccia tra l'utente e il sistema operativo: la shell. La shell mette a disposizione una serie di comandi che permettono all'utente di creare processi, eseguire operazioni sui le e interagire con i dispositivi di input ed output. Si capisce dunque, che il sistema operativo alloca le risorse (tabella delle pagine, strutture PCB) in maniera dinamica a seconda delle esigenze dell'utente e del carico stesso dei programmi mandati in esecuzione. La possibilità di accedere alla memoria secondaria implica il vantaggio di poter gestire page fault in lettura e swapping dei processi, garantendo grossi carichi di lavoro. Nel capitolo 4 si vedrà come questo concetto di sistema dinamico dovrà necessariamente venire sosti- tuito da un sistema statico. L'impossibilità di simulare i dispositivi di memoria secondaria, nonché il le system, introduce una pesante limitazione sullo sviluppo e sulle funzionalità del sistema operativo.

(28)
(29)

Capitolo 3

SimCPU - implementazione hardware

Il primo capitolo riporta le direttive fondamentali per orientarsi in maniera quasi completamente autonoma all'interno del codice del simulatore didattico mentre le generalità sul funzionamento dei sistemi operativi individuate nel capitolo pre- cedente, ha permesso di comprendere a grandi linee quali siano eettivamente le modiche da apportare all'architettura. In questo capitolo verrà esaminata dapprima una versione evoluta di SimCPU, derivata dal merging eettuato dal codice sviluppato in due diversi lavori di tesi che, in maniera indipendente e scorrelata l'uno dall'altro avevano operato modiche sulla versione didattica di SimCPU. Le competenze acquisite nel capitolo 2, sono inne necessarie e su- cienti a comprendere il perché di determinate modiche hardware adottate nei due progetti.

3.1 Macrostruttura di SimCPU - versione base evoluta

Con riferimento alla relativa sezione del capitolo 2 che descrive la necessità di introdurre un dispositivo hardware per la traduzione da indirizzi virtuali ad indi- rizzi sici (MMU), si voleva richiamare in questa sede il fatto che l'introduzione del sistema della memoria virtuale era dovuto alla restrizione imposta sulla gran- dezza della memoria sica, che essendo limitata, non permetteva la presenza di interi programmi in memoria.

In realtà, il problema relativo a SimCPU è del tutto opposto ma l'implemen- tazione della paginazione e della memoria virtuale giova comunque al sistema.

Essendo SimCPU una macchina con un parallelismo di 16 bit, la memoria in- dirizzabile è pari a 65536B (216) e dunque anche la memoria sica presentava il medesimo numero di locazioni. Tuttavia, la necessità di sviluppare un sistema operativo che garantisse l'esecuzione di più programmi presenti contemporanea- mente in memoria, ha determinato come conseguenza un ampliamento della me- moria sica che è andata oltre la capacità di indirizzamento della macchina (che

21

(30)

nel frattempo ha mantenuto inalterato il suo grado di parallelismo). La macchina dunque non è più in grado di accedere ad ogni locazione della memoria ma deve avvalersi di un sistema di supporto sia hardware che software che comunichino e operino l'interfacciamento richiesto, altrimenti irrealizzabile.

Le principali modiche introdotte in questo senso, dunque, hanno riguardato l'introduzione della MMU per la gestione mediante paginazione di una memoria

sica non più a 64kB, ma estesa no ad 1MB (nonostante il limite architetturale di una macchina a 32bit sia di 232 locazioni di memoria). Allo stesso tempo è stato introdotto il ag di kernel ed una serie di registri speciali demandati a specici scopi come ad esempio la memorizzazione dell'indirizzo base della tabella delle pagine del sistema operativo e dell'ultimo programma utente in esecuzione.

Inoltre, prima di ogni instruction fetch, è stato aggiunto il cosiddetto interrupt test per la gestione delle interruzioni da dispositivi hardware esterni. Questi concetti verranno analizzati meglio nelle prossime sottosezioni.

3.1.1 Registri speciali, ag e istruzioni kernel

Il primo passo verso l'implementazione del sistema operativo è stato l'introduzione del ag di kernel il quale discrimina tra due diversi stati di funzionamento della CPU. Il sistema operativo è formato da codice che espleta la funzione di garantire l'esecuzione di più programmi utenti che devono coesistere all'interno della stessa memoria sica. Per ovviare a comportamenti maliziosi dei programmi utente e proteggere l'intero sistema è necessario introdurre due modalità dierenti di esecuzione delle istruzioni da parte della CPU, la modalità utente e la modalità kernel. In particolare, le istruzioni più sensibili verranno precluse ai programmi utente mentre potranno essere utilizzate solamente quando viene eseguito codice di sistema operativo (o kernel per l'appunto).

A tutti gli eetti, il sistema operativo si comporta come un programma spe- ciale che regola il funzionamento di altri programmi. Come i programmi utente, il sistema operativo possiede una sua tabella delle pagine (che si vedrà essere me- morizzata nel sul spazio di indirizzamento assieme a quelle dei programmi utente) il cui indirizzo base è sso e viene memorizzato all'interno di un registro speciale.

Un altro registro speciale viene dedicato alla memorizzazione dell'indirizzo base della tabella delle pagine del programma utente in esecuzione.

L'introduzione del sistema di paginazione, ha reso necessario dunque l'introdu- zione di una certa quantità di registri speciali nei quali memorizzare dati sensibili e importanti (KTB - Kernel Base Table Address \ UTB - User Base Table Ad- dress). Tra i ag aggiunti, si ricordano, oltre al ag di kernel_mode, quelli che abilitano o disabilitano il sistema di paginazione, l'attività della TLB (Transla- tion Lookaside Buer) e il ag di interruzione. Il ag di interruzione, come verrà visto in seguito, è soltanto un ag ttizio in quanto l'eettivo test della presenza o meno della necessità di servire un interruzione avviene sulla linea di interruzione stessa, e non sul ag.

L'introduzione del kernel_mode e dei registri speciali, ha reso possibile imple- mentare delle protezioni per istruzioni particolarmente sensibili dell'Instruction

Riferimenti

Documenti correlati

Tutto quanto sopra premesso, qualora Ella concordi, si propone di stipulare un contratto biennale di manutenzione programmata, tipologia “Professional”, con la ditta Schindler Spa,

Oltre il 31-esimo giorno solare Al 200% del canone mensile si aggiunge il 200% del canone giornaliero per ciascun giorno di ritardo Il mancato rispetto dei tempi massimi

(come alcuni credono) AL FATTO che gli stessi figli siano dediti agli studi in una scuola superiore o universitaria oppure al fatto che abbiano compiuto o meno il 26° anno di età..

A partire da dalla data di espletamento della Porta GbE VoIP e dei link trasmissivi, comunicata all’Operatore in fase di chiusura della richiesta, sarà avviata

Si definiscono due parametri da passare alla chiamata di sistema rename tramite il suo wrapper rename() della libreria del C ( man 3 rename). const char src_path[]

• Effettuare il trasferimento immediato della chiamata: fare clic su Trasferimento chiamata immediato ( ) per reindirizzare l’interazione outbound corrente a un contatto o a

Un prezzo elevato per la Vostra proprietà rende gli immobili concorrenti ben più attraenti della Vostra casa... Per questo il nostro 5° pilastro è pre-qualificare ogni singolo

Nota: Se avete già creato un gruppo (siete quindi il suo amministratore) che contiene già i nominativi con cui volete comunicare, o se fate già parte di un gruppo che contenga