:: Sistemi Operativi: appunti del 25 febbraio 2008 ::
Torna alla pagina di Sistemi Operativi
Lezione 1
La macchina di Von Neumann
Nella macchina di Fonnoiman ci sono tre elementi principali: processore, memoria, periferiche. La caratteristica della memoria è che essa contiene sia i dati che i programmi (ci sono architetture per cui non è così). Le tre componenti sono collegate tramite bus.
Un programma è un insieme di istruzioni date alla macchina che servono per risolvere un problema. Trasforma la macchina in un automa che risolve istanze di quel problema. Le istanze del problema le si trovano già in memoria, oppure le si recuperano dalle periferiche sarde (anche dette I/O).
Il processore
Tra le altre cose il processore contiene il registro PC, ovvero Program Counter, che contiene l'indirizzo di memoria della prossima istruzione che deve eseguire.
Tutto ciò che fa un processore si riassume in queste tre fasi:
- fetch: recupera l'istruzione dall'indirizzo indicato dal PC
- decode: la Control Unit decodifica l'istruzione per vedere se è lecita
- execute: l'istruzione viene eseguita.
Il processore è stupido e sa fare solo queste cose in sequenza, ad alta velocità. Da notare che il principio fetch - decode - execute è generico: un po' dappertutto prima si recupera l'ordine, lo si analizza e lo si esegue (in questa sequenza).
Il PC dopo ogni fetch viene incrementato di uno. Va bene, ma quando devo rappresentare ad un processore un salto come if a > 0 ... else ... come faccio? Occorre far saltare il PC in un'altra posizione della memoria. A questo scopo esistono le istruzioni di jump, condizionato o meno, che servono per spostare il PC in una posizione che non è quella successiva alla posizione attuale, come farebbe in automatico.
L'esecuzione delle istruzioni in sequenza o a seguito di salti è detta sincrona, perché avviene in continuo secondo il clock del processore.
Ma i segnali in arrivo da una periferica non possono essere sincroni, perché arrivano quando vogliono. Si chiamano infatti attività asincrone. Il processore deve reagire asincronicamente a questi input: succede che le periferiche interrompono il normale flusso dell'esecuzione dei comandi per ottenere attenzione. Quando sono soddisfatte, il processore torna a fare quello che faceva prima.
Da notare che la reazione ad un'interruzione NON è istantanea: il processore si accorge del mondo esterno solo nell'intervallo di tempo tra la fase di execute e la fetch successiva. Dopo la execute, il processore vede se qualcuno ha chiesto la sua attenzione, e se ne occupa.
Lezione 2
Chiamate di procedura
Che cosa succede in Fonnoiman se chiamo una procedura o rispondo ad un interruzione? Devo smettere di fare quello che stavo facendo, eseguire la procedura, e poi tornare. Adesso vediamo come.
Una procedura ha un certo numero di parametri formali, e una chiamata di procedura è l'atto di chiamare questa procedura con certi valori dei parametri formali. Per permettere l'esistenza di una procedura, occorre il principio dello stack, ovvero una pila realizzata in memoria.
Quando viene chiamata una procedura, salvo tutto ciò che ho di interessante nella CPU attualmente (PC, valori dei registri e così via) in cima allo stack. Poi, sempre nello stack trovo dello spazio per contenere i valori della procedura: il suo PC, i suoi parametri etc. In questo modo, conoscendo l'indirizzo in memoria della cima dello stack, il codice della procedura saprà con certezza che, in modo relativo alla cima dello stack, il primo parametro si troverà 1 word dopo, il secondo 2 word dopo, l'indirizzo a cui impostare il PC alla fine della procedura in un certo altro posto e così via. In questo modo, creo il contesto di attivazione della procedura, eseguo il suo codice, e sono in grado di tornare poi al codice che ha chiamato la procedura.
Ovviamente una procedura può chiamarne un'altra etc. etc.
Nel contesto di attivazione viene salvato anche l'indirizzo di partenza della porzione di stack che riguarda chi ha chiamato quella procedura. Questo serve per recuperare le variabili. Infatti, le variabili di una procedura vengono cercate prima nella porzione di stack a me allocata, poi se non le trovo vado nella porzione di stack relativa a chiamato me e le cerco lì, se non le trovo ancora vado nella porzione di stack relativa a chi ha chiamato il chiamante etc. etc.
Le variabili locali scompaiono quando la procedura termina: non servono più, e lo spazio che occupavano deve essere reso nuovamente disponibile.
Alla fine della procedura, poi, si ripristina il contesto di chi l'aveva chiamata: PC, registri e così via.
La differenza tra chiamata di procedura e risposta ad interrupt è la sincronicità. Una procedura viene sempre chiamata con una CALL. La risposta ad un interrupt invece no, non è predicibile quando avverrà (asincrona rispetto al processore). Quando si risponde ad un'interruzione, si salva tutto il contesto locale e si salta ad un indirizzo che era stato precedentemente associato a quella particolare interruzione. Si esegue il codice di quell'indirizzo, e alla fine si ritorna. Siccome non c'è nessuna CALL, durante la risposta ad un interrupt in genere si disabilitano gli interrupt, perché il processore non può rispondervi: non saprebbe più tornare al programma originale se un interruzione viene interrotta ancora una volta, proprio perché nessuna istruzione CALL ha salvato il PC del programma originale, e quindi il processore non saprebbe più ripristinare il contesto originale. Ci sono sistemi che lo fanno, è vero, ma lasciamo perdere questi casi strani.
Torna alla pagina di Sistemi Operativi