–
LEZIONI DI TEORIA SO 1 (Prima Parte)
Lezione 1 e 2 - RICHIAMI E INTRODUZIONE
COSA È UN PROCESSORE E INTERFACCIA HW/SW
Instruction Set Architecture (ISA)
Il processore è una macchina sequenziale che esegue delle istruzioni residenti in memoria per manipolare dei dati a loro volta
residenti in memoria. Un processore non esiste se non è attaccato ad una memoria (chiamata set di istruzioni). L’Instruction Set
l’insieme l’interfaccia
Architecture specifica delle caratteristiche del processore visibili al programmatore, definisce tra hardware e
software, e specifica la funzionalità del processore in termini di:
Dati gestiti (Tipi di dati)
Registri interni destinati a contenere i dati da manipolare
Istruzioni che prelevano dalla memoria e manipolano questi dati
Istruzioni di "salto"
Modalità di funzionamento speciali
Istruzioni speciali
processore è l’esecutore
Un di istruzioni del proprio Instruction Set.
Tramite le istruzioni macchina è possibile eseguire algoritmi specificati
in un linguaggio ad alto livello. Il processore può eseguire le istruzioni
del proprio IS solo una volta che le stesse sono state convertite in formato
binario (codice macchina). La trasformazione da una rappresentazione
all'altra viene eseguita da dei SW di supporto, il compilatore e
l'assembler. Figura: Come funziona il processore? Si accende, genera un
indirizzo di memoria per la memoria di istruzioni per eseguire una
istruzione che è all’interno di quell’indirizzo. Carica tramite una Fetch
l’istruzione (perché non le ha dentro) ogni volta che le serve. Una volta
caricata, viene decodificata, e invia a Memoria Istruzioni l’istruzione da eseguire. Da dove vengono le istruzioni? Dal codice ad alto
le trasforma in codice assembly. Questo codice viene passato all’assembler
livello (C). Le istruzioni vengono prese dal compilatore e
che poi lo passa alla memoria istruzioni, dopo aver convertito le istruzioni in codice macchina.
Fetch delle istruzioni
Allo scopo di eseguire un algoritmo il processore esegue delle istruzioni del proprio IS. All'istante 0 dell'esecuzione (quando lo
accendo) le istruzioni che codificano il programma da eseguire risiedono nella memoria istruzioni. Per poter essere eseguita dal
processore ciascuna istruzione deve essere caricata in un registro interno del processore (fetch dell'istruzione). Solitamente il registro
l’istruzione
destinato a contenere l'istruzione corrente (quindi da eseguire) è denominato Instruction Register (IR). Il flusso di
esecuzione delle istruzioni viene definito dalla dislocazione in memoria delle istruzioni stesse e dalla tecnica di fetch utilizzata. Il
tale flusso) prevede l’esecuzione
flusso normale di esecuzione (che non fa uso delle istruzioni dedicate che si occupano di modificare
sequenziale delle istruzioni secondo l'ordine di salvataggio in memoria. Un registro interno del processore contiene l'indirizzo della
locazione di memoria all'interno della quale è contenuta la prossima istruzione da eseguire e prende il nome di Program Counter
(PC). Ogni volta che viene caricata ed eseguita una istruzione che non preveda la modifica del flusso di esecuzione, il PC viene
incrementato in automatico in modo che la prossima istruzione caricata sull'IR e quindi eseguita sia la successiva a quella attualmente
eseguita (sequenza di esecuzione normale). La modifica del flusso di esecuzione sequenziale viene gestita dalle istruzioni di salto.
Tipologie di istruzioni
Le principali tipologie di istruzioni sono 3 (a cui vanno aggiunte istruzioni di tipo eterogeneo non classificabili in maniera sintetica):
Istruzioni di spostamento dati: si occupano di spostare i dati dalla memoria ai registri interni del processore e viceversa.
Esistono inoltre istruzioni che spostano i dati fra registri interni e istruzioni che spostano i dati fra locazioni di memoria
Istruzioni di manipolazione dati: si occupano di prelevare i dati da locazioni (di solito dai registri interni), di utilizzarli
come operandi sorgente per delle operazioni logico/aritmetiche e di salvare il risultato in altre locazioni.
Istruzioni di modifica del flusso di esecuzione (salto): si occupano di modificare il contenuto del PC in modo da
modificare il flusso di esecuzione definito dall'incremento automatico del PC stesso
Come funziona un processore: linguaggio assembly
l’ISA dei
Prendiamo come esempio MIPS. L’architettura
processori della famiglia MIPS è di tipo load/store: non è
possibile operare direttamente sui dati residenti in memoria,
ma è necessario prima copiarli nei registri interni tramite
istruzioni dedicate. I registri interni visibili (solo per la
gestione dei dati interi) del MIPS sono:
32 registri general purpose a 32 bit; il registro zero
contiene 0 e non può essere modificato
che contiene l’indirizzo della
un registro PC a 32 bit,
prossima istruzione da eseguire
load word (lw): carica su un registro interno un
valore dalla memoria [Esempio: Scrivi nel contenuto
del registro 3, il contenuto della locazione di memoria
avente per indirizzo il valore ottenuto dalla somma tra
l’indirizzo 0x100]
il registro 2 e
store word (sw): scrive in memoria il valore contenuto in un registro [Esempio: uguale a Load]
add : somma del valore di due registri
costante contenuta nell’istruzione
Addi: somma di un registro con una
subtract (sub): sottrazione tra il valore di due registri
and: and logico bit a bit del contenuto di due registri
or: or logico bit a bit del contenuto di due registri
set on less than (slt): scrive 1 sul registro destinazione se il primo operando è minore del secondo, zero altrimenti.
branch on equal (beq): salto condizionato all'uguaglianza dei valori contenuti nei 2 registri confrontati
salta all’indirizzo label2]
jump ( j ): salto incondizionato [Esempio:
Nell’esempio: PC=PC+4+imm. 4 perché ci sono 32 bit divisi in 4 byte (=bit), quindi per andare alla prossima istruzione devo
aggiungere 32 bit, ovvero 4 byte. Ad ogni byte è associato un indirizzo.
Formato delle istruzioni
Le istruzioni sono a lunghezza fissa (32 bit), in tre possibili
2
formati [I bit sono dati dal valore elevato 2. Es: 5 =32 che sono
reg]
Immediate: viene usato per le istruzioni di branch (beq). Serve
per le operazioni immediate (load e store).
Reg Type: Viene usato per le istruzioni aritmetiche (add e sub)
Jump: Viene usato per le istruzioni di jump [target: indirizzo di
memoria dove andare per il salto]
opcode: dice quale è l’istruzione attuale Richiami di programmazione assembly
ciclica (loop) viene gestita tramite l’utilizzo di un registro indice/contatore che:
L’esecuzione
viene inizializzato all’esterno del loop in funzione del numero di iterazioni da eseguire
viene aggiornato ad ogni iterazione del loop
del loop, il test che verifica la fine del ciclo ne interrompe l’esecuzione
viene testato ad ogni iterazione
agli elementi di un vettore con indice dipendente dall’indice del loop
L’accesso può essere gestita tramite un reg detto puntatore che:
all’esterno del
viene inizializzato loop
viene utilizzato ad ogni iterazione del loop per comporre l’indirizzo della locazione corrente
dipende dal numero di locazioni di memoria occupate da
viene aggiornato ad ogni iterazione del loop. L’aggiornamento
ciascun elemento del vettore
Esempio 3: codifica assembly
Utilizzo dei registri (esempio 3)
$1 ha la funzione di registro accumulatore
$2 ha la funzione di indice/contatore del
loop
$3 ha la funzione di puntatore all’elemento
corrente del vettore
$6 memorizza la costante usata per il test di
fine ciclo
$7 ha la funzione di buffer temporaneo
crescente, dall’elemento 1
La sequenza utilizzata è
all’elemento 7.
Esempio 4: Utilizzo dei registri
E’ possibile utilizzare un solo registro che accorpi le
funzionalità di registro indice/contatore per il test di
fine ciclo e di registro puntatore. In questo caso
l’inizializzazione e l’aggiornamento del registro,
nonché il test di fine ciclo, vanno gestiti in funzione
del fatto che la funzionalità di registro puntatore
richiede l’accesso a dati che possono occupare più
locazioni di memoria
$1 ha la funzione di registro accumulatore
$2 ha la funzione di indice del loop e anche di
puntatore all’elemento corrente del vettore
$3 ha la funzione di buffer temporaneo
$4 memorizza la costante usata per aggiornare il registro indice- puntatore
stavolta decrescente, dall’elemento 7 all’elemento 1.
La sequenza utilizzata è (spiegazione L2 min 1:08:00 circa)
Meccanismo di chiamata a subroutine
Utilizzo combinato delle istruzioni jl e jr
Salvataggio e ripristino dei registri
Convenzioni per il MIPS nella gestione dello stack da parte del compilatore
Le subroutine utilizzano le stesse risorse quindi ogni volta che prende un processore, deve salvare lo stato e restituirlo come era
ripristinandolo. Lo stato è dato da registri, quindi questi devono essere gli stessi. Infatti deve salvare i registri che esporta.
Meccanismo di chiamata di procedura
opportuno sottoinsieme delle istruzioni dell’IS
Un MIPS viene utilizzato per supportare la chiamata di una procedura (funzione), ed
una volta terminata la sua esecuzione, il ritorno alla istruzione successiva alla chiamata. Ogni volta che deve essere convertita in
―jal
assembly una chiamata a funzione viene utilizzata una istruzione label procedura‖ (jal = jump a link) che forza il salto
all’indirizzo della prima istruzione della procedura, e simultaneamente salva quello dell’istruzione successiva nel registro $31.
―jr
Funziona però solo per un livello di annidamento. Ogni procedura deve terminare con una istruzione $31‖ (jr = jump register) che
all’indirizzo della istruzione successiva a quella di chiamata, cioè all’indirizzo salvato in $31.
forza il valore del PC Per gestire la
a sua volta un’altra, sovrascrivendo l’indirizzo di ritorno in $31, è necessario che questo venga
possibilità che una funzione ne chiami
E’ l’indirizzo di ritorno nonché
salvato in memoria. necessaria una convenzione che definisca come salvare in memoria i parametri
passati alla procedura, i dati locali, ecc.
Gestione dello stack
Quando viene eseguita una funzione viene riservata ad essa una
porzione dello stack, detta frame. La dimensione del frame è
variabile e dipende dal numero di dati relativi alla funzione che è
necessario scrivere in memoria (può essere anche nulla se non ci
sono dati da memorizzare). A ciascuna funzione è associato un
frame pointer. Le informazioni da memorizzare di ogni frame sono:
parametri della funzione invocata oltre il quarto (i primi
quattro parametri sono salvati nei registri da $4 a $7)
indirizzo di ritorno
dati locali della funzione
registri temporanei che verranno sovrascritti durante l’esecuzione della funzione
Gestione della memoria per i sistemi monoprogrammati in modalità reale
Utilizzo della memoria nei sistemi monoprogrammati in modalità reale
Il compilatore utilizza la memoria dividendola in diverse parti: stack, heap, sezione testo e
sezione dati statici/dinamici.
Lo stack è la porzione di memoria gestita automaticamente dal processore, inizia a
partire dalle locazioni con indirizzo più alto e ha una dimensione variabile in
funzione dello spazio di cui ha bisogno il programma.
Lo heap è invece la zona di memoria gestita dinamicamente, con le funzioni
malloc e calloc che allocano spazio dinamicamente (Dynamic data). Inizia dalle
locazioni di memoria più basse e si riempie verso locazioni di indirizzo
più elevato a partire da un certo indirizzo.
La parte della memoria di indirizzi più bassi, da zero in poi, viene
utilizzata per le istruzioni e per il codice (text area).
La parte della memoria riservata (Reserved)
A partire dagli indirizzi di memoria più alti sta la funzione main, il compilatore
alloca ricorsivamente lo spazio per le procedure allocando e disallocando zone
di memoria. Ogni procedura ha a disposizione una zona di memoria privata
definita frame di attivazione. Lo spazio dei frame di attivazione è lo stack.
Comunicazione processore-periferiche: memory mapping
Memory mapping
La comunicazione tra processore e periferica è implementata tramite accessi in lettura/scrittura (istruzioni standard load e store) a
locazioni di memoria. La periferica è dotata di registri interni ai quali il processore può accedere in lettura/scrittura. A ciascun registro
interno di ogni periferica è associato un indirizzo di memoria. Lo spazio di memoria dedicato alle periferiche deve essere costituito da
indirizzi non utilizzati per la memoria.
Lettura: ogni qualvolta il processore necessita di acquisire un dato dall'esterno esegue una istruzione di load. L’indirizzo è
quello del registro della periferica che si vuole leggere. Il dato viene copiato su uno dei registri interni per poter essere subito
utilizzato o copiato in memoria
ogni qualvolta il processore necessita di mandare un dato all’esterno esegue
Scrittura: una istruzione di store. L’indirizzo è
quello del registro della periferica sulla quale si vuole scrivere. Il dato viene copiato da uno dei registri interni sul registro.
Scrittura sulle periferiche (L3 min 50:00 circa)
ha una porta dati e una porta indirizzi. L’indirizzo mandato dal processore viene letto da tutte le periferiche.
Il processore (MIPS)
Ciascuna periferica scrivibile ha la propria porta dati in ingresso collegata al bus di uscita del processore ed è inoltre collegata alla
E’
porta indirizzi. dotata di un decoder che abilita la scrittura su uno dei propri registri interni solo nel caso in cui sul bus indirizzi si
presenti l’indirizzo associato a quel registro e che il segnale di scrittura sia attivo. Gli spazi degli indirizzi non devono essere
sovrapposti. E’ importante Write Enable Signal, bit che distingue il caso in cui sia da fare lettura o scrittura.
Lettura dalle periferiche
Le periferiche da cui leggere hanno la porta dati in uscita collegata ad uno degli ingressi di un multiplexer. Il multiplexer è collegato
in uscita con la porta dati in ingresso del processore, ed è controllato da un decoder che decodifica il bus indirizzi abilitando in uscita
l’ingresso associato all’indirizzo decodificato. E’
Ciascuna periferica è collegata alla porta indirizzi del processore. dotata di un
decoder interno che manda in uscita il contenuto del proprio registro interno avente indirizzo uguale all’indirizzo in transito sul bus
apposito
Generalizzazione
Una volta convenuto che qualunque tipo di dato fisico può essere modellato come un numero binario e quindi esistono dei dispositivi
(sensori/trasduttori) in grado di operare la conversione. Una periferica di input può essere il mouse o il microfono. Una periferica di
out può essere una stampante, un display.
Funzionalità avanzate del processore: interrupt e timers
Interrupt
PC si aggiorna automaticamente, oppure tramite istruzioni di salto nel caso di
L’esecuzione
esecuzione condizionale o esecuzione di subroutines. di pezzi codice
dell’algoritmo
in maniera completamente asincrona rispetto alla codifica si basa
l’interrupt.
su un altro meccanismo: Esiste un ingresso hardware dedicato del
l’ingresso
processore, di interrupt. Ogni qualvolta si presenta un determinato
all’interrupt, l’esecuzione
segnale in ingresso viene sospesa del programma
corrente, e viene eseguito un segmento di codice (di un'altra zona) associato
all’ingresso di interrupt, detto routine di interrupt, al termine del quale, viene
l’esecuzione
ripresa del programma principale esattamente dal punto in cui era
stato interrotto. L’implementazione è basata sulla sovrascrittura e ripristino di PC.
Periferica timer: funzionalità con l’ambiente esterno, rendono spesso
Le specifiche di temporizzazione legate alla gestione real-time della interazione
indispensabile l’utilizzo di meccanismi che consentano l’esecuzione di determinati task (acquisizione di ingressi, scritture in uscita...)
in maniera asincrona (in subroutines è sincrona) rispetto al ciclo di esecuzione del programma. Il meccanismo per risolvere questo
L’interrupt deve essere veloce per permettere il continuo dell’esecuzione del flusso.
tipo di problematiche è l’interrupt. Nel caso la
gestione real time debba avvenire in frequenze e istanti di tempo prestabiliti, risulta indispensabile una periferica che comunichi al
fissa, indipendente dall’esecuzione in corso.
processore un segnale di sincronismo con una frequenza Una periferica che svolge questo
ruolo è il timer, programmato via software in modo da generare un interrupt per il processore con una determinata frequenza. Si tratta
in pratica di un contatore programmabile che conta i cicli di clock in ingresso generando un segnale ad ogni fine conteggio.
Esempio di timer: La figura è una periferica memory-mapped, dotata di un
registro di stato leggibile e parzialmente scrivibile, con una uscita di interrupt. Il
clock utilizzato per il conteggio non è il clock utilizzato per il resto del sistema
(global clk), ma un altro segnale di clock inserito appositamente nel sistema
(sample clk). Ha un porta indirizzi, porta dati, un interrupt in uscita, global clk,
sample clk e reset. astrazione dall’hardware!
A cosa serve il sistema operativo: multiprogrammazione e
Il sistema operativo:
I servizi fondamentali offerti dai sistemi operativi ai processi sono:
astrazione dall’hardware
gestione efficiente delle risorse
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
-
Sistemi operativi - Teoria
-
Sistemi operativi
-
Sistemi Operativi - Appunti Teoria Seconda Parte
-
Sistemi Operativi