ARCHITETTURE DEI SISTEMI DI ELABORAZIONE s165178

ARCHITETTURE DEI SISTEMI DI ELABORAZIONE
[email protected]
http://creativecommons.org/licenses/by-nc-sa/3.0/it/
1. CONCETTI PRELIMINARI
Classificazione degli elaboratori
• Desktop, portatili
• Server
• Mainframe
• Supercomputer
• Embedded system
◦ Ampia varietà di processori
◦ Risposta real-time
◦ Occupazione da minimizzare
◦ Hardware dedicato
Livelli di osservabilità del sistema
• Architettura del singolo processore
• Interconnessione tra elaboratori
• Interconnessione tra elaboratori e altri dispositivi
Evoluzioni
• Innovazioni architetturali
• Miglioramenti tecnologici
Prestazioni
Vi sono diversi parametri per misurare le prestazioni:
• MIPS: milioni di istruzioni per secondo
• FLOPS: operazioni in floating point per secondo
• IOPS: operazioni di input/output per secondo
• MTBF: medium time between failures
• TPS
• MWatt o Mflops/W
• Latenza: intervallo di tempo tra richiesta e soddisfacimento
Legge di Ahmdal
E’ un formalismo per misurare le prestazioni. In linea generale possiamo assumere che
un indicatore sul miglioramento prestazionale sia dato dal seguente rapporto
speedup=
prestazioni con migliorie
prestazioni senza migliorie
Ciò detto, identifichiamo come tEXEC_NEW il tempo di esecuzione dopo l’apporto di una miglioria. Questo tempo è esprimibile come
t EXEC _ NEW =t EXEC _ OLD∗((1−frazione ENH )+
frazioneENH
)
speedupENH
dove frazioneENH è la frazione del tempo di calcolo da cui trae beneficio il miglioramento,
mentre speedupENH è il valore quantitativo del miglioramento.
Fatte queste premesse, si può esprimere la legge di Ahmdal, che dà una formula analitica per il calcolo dello speedup
speedup=
t EXEC _ OLD
=
t EXEC _ NEW
1
(1−frazioneENH )+
frazione ENH
speedupENH
Esempio
Supponiamo di avere un sistema S, caratterizzato da una memoria con performance P.
Si consideri l’ipotesi tendenzialmente valida in cui il sistema S faccia il 50% di operazioni
verso la memoria, il 40% verso la CPU e il 10% verso dispositivi di I/O. Viene introdotta
una memoria con performance 2P. Qual è il miglioramento globale del sistema?
speedup=
t EXEC _ OLD
=
t EXEC _ NEW
1
frazione ENH
(1−frazioneENH )+
speedupENH
=
1
0,5
(1−0,5)+
2
=1,33
Si mantenga ora la memoria con performance P, ma si sostituisca il processore dalle
performance Q con uno dalle performance 10Q.
speedup=
t EXEC _ OLD
t EXEC _ NEW
=
1
frazione ENH
(1−frazioneENH )+
speedupENH
=
1
0,4
(1−0,4)+
10
=1,5625
Dall’esempio dovrebbe essere chiaro come un miglioramento tutto sommato lieve della
memoria porta a prestazioni comparabili con quelle date dall’inserimento di un processore dieci volte più veloce del precedente. Non è dunque vero che il processore “conta
più di tutto”.
Migliorare le prestazioni
Esistono diverse strategie per migliorare le prestazioni. In primo luogo, il progresso tecnologico: la legge di Moore infatti afferma che “le prestazioni dei processori, e il numero
di transistor ad esso relativo, raddoppiano ogni 18 mesi”.
Si possono compiere anche modifiche a livello architetturale, introducendo memorie cache, architetture superscalari e pipeline. Per giustificare l’introduzione delle modifiche a
livello architetturale, si consideri il tempo di esecuzione di un’istruzione:
t exe=t fetch+t decode+t execute
Parti di tempo vengono “perse” nel fetch (prelevamento) delle istruzioni e nella decodifica di esse. Sarebbe buono riuscire a fetchare le istruzioni in anticipo – per averle già
pronte quando necessario – e avere più unità che lavorano in parallelo per avere
un’istruzione pronta da eseguire una volta terminata la precedente.
Queste conclusioni portano a:
• disaccoppiare i bus di sistema, utilizzando una coda di prefetch e una L1 cache
(o, per meglio dire, una per i dati e una per le istruzioni)
• aumentare il parallelismo, introducendo le pipeline o le architetture superscalari
in cui più pipeline lavorano in parallelo.
Sono possibili altri miglioramenti, derivanti dall’introduzione dei sistemi a più core.
• aumentare il parallelismo dei thread, nei sistemi a più core
◦ ILP: Instruction Level Parallelism, all’interno del singolo core
◦ TLP: Thread Level Parallelism, tra più core
• aumentare il parallelismo del trasferimento
◦ architetture serial point-to-point
◦ multi level bus
Impatto sulle prestazioni
Per proseguire ad analizzare le prestazioni di un sistema, si faccia riferimento a un’architettura a bus a tre livelli, in cui sono presenti:
• host bus, ovvero il bus che interfaccia cache e RAM, @ 800 MHz.
• bus veloce @ 33 ÷ 66 MHz
• bus lento @ 8 MHz
Tenendo a mente la velocità di un qualunque processore moderno, si può vedere subito
come la velocità del clock è molto maggiore di quella del bus. Tuttavia, la velocità del
bus solitamente deriva da quella del clock.
Esempio
Nel seguito vengono considerati, in termini quantitativi e semplificativi, i miglioramenti
prestazionali introdotti da alcune evoluzioni tecnologiche-architetturali sui microprocessori. In particolare si valuteranno i seguenti elementi:
• velocità e parallelismo del bus esterno
• velocità del processore
• parallelismo del processore (prefetch, …)
• cache L1
Si supponga dunque di dover gestire la memoria video. Abbiamo uno schermo 600 x
400 con una palette di 24 bit. La frequenza di refresh della VRAM, scritta dal processore
e letta dal controllore video, è di 50 Hz.
Ogni 1/50 Hz = 0,02 s devo dunque caricare i nuovi dati della matrice che va a comporre lo schermo, grande 600 * 400 * 3 B = 720.000 B = 0,72 MB. Combinando i due dati si
ottiene un throughput di 0,72 MB / 0,02 s = 36 MB/s.
Sistema d’esempio #1
CPU @ 8 MHz
Accesso a memoria @ 4 CLK
Bus 16 bit @ 8 MHz
Istruzione media: 2,5 B
Dalle prestazioni del bus troviamo che TCLK è pari a 125 ns, e quindi un accesso in memoria richiede 500 ns. Per fare il fetch di un’istruzione grande 20 bit, servono 2 accessi in
memoria che richiedono complessivamente 1 secondo. L’esecuzione avviene in 500 ns.
Si ha
T m=t fetch +t execute=1000+ 500ns=1500 ns
Il transfer rate del sistema è
TR=
parallelismo bus
2B
=
=1,3 MB/ s
Tm
1,5 μs
Sistema d’esempio #2
Coda di prefetch
CPU @ 8 MHz
Accesso a memoria @ 2 CLK
Bus 16 bit @ 8 MHz
Istruzione media: 2,5 B
In questo sistema, un accesso a memoria richiede 250 ns. Con la coda di prefetch, a regime si può “annullare” il tempo di fetch. Quindi
T m=t fetch +t execute=0+250 ns=250ns
TR=
parallelismo bus
2B
=
=0,008 B/s∗109 =0,008 MB /s∗103 =8 MB/ s
Tm
250ns
Sistema d’esempio #3
Coda di prefetch
CPU 8086
Accesso a memoria @ 2 CLK
Bus 32 bit @ 30 MHz
Istruzione media: 2,5 B
Dalle prestazioni del bus troviamo che TCLK è pari a 33 ns, e quindi un accesso in memoria richiede 66 ns. Si ha
T m=t fetch +t execute =0+66 ns=66 ns
TR=
parallelismo bus
4B
=
=60 MB /s
Tm
66 ns
Sistema d’esempio #4
TCLK = 5 ns
TCACHE = 10 ns
TDRAM = 60 ns
TMEM = 0,9 * 10 ns + 0,1 * 60 ns = 15 ns
TISA = 200 ns
TPCI = 33 ns
(ipotesi: 90% hit)
TIstruzione, ISA = 0,25 * 5 ns + 0,7 * 15 ns + 0,05 * 200 ns = 22 ns
TIstruzione, PCI = 0,25 * 5 ns + 0,7 * 15 ns + 0,05 * 33 ns = 13,4 ns
Dagli esempi si può comprendere che:
• il sistema 3 riesce a garantire un throughput come quello richiesto
• l’aumento della velocità della CPU non porta a sensibili miglioramenti
2. ARCHITETTURA x86 32/64
Architettura Pentium 80x86
Architettura
Indirizzamenti
Registri dati
Nativa su...
X86 – 16, IA – 16
16 bit (offset)
8, 16 bit
8086, 80286
X86 – 32, IA – 32
16, 32 bit (offset)
8, 16, 32 bit
80386, 80486
X86 – 64, IA – 64
16, 32, 64 bit (offset)
8, 16, 32, 64 bit
Pentium 4, Core
Parallelamente a queste architetture, esiste anche IA - 64, un’architettura totalmente
differente basata su tecnologia VLIW Very Long Instruction Word. Nella tecnologia VLIW
le parole di istruzione sono molto lunghe e contengono sottoistruzioni eseguibili in parallelo.
Modo reale e modo protetto
Quando si parla di modo protetto si intende quella modalità nativa del Pentium/Core in
cui tutte le funzionalità architetturali sono disponibili. Prevede la gestione della memoria
con segmentazione ed eventualmente paginazione. In HW sono realizzate tutte le specifiche per gestire il multitasking. Può essere a 32 bit (modo base di Windows e Linux) o a
64 bit (compatibile con 32 bit).
Viceversa, il modo reale è quella modalità che prevede l’emulazione HW, seppur in
modo esteso, dell’8086. Si ha la visibilità sui registri a 32/64 bit. E’ il modo attivo al reset ed è pertanto il modo nativo del BIOS nel PC. In modo reale, il programmatore vede
una riduzione della seguente architettura
1: main registers;
2: segment registers and IP;
3: address adder;
4: internal address bus;
5: instruction queue;
6: control unit;
7: bus interface;
8: internal databus;
9: ALU;
10/11/12: ABUS, DBUS, CBUS
La execution unit EU provvede alla decodifica e all’esecuzione delle istruzioni. Riceve
byte per byte le istruzioni dalla bus interface unit BIU, che gestisce tutte le operazioni
da e verso l’esterno (fetch istruzioni, lettura e scrittura operandi, generazione indirizzi...)
lavorando in parallelo con la EU.
A sua volta la BIU gestisce una coda delle istruzioni, struttura FIFO di dimensioni pari a 4
istruzioni (10 byte) in cui vengono accumulate le istruzioni che si prevede dovranno es-
sere eseguite. Viene caricata dalla BIU ogni qual volta vi è una word libera. Se viene
eseguita una istruzione di salto, essa viene azzerata e la EU deve attendere il tempo necessario per il fetch di una nuova istruzione. Per ottimizzare i tempi, la BIU esegue l’operazione di prefetch caricando preventivamente istruzioni dalla memoria.
Vi sono quattro tipi di registro: dato, indirizzo, controllo e segmento. Per quanto riguarda
i primi:
• AX è usato come accumulatore
• BX può essere utilizzato anche nel calcolo degli indirizzi
• CX viene usato anche come contatore
• DX contiene l’indirizzo di I/O in alcune istruzioni di I/O
Per quanto riguarda i registri di indirizzo (puntatori):
• IP contiene il valore alla prima istruzione da eseguire e non può comparire esplicitamente come operando di una istruzione
• SP contiene il puntatore alla testa dello stack
• BP viene utilizzato come base per fare accesso all’interno dello stack
• SI e DI vengono utilizzati come registri indice
I flag di condizione e controllo sono all’interno della PSW. I primi vengono automaticamente scritti al termine di varie operazioni, e danno informazioni sul risultato di istruzioni aritmetiche. I secondi, invece, possono venire scritti e manipolati da apposite istruzioni e servono a regolare il funzionamento di talune istruzioni del processore.
I registri di segmento, infine, sono CS, DS, ES e SS. Vengono utilizzati per costruire gli indirzzi fisici con i quali fare accesso in memoria.
Gestione memoria modo reale
Ogni volta che l’8086 deve generare un indirizzo fisico da mettere sull’ABUS esso esegue un’operazione di somma tra il registro base moltiplicato per 16 a cui viene sommato
lo spiazzamento.
Base
Spiazzamento
Dati
DS
BX, SI, DI
Codice
CS
IP
Stack
SS
SP, BP
Questo perché a livello di programmazione vedo una memoria formata da 2 14 = 16384
blocchi, ciascuno grande 4 GB. Nel modo protetto i segmenti non sono sovrapposti,
mentre nel modo reale, considerando anche le modalità di costruzione dell’indirizzo, i
segmenti sono sovrapposti. Ciascun segmento è grande 64 KB e inizia a un indirizzo
multiplo di 16.
Ricorrere a un meccanismo di memoria segmentata permette la separazione di dati, codice e stack in segmenti diversi e sovrapponibili in modo da sprecare al minimo la memoria.
Accesso alla memoria e parti riservate
Nel modello IA – 16 si ha uno spazio di indirizzamento pari a 1 MB. I dati memorizzati in
una word hanno il byte meno significativo memorizzato nel byte con indirizzo minore,
secondo la codifica little endian.
Alcune parti della memoria sono riservate: gli indirizzi da 00000H a 003FFH contengono
la Interrupt Vector Table. Gli indirizzi da FFFFCH a FFFFFH contengono dati di versione
del BIOS. Gli indirizzi da FFFF0H a FFFFBH contengono un’istruzione di salto alla routine
di caricamento del programma di bootstrap.
All’atto del reset, infatti, nel PC viene caricato il valore FFFF0H. Si salta nel BIOS, nel
modulo di inizializzazione che determina quanta memoria c’è scrivendo e leggendo dalle
varie locazioni. Fatto questo, bisogna leggere il sistema operativo presente sul disco: se
ne occupa una routine del BIOS, presente in ROM. Viene letto il MBR (settore 1, traccia
0, cilindro 0) che indica dov’è presente il sistema operativo, avendo cura di leggere informazioni geometriche sul disco. Nella RAM viene dunque caricato prima il boot sector,
e poi il SO.
Stack
In 8086 vi sono alcune strutture e meccanismi hardware per la gestione di uno stack. Lo
stack è un segmento di memoria la cui testa è puntata da SS. Il top dello stack (l’ultima
locazione riempita) è puntata da SP. Cresce dalle locazioni di memoria con indirizzo
maggiore verso quelle a indirizzo minore. Ogni PUSH decrementa di due unità SP, scrivendo una word nella locazione da esso puntata. La POP ha un comportamento speculare.
Viene manipolato automaticamente anche all’atto della CALL di una funzione (= PUSH
IP) e alla successiva RET (= POP IP), nonché dopo aver chiamato una routine di servizio di un interrupt (= PUSH IP, PUSH PSW) e alla successiva IRET (= POP PSW,
POP IP).
Aumentare le prestazioni
Vi sono tre vie perseguibili per aumentare le prestazioni:
• incrementare l’esecuzione di singole istruzioni mediante pipeline (ILP)
• incrementare il numero di istruzioni eseguite in parallelo mediante processori superscalari (ILP)
• incrementare il numero di porzioni di codice eseguite in parallelo (ILP)
Pipeline
La pipeline è una tecnologia utilizzata nell'architettura hardware dei microprocessori dei
computer per incrementare il throughput, ovvero la quantità di istruzioni eseguite in una
data quantità di tempo, parallelizzando i flussi di elaborazione di più istruzioni.
Senza pipeline
Con pipeline
Si suddivide il tempo di esecuzione di una istruzione in una serie di stadi e si cerca di
parallelizzarli. Nel pipeline perfetto, la durata dello stadio è sempre sfruttata: se lo voglio avere devo dimensionare lo stadio con la durata dell’istruzione più gravosa, e naturalmente avrò momenti in cui la pipeline è sottosfruttata.
Se la pipeline è perfetta, ogni istruzione è completata nel tempo di uno stadio. Occhio
però a non confondere il tempo di esecuzione di una istruzione (dato dalla somma dei
tempi necessari a percorrere tutti gli stadi di pipeline) al numero di istruzioni eseguite in
un secondo, indicato come MIPS o throughput.
Prestazioni di una pipeline
Sia perciò TS il tempo di esecuzione di una istruzione in un sistema senza pipeline e T p il
corrispettivo in un sistema con pipeline. In caso di un sistema con pipeline non perfetto,
vale
T S≤T P
Ancora, sia MIPSS = 1/TS il numero di istruzioni eseguite in un sistema senza pipeline, e
sia MIPSP = n/TP il numero di istruzioni eseguite in un sistema con pipeline, con n numero di stadi della pipeline. Vale
MIPS S ≤MIPS P
e, intuitivamente, si potrebbe dire che le prestazioni crescono linearmente con n. In realtà, esiste un limite oltre il quale aumentando n non si riesce più a soddisfare il vincolo di
una pipeline ottima. Questo aspetto è anche legato al fatto che il numero degli stadi è
una lama a doppio taglio in caso di JMP, in quanto devo svuotare tutti gli stadi.
Siano, infine:
k: numero di stadi della pipeline
τi: tempo di esecuzione dell’i-esimo stadio
τmax: tempo di esecuzione massimo tra gli i stadi
Si ha che
il tempo di esecuzione di un’istruzione è pari a kτmax
il tempo di esecuzione di N istruzioni, senza pipeline, è pari a Nkτmax
il tempo di esecuzione di N istruzioni, con pipeline, è pari a kτmax + (N – 1) τmax
da cui si può derivare un fattore di accelerazione
fattore di accelerazione=
esecuzione di N istruzioni , senza pipeline
Nk
=
esecuzione di N istruzioni , con pipeline
k +( N−1)
in cui, per N grande, il rapporto tende a k. I MIPS di picco risultano essere pari a
MIPS=
1
τ max
=
frequenza clock CPU
m
dove m è il numero di periodi di clock richiesti per eseguire le operazioni in uno stadio.
Pipeline nel Pentium
Nei processori Pentium la pipeline è formata da 5 stadi.
•
•
•
•
•
F: fetch, in cui prelevo un’istruzione (= un certo numero di byte dalla memoria,
sulla base del primo in cui è contenuto il codice operativo dell’istruzione) dalla
coda di prefetch o dalla L1. Se non riesco a effettuare un prelievo (coda vuota) si
entra in stallo
D1: decodifica, in cui viene verificata la legittimità dell’istruzione in modo protetto. Il tempo di questo stadio è coerente con quello richiesto dall’istruzione più
onerosa.
D2: decodifica, in cui vengono reperiti gli operandi dai registri o dalla memoria.
Nel secondo caso, c’è una fase di risoluzione degli indirizzi che coinvolge il modo
protetto per segmentazione e paginazione
EX: esecuzione, in cui si svolgono le operazioni sulla ALU. Se ho istruzioni che non
coinvolgono la ALU, come le MOV, l’unità aritmetica non fa niente.
WB: write back, in cui scrivo il risultato sull’operando che deve recepirlo.
Ogni stadio richiede 1 CLK. In alcune condizioni, in uno stadio si inseriscono più periodi
di clock (es.: accesso alla memoria). Nei processori attuali, dopo aver constatato che 30
stadi avevano complessità di gestione elevate e forti penalità in caso di salto, si è
passati a un’architettura con circa 12 stadi combinata con parallelismo di thread.
Processore superscalare
In un’architettura superscalare, per aumentare il parallelismo, si ricorre a:
• cache dati e cache codice separate
• utilizzo di più pipeline
La memoria ROM di microcodice serve per esplodere le istruzioni complesse in istruzioni
semplici, dato che si viaggia con istruzioni CISC. In termini di pipeline, il processore illustrato è un semisuperscalare in quanto abbiamo 2 pipeline da 5 stadi ognuna, ma non
tutte possono ospitare indistintamente istruzioni.
Situazioni critiche che possono ridurre le prestazioni sono date da eventuali incompatibilità tra istruzioni su dati e/o operazioni, situazioni di stallo per accesso a memoria o I/O
lenti, salti condizionati.
Con P pipeline operanti in parallelo si hanno MIPS di picco pari a
MIPS=P
1
τ max
=P
frequenza clock CPU
m
I/O
L’accesso alle periferiche avviene spesso attraverso speciali locazioni associate ad un
certo indirizzo. Vi sono due modi:
• memory mapped
• isolated I/O
Nel primo, l’accesso alla periferica avviene attraverso una normale istruzione MOV. Non
è mai realizzato nei sistemi PC in quanto dovrei riservare un tot. di indirizzi di memoria
ai dispositivi di I/O. Nel secondo caso si usano speciali istruzioni di I/O, utilizzando segnali di controllo per far capire che gli indirizzi nell’istruzione sono riferiti a periferici.
In ogni caso, la CPU può vedere 256 o 65536 (registri associati ai) periferici.
Unità speciali
Oltre ai citati registri di CPU, esistono due registri contenuti in due unità speciali:
• coprocessore matematico
• unità di gestione delle istruzioni SIMD
◦ MMX, SSE
▪ Basate sulla visione dei registri in diversi gruppi logici
▪ Rende possibile operazioni in parallelo
3. BUS DI SISTEMA E DI CPU NELL’’ARCHITETTURA 80x86
Differenze tra sistemi desktop e sistemi server
Sistemi desktop
In un sistema desktop si ha una molteplicità di bus.
•
•
•
HOST BUS: sistema di interconnessione CPU – memoria (normale o cache). Velocità alta (400 ÷ 500 MHz)
BUS PER PERIFERICI VELOCI (33 ÷ 66 MHz)
BUS PER PERIFERICI LENTI (10 MHz)
Vi sono poi dei bridge che servono a gestire il collegamento tra i bus. In caso di una MOV
AX, mem devo ben sapere, una volta che l’indirizzo è mandato sul bus esterno se mem è
un indirizzo di memoria o si riferisce all’I/O. In sostanza, cerco di andare ogni volta sulla
porzione di calcolatore che mi garantisce prestazioni migliori a seconda di quello che voglio fare.
Nei PC attuali il bus lento non c’è e vi possono essere diversi dispositivi collegati al controller del bus veloce PCI-e. In alcune versioni, infine, il FSB è rimosso e sostituito con
collegamenti punto-punto intrinsecamente paralleli (architetture NUMA).
Sistemi server
Molto diversi dai sistemi desktop: ho più CPU operanti in parallelo, interconnesse su un
bus o con una rete. La DRAM è condivisa. C’è una pluralità di bus per le periferiche.
Cicli di bus
Definizione e concetti generali
E’ l’intervallo di tempo in cui si realizza una transazione tra master (CPU) e slave (memoria, I/O). E’ un intervallo di tempo atomico ed è caratterizzato dal tipo di trasferimento.
Esistono 4 tipi fondamentali di cicli di bus, che possono essere di lettura o scrittura di
memoria o I/O.
I bus dei processori 80x86 sono sincroni (in realtà semisincroni) e caratterizzati da un
numero fisso di periodi di clock di bus. Nei processori tipo Pentium o nei bus di sistema
(es.: PCI), il ciclo di bus è pari a 2TCLK. La scelta della frequenza di clock dev’essere compatibile con i dispositivi che si affacciano col bus.
Tipi di ciclo
L’inizio del bus cycle è definito da un opportuno segnale.
CICLO DI LETTURA
T1: sull’ABUS viene scritto l’indirizzo
T2: la CPU forza sul DBUS il valore Z
T3, T4: la memoria scrive il dato sul DBUS
CICLO DI SCRITTURA
T1: sull’ABUS viene scritto l’indirizzo
T2: la CPU scrive il dato sul DBUS
T3, T4: la memoria legge il dato dal DBUS
CICLI DI WAIT
Può succedere che la memoria non riesca a rispondere nelle tempistiche dettate dal
clock del bus. In tal caso, si inseriscono tra T 3, T4 una serie di stati di attesa fino a che la
memoria risponde. Per comunicare all’8086 la necessità di uno o più cicli di wait, esiste
il segnale READY.
CICLI DI IDLE
Inseriti dalla CPU quando necessario, ossia quando:
• la CPU non necessita di nuovi dati
• la coda interna delle istruzioni è piena e non si può fare prefetch
Prestazioni
Di solito si indica la capacità di trasferimento dei bus in MBps o in GBps. La formula è:
CT BUS=
bit f clk
8 2
dove:
bit è il parallelismo del data bus
fclk è la frequenza di clock del bus
2 sta a indicare il fatto che un ciclo di bus è composto da 2 periodi di clock
E’ anche possibile indicare il numero di trasferimenti al secondo in alternativa ai Bps,
usando i Megatransfer [MT/s, GT/s]. In tal modo è possibile esprimere le prestazioni del
bus indipendentemente dal parallelismo o dal timing. In un bus double data rate – in cui
i dati sono trasferiti sia sui fronti di salita e di discesa – con un clock di 100 MHz, il tra sferimento effettivo è di 200 MT/s.
Frequenza FSB
Perchè dunque non far crescere la velocità del FSB per aumentare le prestazioni?
• Innanzitutto perché non ha senso andare velocissimi se poi i livelli sottostanti “tirassero giù” le prestazioni
• Inoltre, da considerazioni di tipo elettromagnetico, si ha che più si aumenta la lunghezza d’onda e più diminuisce la capacità di irradiamento: tradotto, si rischia di
far diventare il nostro dispositivo un’antenna che trasmetta – inutilmente – bit
nell’etere.
Pentium
Segnali di dato
Dipendono dal parallelismo del bus e sono in stretta correlazione con le dimensioni della
cache. Per ogni 8 byte sul bus c’è un bit di parità.
Segnali di indirizzo
Nell’esempio il DBUS è di 64 bit (8 byte). Gli indirizzi sono invece espressi su 32 bit, di
cui 32 – ln2(8) = 29 (A31 – A3) sono direttamente inviati sull’ABUS e i restanti ln 2(8) = 3
sono elaborati per selezionare il primo banco significativo.
I byte sono infatti organizzati in parallelo in modo da poter leggere tanti byte quanti
quelli consentiti dal parallelismo del DBUS.
Dopo la decodifica di un’istruzione come MOV AX, [0BEAC] il processore sa anche quanti
banchi selezionare e attiverà gli opportuni segnali di bank enabled. I problemi nascono
quando devo leggere, ad esempio, dal banco n° 2 per 4 byte. Non posso fare altro che
leggere due volte da memoria: è dunque importante che i dati siano ben “allineati” in
un programma, pena un calo delle prestazioni.
Segnali di controllo
• ADS: il fronte di salita durante T1 segnala la presenza di un indirizzo sull’ABUS
• IO/M: indica se il ciclo di bus fa riferimento alla memoria o a un dispositivo I/O
• R/W: indica se si tratta di un ciclo di lettura o di scrittura
• C/D: indica se sul bus sono presenti dati oppure no
• READY: indica se la memoria è riuscita a rispondere nei tempi previsti, in modo da
poter inserire cicli di wait in caso contrario
Segnali specifici di particolari funzioni
• DMA
◦ HOLD: tirato su a 1 da un dispositivo che vuole acquisire il controllo del bus, riportato a 0 quando il dispositivo rilascia il bus
◦ HLDA: acknowledgement per HOLD
• Interrupt
◦ INTR: segnala la richiesta di interrupt da parte di un dispositivo esterno
◦ INTA: segnala l’accettazione della richiesta da parte della CPU
◦ NMI: indica una richiesta di interrupt non mascherabile
• L1/L2 cache control
• Multiprocessor
• LOCK: indica che un istruzione con il prefisso LOCK è in corso di esecuzione e conseguentemente il bus non può essere utilizzato da un altro potenziale master
◦ Serve per istruzioni che devono essere atomiche anche se fatte in più passi
◦ Se il processore non gestisce questo segnale in HW non posso gestire sistemi
multiprocessor
Cicli di bus
Esistono due tipi di cicli in Pentium: single transfer e burst cycle.
Single transfer
Si parla di single transfer quando viene trasferito un solo dato. Viene utilizzato dalla CPU
prevalentemente quando deve reperire un dato dalla memoria.
E’ interessante valutare un esempio. Si supponga di avere un Pentium, con host bus a
30 MHz interfacciato con una DRAM avente ta = 80 ns. Si possono fare le seguenti considerazioni
un ciclo di bus dura 2TCLK = 2 * 33 ns ≈ 66 ns
la CPU acquisisce i dati dal bus in circa ¾ di un ciclo di bus: 66 ns * 0,75 = 50 ns
ogni ciclo di wait eventualmente inserito ha durata pari a twait = 1TCLK
Ciò detto, deve valere
t a, MEM⩽t a, CPU + nt wait
Nell’esempio, 80 ns ≤ 50 ns + n * 33 ns. Segue n = 1. Più n è alto e più le prestazioni
dell’intero sistema diminuiscono. Bisogna quindi tenere a mente che:
• il throughput dipende da quello che fa accesso al bus
• aumentare la frequenza del bus quando ho n alto si traduce in guadagni tutto
sommato trascurabili
Burst cycle
Un burst cycle è invece usato per aggiornare la memoria cache ed è utile anche nei bus
di sistema per gestire il DMA. Si tenga presente, a riguardo delle cache, che quanto
maggiore è il numero di byte di una linea di cache e tanto più è efficace il principio di località dei riferimenti, ma allo stesso tempo tanto più resta bloccato il bus.
Negli attuali processori il compromesso è ottenuto limitando a 4 il numero di cicli di bus
per aggiornare la cache, ma incrementando il numero dei segnali del DBUS per leggere
un numero elevato di byte. Questi 4 cicli di bus sono particolari, perché sfruttando il fatto che i byte sono adiacenti e gli indirizzi noti a priori la BIU realizza un ciclo di burst.
Solo il primo ciclo di bus è composto da 2 periodi di clock, i restanti da 1. Si parla spesso
di cicli “2-1-1-1” che durano quindi 5TCLK.
Resta inteso che le memorie devono essere in grado di rispondere nelle tempistiche.
Bus pumped
Tecnologia introdotta dal Pentium 4. Per aumentare la velocità di trasferimento possiamo
aumentare il clock oppure il numero di trasferimenti per clock: i bus pumped seguono
questa strategia, trasferendo i dati sui fronti di salita e di discesa.
Nei bus dual pumped il trasferimento è fatto sui due fronti (salita e discesa). Un bus dichiarato a 200 MHz è in realtà un dual pumped a 100 MHz. Nei bus quad pumped, invece, il clock viene sfasato di π/4 e poi derivato, in modo da ottenere 4 trasferimenti in un
periodo di clock. Ad esempio, i Core Duo hanno un FSB con una frequenza di clock pari a
266 MHz: sfruttando il quad pumping si ottiene una frequenza di 1066 MHz. Supponendo
un bus a 64 bit si ottiene
CT BUS=
64
byte∗1066 MHz=8∗1066∗106 byte / s=8,528 GB/s
8
Dual pumped
Quad pumped
Bus di sistema
Un bus di sistema è un bus orientato alla gestione di periferici. Vengono così chiamati in
quanto si occupano di coordinare le risorse del sistema.
Il bus PCI
E’ il bus di sistema per antonomasia. L’initiator è il soggetto che ha il possesso del bus,
il target è il destinatario del ciclo.
Controller
A bordo della singola scheda PCI, infatti, bisogna avere un controller di modulo che gestisce la tipologia dei segnali sia dal punto di vista delle tempistiche che dagli aspetti
elettrici e dalle funzionalità. Importante sottolineare che i moduli, attraverso il controller,
sono programmabili: la programmazione avviene andando a scrivere nella memoria associata ad ogni modulo su un’opportuna area di massimo 256 B.
Serve inoltre un controller bridge per far dialogare il bus PCI con i livelli di bus superiori.
Questo controller è solitamente incluso nel north bridge. Assume una certa importanza
perché deve adattare la velocità del front size bus alla velocità del bus PCI, e quindi “tradurre” i cicli FSB in cicli coerenti per il PCI. Si risolve utilizzando dei buffer.
Vediamo un esempio. Supponiamo che
il FSB viaggi a 300 MHz (TCLK = 3,3 ns)
il PCI viaggi a 33 MHz (TCLK = 30 ns)
e che ci sia da fare una IN AH, 1234H. Il processore, che non può contattare le cache,
acquisisce il bus e fa un ciclo che al peggio dura attorno ai 7 ns, in cui:
• sull’ABUS viene mandato l’indirizzo del periferico
• sul CBUS vengono mandate informazioni che identificano il tipo di ciclo: leggo da
un dispositivo di I/O
L’informazione “devo fare una IN” va a finire al north bridge, il quale trasferisce sul bus
di sistema l’informazione: il ciclo di 7 ns non può andare avanti perché il north bridge
dice di aspettare. La CPU vorrebbe spararsi un colpo in testa mentre vengono inseriti i
cicli di wait. Sul bus PCI viene generato un ciclo che dura attorno ai 60 ns. Nel massimo
della sfiga, oltre ai 60 ns possiamo prevedere l’aggiunta di cicli di wait anche sul bus PCI
se il periferico è lento.
Nel caso di una OUT AH, 1234H la CPU fa più o meno le stesse cose di prima, con la
differenza che in 7 ns il dato va a finire al buffer del north bridge e non ci sono problemi
di stalli sulla pipeline. Sarà poi compito del controller attuare il ciclo.
Linee del bus PCI
• Di sistema
◦ CLK, Reset...
• Indirizzi e dati
◦ 32/64 linee multiplexate
• Arbitraggio del bus
• Gestione degli interrupt
• …
Cicli di bus PCI
Hanno “finezze” maggiori rispetto al ciclo di FSB, in cui mi serve sapere se devo leggere
o scrivere da memoria o da dispositivo. Ci sono dunque una serie di informazioni che costituiscono una sorta di comando su 4 bit (16 possibili configurazioni).
Ciclo di lettura
1. Fase di arbitraggio: viene scelto, tra tutti quelli che hanno richiesto il controllo del
bus, uno che effettivamente lo avrà
◦ In buona sostanza ogni dispositivo è collegato a un arbitro a cui sono inviate le
2.
3.
4.
5.
6.
richieste: al termine di un ciclo tutte le richieste vengono raccolte e si assegna
il grant secondo le politiche implementate sull’arbitro
Assegnazione
Acquisizione del bus (a): l’equivalente dell’ADS si chiama FRAME.
Viene inviato l’indirizzo (b) e potenzialmente potrei avere un ciclo di burst in cui
trasferisco più dati (d) senza modificare l’indirizzo
Viene inviata la tipologia del ciclo di bus (c)
Ci sono due segnali di READY: uno da arte dell’iniziatore e uno da parte del target.
Si decide se inserire uno o più cicli di wait andando a vedere in coppia IRDY e
TRDY.
Evoluzione del PCI
Fino a qualche tempo fa si avevano bus sincroni (= con un clock) paralleli (= più bit alla
volta) che servivano più dispositivi (= collegamento molti-molti). Con l’avanzamento
tecnologico si sono determinate criticità:
• aumento del parallelismo limitato dalla complessità di layout
• aumento del transfer rate limitato da problemi di timing skew e relativi problemi
di sincronizzazione
◦ se ho una pista più lunga di un’altra ho tempi di propagazione diversi
Il concetto di bus parallelo è andato in crisi. Si è andati verso un meccanismo seriale, in
cui si va velocissimi ma “un bit alla volta”. Sui bus tradizionali, inoltre, non posso avere
full duplex.
Con questa filosofia sono mutati anche i bus di CPU. Nei Core i7 notiamo due sostanziali
evoluzioni: le cache vanno tutte dentro al chip e ho collegamenti punto-punto verso la
memoria, il FSB è sostituito dal collegamento punto-punto QPI, X58 è il northbridge che
lavora verso il sottosistema grafico e l’I/O HUB ICH10, non c’è più un bus tra north bridge e south bridge e infine i singoli dispositivi periferici vengono collegati all’I/O HUB.
Si passa dunque da un sistema a bus a un sistema con molti collegamenti punto-punto.
4. SOTTOSISTEMA DI MEMORIA NELL’’ARCHITETTURA 80x86
Generalità
Motivazioni e problematiche
Nelle moderne architetture i problemi riguardano la discrasia prestazionale tra DRAM e
host bus su cui si affacciano i processori, rendendo di fatto inutile lo sviluppo di CPU performanti.
Si valuti il seguente esempio. Si supponga che
BUS @ 100 MHz (TCLK = 10 ns)
DRAM: ta = 40 ns
DBUS @ 64 bit
e si valuti il guadagno prestazionale nel riempimento di una linea di cache (= 4 cicli consecutivi di bus) di un ciclo burst rispetto a cicli single transfer.
In caso di trasferimenti singoli si ha che ogni ciclo di bus dovrebbe durare 2TCLK, ma in
realtà la memoria costringe a introdurre n cicli di wait
3
40 ns⩽( 20+ n∗10)ns
4
Dai calcoli si ottiene n = 3, il che porta ogni ciclo di bus a durare 5TCLK. Si ha quindi che il
tempo impiegato è pari a
5T CLK∗4=(5∗10∗4) ns=200ns
In caso di ciclo burst 2-1-1-1, possiamo estendere i risultati al primo ciclo di bus, mentre
per gli altri, che durano 10 ns, dobbiamo applicare
3
40 ns⩽( 10+ n∗10)ns
4
e dai calcoli si ottiene n = 4. Ne consegue che il primo ciclo di bus dura 5TCLK e, sorprendentemente, anche gli altri. Il guadagno prestazionale che si ha è pari a 0: nel caso del
ciclo burst si è considerato che la memoria risponda in un tempo costante per qualunque indirizzo generato, indipendentemente dal fatto che questo sia vicino o meno a
quelli precedenti.
Come si vedrà nel seguito, una delle soluzioni tecnologiche adottate per risolvere i problemi qui descritti è ricorrere a memorie in cui il tempo di accesso diminuisce se gli indirizzi sono contigui. Si percorrono anche strade di miglioramento architetturale, come
l’interleaving.
Altre problematiche importanti relative alla memoria, ma in secondo piano rispetto a
quelle appena descritte, sono:
• refresh: bloccante
• rilevazione e correzione degli errori: tanto più critico quanta più memoria c’è
• prestazioni del DRAM controller e del chipset: non si possono mandare i segnali
direttamente dal bus, e dunque il controllore dev’essere il più veloce possibile
Organizzazione
La memoria viene organizzata in banchi. Ogni banco è costituito da un insieme di chip.
L’indirizzo sull’ABUS viene parcellizzato in una serie di sottoblocchi che verranno analizzati meglio in seguito e che sostanzialmente selezionano il banco, il rank all’interno del
banco e il chip all’interno del banco.
Vi è un controllore che è abilitato a operare quando c’è un indirizzo di memoria stabile.
Si occupa anche di sincronizzare le diverse tempistiche (= genera il segnale READY) ed
è in grado di “chiedere” cicli di wait.
Per comprendere come vengono gestiti gli indirizzi, consideriamo un’architettura con:
ABUS @ 24 bit
DBUS @ 16 bit
SRAM 1 MB
Due banchi da 512 KB
Ogni banco è realizzato con 8 chip da 64K (= 65536) parole
Ogni parola è lunga 8 bit
Vi sono dunque 65536 KB disponibili su ogni chip. Ogni banco conta 8 chip: 64KB * 8 =
512 KB. Ok.
BLE
BHE
64K
64K
64K
64K
…
…
64K
64K
Per indirizzare la memoria servono ln2(1MB) = 20 bit, così suddivisi
23 22 21 20 19 18 17 16 15 14 13 12 11 10 9
0
0
0
8
7
6
5
4
3
2
1
0
0
3
16
1
•
•
•
A17 – A19: quale degli 8 blocchi da 128 KB (64 KB + 64 KB) seleziono?
A16 – A1: quale delle 65536 parole devo selezionare all’interno del chip?
A0: quale dei 2 banchi (BHE – BLE) seleziono?
In fondo si vedranno ulteriori esempi.
Organizzazione fisica
Viene qui riportato un chip da 1Mb (256K x 4 bit).
Ho tanti piedini di dato DQx quanto è grande la singola parola di RAM. Dato che ho 256K
righe, mi servono ln2(256K) = 18 bit per indirizzare la memoria, ma sul singolo chip ho 9
piedini di indirizzo (A0 – A9): la riduzione dei piedini è possibile utilizzando i segnali RAS
e CAS, che servono a “dividere” l’indirizzo in una parte di riga e in una di colonna.
Tempistiche
Un ciclo base di DRAM è riassumibile col seguente timing
1.
2.
3.
4.
5.
Si manda l’indirizzo di riga (A0-A9)
Si effettua lo strobe per l’indirizzo di riga (RAS)
Si manda l’indirizzo di colonna (A0-A9)
Si effettua lo strobe per l’indirizzo di colonna (CAS)
Dopo un certo lasso di tempo, a seconda dell’operazione scelta (WRITE) si legge
o si scrive (DOUT)
Il parametro che abbiamo sempre indicato come tacc è il lasso di tempo che intercorre tra
la discesa del segnale RAS e il momento in cui i dati sono stabili.
Vi sarebbe in realtà un ulteriore tempo da considerare, in quanto dopo la lettura
dev’essere fatto un refresh per ristabilire la carica del condensatore. Questo tempo è paragonabile con tacc. Il refresh è bloccante, quindi se devo andare a leggere la cella successiva di memoria devo aspettare. La cosa è brillantemente risolta col meccanismo delle memorie interlacciate in cui, supponendo un’architettura a due banchi, gli indirizzi
pari sono su un banco e quelli dispari sull’altro. In questo modo posso ottimizzare i tempi di accesso.
Tipi di DRAM
Ulteriori miglioramenti sono stati introdotti con la tecnologia fast operative, in cui si possono selezionare celle adiacenti senza dover completare un ciclo completo di RAS. Vengono adoperate quando si devono fare trasferimenti di dati con indirizzi adiacenti, come
nel caso dei cicli burst per aggiornare la cache. Esistono tre tipi di fast operative mode,
che categorizzano le DRAM:
• asincrone
• sincrone
• protocol based
Della categoria sincrone – in cui c’è un clock unitario, o comunque fasato, tra bus e memoria interna – fanno parte le SDRAM e le DDR SDRAM.
SDRAM
Sono la base concettuale delle DDR attuali. Ottengono prestazioni migliori tramite un
memory controller più efficiente (burst control) e una sincronizzazione dei clock.
A titolo di esempio, con un bus a 66 MHz (= 15 ns), si possono ottenere cicli burst di tipo
5-1-1-1.
DDR SDRAM
DDR sta per double data rate. Si usa una filosofia dual pumped: sempre presenti RAS e
CAS, ma i dati escono sia sui fronti di salita che su quelli di discesa.
Quando si parla di DDR-num si indicano dunque RAM che lavorano a un clock virtuale di
num MHz e il cui clock reale è ovviamente num/2. Col passare del tempo si sono introdotte le DDR2 e le DDR3, che sostanzialmente aumentano il throughput lasciando invariato il tempo di accesso.
Vediamo ora un breve esempio di come i miglioramenti tecnologici portino un aumento
delle prestazioni.
Si supponga che
DBUS @ 100 MHz (TCLK = 10 ns)
DRAM: ta = 40 ns, ta,next = 5 ns
Se lavoro con cicli single transfer ho 4 cicli di 20 ns. Vale sempre:
3
40 ns⩽( 20+ n∗10)ns
4
da cui n = 3. Ne consegue che ogni ciclo di bus dura 3+2 = 5TCLK = 50 ns. I quattro cicli
si esauriscono in 20TCLK =200 ns.
Con un ciclo burst 2-1-1-1 si ha che il primo ciclo dura 5TCLK e, per i restanti, vale
3
5 ns⩽( 10+n∗10) ns
4
che è soddisfatta anche con n = 0. I restanti cicli dunque durano 1TCLK. In definitiva, il ciclo burst dura 8TCLK = 80 ns. Da queste considerazioni dovrebbe essere evidente come
l’uso di memorie fast operative è ottimo quando si ha a che fare spesso con cicli burst.
DRAM controller
E’ interfacciato a bus e memorie e serve ad adattare tempistiche e segnali delle due
parti in gioco.
Progettazione della memoria
Nel progettare uno schema di memoria si tenga a mente la seguente architettura
Banco 0
Banco 1
Banco 2
Banco 3
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
...
...
...
...
...
...
...
...
...
...
...
...
I banchi sono entità logiche. Ne ho tanti quanto è il parallelismo in byte del DBUS. Per
ogni banco ho un certo numero di rank (evidenziato in giallo), ovvero l’insieme dei chip
che coprono i banchi esistenti.
Il numero di chip per banco è determinato dal parallelismo del chip, tenendo conto che 1
banco ha un parallelismo di 8 bit.
Esempio
Si abbia un’architettura con le seguenti specifiche
DBUS @ 64 bit
ABUS @ 36 bit
DRAM: 8 GB
Chip: 1 Gb (512 M * 2 bit)
Per progettare lo schema di memoria si parte dal numero di banchi presenti. Il DBUS è
su 8 byte (= 64 bit), quindi il numero di banchi presenti è 8.
Ad ogni incrocio banco-rank, dato che il parallelismo di un banco è 2 bit, ho 4 chip “per
banco”. Questo porta a dire che su un rank vi sono 1 Gb * 4 * 8 = 32 Gb. Detto in altri
termini, su un rank ho 4 GB.
Dato che la memoria è da 8 GB posso concludere che questa memoria è formata da 2
rank da 4 GB, ciascuno dei quali è costituito da 8 chip 512M * 2 bit.
Gestione errori
Sin dai primi PC è sorta la necessità di gestire gli errori in memoria, che avvengono con
una probabilità tanto maggiore quanto più grande è la memoria. Gli errori sono causati
da eventi interni o esterni e possono essere temporanei o permanenti.
Si parla in particolare di errori hard quando la situazione di errore è permanente, per
esempio a causa di una saldatura che salta. Gli errori soft sono invece temporanei, causati da particelle alfa o da errori di prossimità dovuti all’induzione.
Bisogna prevenire queste disfunzioni. Sono stati perciò introdotti due meccanismi:
• rilevazione degli errori di parità sul byte
◦ si usa 1 bit per ogni byte di memoria
◦ circuiteria integrata nel processore
◦ è in grado solamente di rilevare gli errori singoli
◦ la rilevazione dell’errore scatena un INT non mascherabile, la cui routine di gestione è memorizzata nel BIOS
•
rilevazione e correzione errori sulla parola
◦ si usano 8 bit (= 1 byte) per ogni 8 byte di memoria
◦ corregge gli errori singoli e rileva quelli doppi
◦ la correzione dev’essere fatta per quanto più possibile real-time
▪ va modificato il valore sul DBUS
▪ controllo via HW con algoritmi di tipo Hamming
◦ diminuzione delle prestazioni nell’ordine del 2%
Nell’esempio precedente sulla progettazione della memoria, si tiene conto del fatto che
bisogna avere 8 bit per ogni 8 byte. Su una memoria da 8 GB, dunque, serve 1 GB per
gestire una parità che consenta la correzione degli errori.
Si deve dunque “immaginare” di progettare un’ulteriore memoria da 1GB. Brevemente,
dato che ho sempre 4 “chip per banco”, vi sono 4Gb sul singolo banco (= 512 MB). Viene dunque affiancato un ulteriore banco di memoria.
5. SOTTOSISTEMA DI I/O NELL’’ARCHITETTURA 80x86
Il modello
Quando si parla di dispositivi di I/O è bene distinguere il dispositivo fisico dall’interfaccia
tra dispositivo e sistema dei bus. Si tenga infatti presente che il processore gestisce e
vede unicamente l’interfaccia, la quale poi si farà carico di gestire i segnali analogici e
digitali che in qualche modo afferiscono al device vero e proprio.
L’interfaccia è quindi un insieme di circuiti che consente a un periferico di interfacciarsi
col bus. E’ saldata direttamente sul dispositivo oppure è connessa mediante gli slot. La
CPU vede solo questa interfaccia logica
in cui:
• il registro di controllo definisce, quando opportunamente scritto, le modalità di
funzionamento del periferico fisico
• il registro di stato, quando letto, permette di conoscere lo stato del periferico
• uno o più registri di dato, bidirezionali a seconda del tipo di operazione (ovvero
possono essere letti o scritti ma non letti e scritti)
Verso i dispositivi, infine, vi è un collegamento punto-punto diverso per ogni device.
Tipi di connessione
Isolated I/O
L’accesso ai registri dei dispositivi periferici è fatto mediante istruzioni di IN/OUT. E’ il
modo di indirizzamento più rapido e permette una maggior disponibilità dello spazio accessibile di memoria fisica.
Supponiamo di avere a che fare con una porta parallela 8255, che ha 3 registri di dato
(0038H, 003AH, 003CH) e uno di controllo (003EH). Lo schema isolated I/O prevede che
l’indirizzo venga diviso in una serie di sezioni al fine di abilitare la circuiteria opportuna.
Ad esempio, supponiamo di avere una IN AL, 38H (= scrivo nel registro di dato A).
Sull’ABUS va a finire
7 6 5 4 3 2 1 0
0 0 1 1 1 0 0 0
I bit A5-A4-A3 selezionano il dispositivo 8255. Il decoder 74F138 viene abilitato solo se i
suoi ingressi di controllo sono 0-0-1: si derivano dunque i segnali A0 (che seleziona un
gruppo di dispositivi), IO/M negato (che determina se il ciclo è su un dispositivo di I/O o
sulla memoria) e si forza un ingresso a 1. Se sono verificate le condizioni di selezione e
abilitazione viene attivato il segnale CS.
Dato che i registri interni all’8255 sono 2, al device arriveranno A1 e A2 che selezionano
il registro interno. Infine, a seconda della decodifica dell’istruzione, verrà attivato uno tra
i segnali RD e WR che indica se sul dispositivo sto compiendo un’operazione di lettura o
di scrittura.
Tutto questo è sempre regolato dal discorso già affrontato a riguardo dei cicli di bus: se il
dispositivo non ce la fa a rispondere in 2TCLK, si invia un opportuno segnale di READY che
porterà all’inserimento di un certo numero di cicli di wait.
Memory mapped I/O
Gli indirizzi dei dispositivi di I/O occupano una parte dello spazio di indirizzamento della
memoria. Non devo usare istruzioni specifiche per trattare i dispositivi, ma lo stesso indirizzo non può essere usato come cella di memoria: nei dispositivi general purpose, in
cui non si sa a priori quanta memoria c’è e come si vuole usare si utilizza lo schema isolated I/O, rendendo il memory mapped preferito nei sistemi embedded.
Lo schema di connessioni è simile a quello visto in precedenza, con le differenze a livello
di segnali IO/M e, in caso di diversa mappatura degli indirizzi, di abilitazione del decoder.
6. GESTIONE DELL’’I/O: INTERRUPT
Gestione dell’I/O
La gestione dell’I/O nelle architetture 8086 può essere fatta:
• via software
◦ polling
◦ interrupt, sebbene ci sia bisogno di un segnale aggiuntivo
• via hardware
◦ DMA
La scelta dipende da quanto è carica la CPU (il polling si può preferire in quelle situazioni
in cui il carico è basso) e dalla presenza di processi più intensi (meglio essere avvisati
che c’è bisogno di gestire un dispositivo di I/O e quindi preferire gli interrupt).
Se si sceglie DMA, si tenga presente che il processore lavora per gli affari suoi e viene
innescato un processo parallelo. In questo lasso di tempo la CPU non può usare il bus.
Parlando dei dispositivi di I/O, questi possono essere divisi in:
• dispositivi a carattere
• dispositivi a blocco
Nei primi la CPU vuole analizzare un singolo byte alla volta ed è rilevante solo il tempo
di accesso. Nei secondi si aspetta la fine di un blocco per fare l’analisi, e oltre al tempo
di accesso è rilevante il tempo di trasferimento.
Interrupt
Principi
A differenza del polling, la CPU non interroga i dispositivi di I/O. Quando un dispositivo
dev’essere servito attiva la richiesta di interrupt. Al termine di ogni istruzione, si verifica
se c’è una richiesta di interrupt e viene servita, eseguendo la procedura di servizio degli
interrupt (interrupt handler). C’è un circuito che gestisce le richieste di interrupt, ovvero
l’interrupt controller.
Un parametro fondamentale da tenere presente quando si parla di interrupt è il tempo
di latenza, ovvero l’intervallo di tempo che intercorre tra la richiesta di interruzione e il
trasferimento del dato da/per l’I/O.
E’ costituito da tre tempi:
• Ti, tempo dell’istruzione corrente: siccome si valutano le richieste di INT alla fine
•
•
di ogni istruzione, è bene considerare il tempo massimo di esecuzione. Può essere
potenzialmente lunghissimo se l’interrupt è mascherabile.
Tc, tempo di attivazione della routine di INT: in sintesi, salvare PC sullo stack, prelevare nuovo PC e caricarlo in PC.
Tr, intervallo di tempo tra inizio della routine e prima istruzione utile: spesso, infatti, salvo prima il contesto del processo interrotto
T lat =T i + T hw +T r
Un sistema ben dimensionato è un sistema in cui il transfer rate del periferico (ovvero il
tempo tra due dati) dev’essere compatibile con Tlat. In particolare, deve valere
1/TR≥T lat
Sarebbe meglio che 1/TR sia molto più grande di T lat: se è solamente “più grande”, si ha
un sistema in cui la CPU fa poco. Se invece è T lat ad essere più grande si va incontro a
perdite di dati.
La massima priorità deve essere attribuita al periferico con massimo transfer rate: così
facendo, un periferico lento può essere interrotto da uno più veloce.
Tipi di interrupt
In un’architettura 80x86 vi sono 256 tipi di interrupt suddivisi in quattro gruppi:
• interrupt interni
◦ trap, fissati dal processore
◦ rilevano situazioni anomale nella gestione dei processi interni
▪ divisione per zero
▪ overflow
• interrupt non mascherabili
◦ mascherabili esternamente nei PC
• interrupt software
◦ impiegati per richiamo ai servizi del SO, tipo SVC
• interrupt hardware esterni
◦ caratteristici dell’architettura
L’elenco qui fatto è in ordine di priorità decrescente: un interrupt interno dunque ha la
priorità su qualunque altro interrupt. All’interno di uno stesso gruppo si disambigua a seconda del numero dell’interrupt.
Vector table
Contiene i puntatori alle routine di servizio di ciascuno dei 256 tipi di interrupt possibili e
si trova in memoria agli indirizzi più bassi (00000H ÷ 003FEH). Ad ogni tipo di interrupt
sono associati 4 byte in modo reale (8 in modo protetto).
Se dev’essere gestita l’INT n, al byte n*4 si trova l’offset IP e al byte n*4 + 2 si trova CS.
Questi due dati determinano dove si trova la procedura in memoria.
Gestire le interruzioni in questo modo permette di:
• consentire al BIOS di caricare gli indirizzi assoluti nella IVT, in modo che noi non
dobbiamo sapere dove sono le routine in memoria
• permettere di bypassare – ad esempio – la routine di gestione dischi, cambiando il
valore contenuto in INT 13
Vi è un flag, IF, che permette di disabilitare gli interrupt. Questo flag è modificabile via
SW con le istruzioni CLI e STI e viene automaticamente resettato all’attivazione di una
ISR. Una particolare categoria di interrupt, detti non mascherabili, non sono disabilitabili
usando il flag IF e rispondono alla INT 2. Si utilizzano in presenza di eventi “gravi” come
la caduta di alimentazione o errori di memoria.
Protocollo di interrupt
1.
2.
3.
4.
5.
6.
7.
8.
9.
Un dispositivo esterno invia una richiesta di interrupt su INTR
Al termine dell’istruzione la CPU verifica se INTR = 1
Se INTR = 1, la CPU invia un impulso su INTA per segnalare rilevazione dell’INT
CPU invia un secondo impulso su INTA per chiedere l’identificativo n del device
CPU legge dal DBUS n
CPU salva nello stack PSW, CS e IP
CPU azzera IF e TF per disabilitare interrupt esterni e trap mode
CPU accede all’elemento 4*n della IVT
Viene attivata la corrispondente procedura di interrupt
Tempistiche e priorità
Si noti che quando arriva l’ACK per l’8259, ovvero il controllore degli interrupt, IF viene
disabilitato. Dobbiamo essere noi a riabilitare le interruzioni.
Se arrivano più richieste di interrupt contemporaneamente, quando arriva il primo ACK
all’8259 si sceglie il dispositivo a priorità maggiore. IR0 ha la più elevata.
Interfacce I/O bufferizzate
Se ho a che fare con input non bufferizzato, l’informazione viene aggregata con un parallelismo consono all’interfaccia. Siccome i tempi di lettura possono essere non compatibili tra loro, potrei andare incontro a problemi di vario genere, superabili con I/O bufferizzato in cui più dati vengono scaricati tutti insieme.
Quando si gestisce un interrupt bisogna infatti considerare:
• il tempo di latenza Tlat definito come il tempo trascorso tra la richiesta di interrupt
e il trasferimento del rimo dato
• il tempo di trasferimento fisico dei dati Ttra dall’interfaccia ai registri
• il tempo di chiusura della routine Tchi in cui si ripristina il contesto mediante una
serie di POP
Se le interfacce non sono bufferizzate deve valere che, con N = 1
T tot =T lat + N∗T tra +T chi
sia molto minore del tempo in cui si scatena un’istruzione.
Esempio
Si supponga di avere un sistema con:
Core @ 2 GHz
PCI @ 33 Mhz
Scheda LAN @ 10 Mbps, interfaccia a 8 bit
Si ipotizzi che Tlat = 1 µs e Tchi = 1 µs. Come può essere gestito l’I/O?
Approccio 1: I/O non bufferizzato
Dalla velocità della scheda di rete si determina che un bit arriva ogni 100 ns. Dato che
l’interfaccia è a 8 bit, viene scatenata un’interruzione ogni 800 ns.
Per determinare quanto vale il tempo di trasferimento, consideriamo che per compiere
una IN AH, … serve un ciclo di bus PCI che dura 2 * 30 = 60 ns.
Si ha
1 µs+ 1∗0,06 µs+1 µs=2,06 µs
incompatibile con un’interruzione scatenata ogni 800 ns.
Approccio 2: I/O bufferizzato
Se supponiamo di avere un buffer di 10 byte, l’interrupt scatta ogni 800 ns * 10 = 8 µs.
Si ha
1 µs+ 10∗0,06 µs+1 µs=2,6 µs
che è compatibile con un’interruzione scatenata ogni 8 µs. Si noti che le tempistiche
sono sulla carta soddisfatte anche con N = 3 (nel caso in esame, dato che l’interfaccia è
a 8 bit, un buffer di 3 byte), ma scegliendo N “al pelo” occupo completamente il sistema
nella gestione degli I/O.
7. INTERRUPT: 8259/APIC - PC INTERRUPTS
Generalità
Dispositivo progettato per minimizzare il software ed i tempi di risposta per la gestione
di livelli multipli di interrupt a diversa priorità. Oggi è embedded dentro il south bridge.
Assume che la gestione degli INT sia vettorizzata. Si possono collegare in cascata: in un
PC è contenuto l’equivalente di 2 dispositivi 8259, il che equivale a dire che è possibile
gestire 15 livelli di interruzione. La cosa condiziona la progettazione dei sistemi operativi
e dell’hardware.
Diagramma a blocchi
I segnali sulla SX sono quelli di interfaccia col bus e si usano per programmare l’8259. In
particolare, i segnali CAS servono per interfacciare più 8259 in cascata. I segnali IR0-7
sono quelli riservati ai periferici: vi sono 8 linee, una per ogni slot/dispositivo di I/O.
L’IRR (Interrupt Request Register) riceve i singoli segnali di interruzione man mano che
arrivano: quando viene settato, viene inviato INT.
La Control Logic, alla ricezione del secondo INTA, legge IRR. Il dispositivo da servire
viene determinato dal Priority Resolver. Mediante l’IMR (Interrupt Mask Register) posso
mascherare specifiche INT.
Temporalmente si ha che:
1. un segnale di richiesta interrupt determina il settaggio del corrispondente bit in
IRR
2. l’8259 valuta le richieste e invia INT alla CPU
3. la CPU conferma e manda il primo INTA
4. si seleziona la richiesta a priorità più alta
5. la CPU invia il secondo INTA
6. l’8259 invia sul DBUS il codice del dispositivo che ha richiesto l’interruzione
7. il ciclo di interrupt è concluso resettando il bit ISR: in modo AEOI avviene in maniera automatica
Il codice del dispositivo è la somma dei termini k e j, dove k è una costante e j è il dispositivo che ha richiesto il servizio.
Programmazione dell’8259
L’8259 viene programmato inviando due tipi di parole di comando.
ICW
Sono parole in ordine fisso, inviate una sola volta in fase di programmazione. Permettono di:
• scegliere se sentire gli INT sui fronti o sui livelli
• impostare una configurazione di 8259 in cascata
• impostare la costante k
• indicare se a un dato pin è collegato un altro 8259 o un dispositivo periferico
• specificare il livello del master a cui è collegato uno slave
• scegliere la modalità AEOI
OCW
Possono essere inviate singolarmente in qualunque fase del programma. Permettono di:
• mascherare i singoli canali di interruzione
• gestire la rotazione delle priorità
Nell’8259, tuttavia, il modo di funzionamento predefinito è il fully nested mode, in cui le
richieste di interruzione sono ordinate per livelli di priorità da 0 a 7, con 0 livello a maggior priorità. Il bit in ISR rimane settato finché la CPU invia un comando di EOI immediatamente prima di tornare dalla routine di servizio dell’interruzione. In caso di AEOI,
ISR viene resettato subito dopo il secondo INTA.
Interruzioni nei PC
Nei PC oggi si possono gestire 15 tipi di interruzioni diverse. Inoltre vi è una fonte di interruzioni non mascherabili, derivanti da:
• errori di memoria non recuperabili
• dispositivi collegati al bus ISA, collegandoli a un piedino opportuno
• tempistiche da rispettare in sistemi real-time
Per ragioni di diagnostica, queste tre fonti di interruzioni sono comunque mascherabili
con un bit.
In caso di architetture multiprocessore, la gestione delle interruzioni si complica in quanto devo determinare a chi inviare un INT che arriva e devo sapere dov’è il vettore delle
interruzioni. Per questo è stato introdotto un I/O APIC che ha la funzione di schedulare
l’interrupt.
8. DMA NELL’’ARCHITETTURA 80x86
Generalità
Nell’ottica di un tradizionale trasferimento da dispositivo a memoria, le operazioni da
svolgere sono:
1. IN dal periferico verso un registro
2. MOV dal registro verso la memoria
Entrambe le operazioni richiedono un ciclo di bus per il fetch e uno per l’esecuzione.
Supponendo di non avere meccanismi di prefetch, mi servono almeno quattro – questo
se non c’è bisogno di inserire cicli di wait – cicli di bus.
Se però non passo dalla CPU in quanto non ho bisogno di interpretare il dato, come può
essere in un input da tastiera, allora posso usare il DMA e fare il trasferimento diretto da
dispositivo a memoria in un ciclo di bus. Affinchè questo sia possibile è necessario che:
• vengano introdotti cicli di bus specifici al fine di selezionare nello stesso ciclo due
risorse esterne
◦ il dispositivo di I/O viene infatti selezionato mediante HW dedicato, mentre
sull’ABUS si gestiscono gli indirizzi di memoria
• un controllore di DMA che possa sostituirsi alla CPU per gestire il ciclo di bus
• un arbitro che assegni il bus alla CPU o al DMA controller
Il concetto di latenza dell’interrupt è estendibile anche al DMA, dicendo che nel peggiore
dei casi è funzione del ciclo di bus e del numero di cicli di wait.
Come faccio a richiedere un ciclo di DMA? Il procedimento è analogo all’interrupt: si
“alza la mano”. Serve un controllore per sapere a chi dare la vittoria e trasferire la richiesta alla CPU o al controllore del bus (north bridge). Chi ha chiesto un trasferimento
in DMA deve ovviamente essere avvertito: HLDA, attraverso un’opportuna rete logica,
controlla il chip select.
Tempistiche e segnali
Tempistica richiesta DMA
1.
2.
3.
4.
Il periferico fa richiesta (DMAREQ)
Il controllore fa richiesta all’arbitro del bus (BUSREQ oppure HOLD)
Il controllore risponde (BUSACK oppure HLDA)
Il DMA controller informa il periferico che si può effettuare il trasferimento
(DMAACK)
Ciclo di bus normale
Il trasferimento di un dato da memoria a I/O avviene normalmente in due passaggi:
1. La memoria è letta mediante ABUS e MEMR (MOV AH, mem)
2. Il periferico è scritto mediante ABUS e IOW (OUT io, AH)
Ciclo di bus DMA
Nello stesso ciclo di bus dev’essere selezionata sia la memoria sia l’interfaccia I/O.
1. La memoria è selezionata mediante ABUS e MEMR/MEMW
2. Il periferico è selezionato mediante un circuito HW e da IOR/IOW
Si passa così da due cicli di memoria ad un solo ciclo. Si noti che quando un periferico
effettua la richiesta al DMA Controller e quando questa viene propagata alla CPU, la CPU
non aspetta di terminare l’istruzione come nel caso degli interrupt, ma il ciclo di bus. Ciò
vuol dire che un’istruzione può potenzialmente essere interrotta a metà.
8237
DMA controller nelle architetture 80x86.
Oggi è integrato nel chipset. Ha 4 segnali di richiesta e ACK (DREQ-DACK) il che vuol
dire che gestisce 4 canali di DMA. Opera con l’ABUS (con relativo strobe) e il DBUS. Ha i
segnali di HOLD e HLDA. Ha i segnali di lettura/scrittura di memoria/dispositivo.
Si notino due cose:
1. c’è un segnale di READY, come lo ha la CPU: se deve scrivere una memoria lenta,
il DMA deve occuparsi della gestione dei cicli di wait
2. i segnali IOR/IOW sono bidirezionali perché quando il DMA è da programmare è
considerato alla stregua di un normale dispositivo
In un PC è integrato l’equivalente di due 8237, per un totale di 7 canali DMA.
Ogni canale DMA contiene:
• un registro indirizzi
◦ viene memorizzato l’indirizzo da cui partire
◦ viene incrementato man mano che si trasferisce
• un contatore di cicli
◦ viene memorizzato il numero di cicli da fare
▪ funzione del periferico e del parallelismo dell’interfaccia
•
◦ viene decrementato man mano che si trasferisce
un registro di modo
◦ IN/OUT
▪ leggo dalla memoria o scrivo in memoria?
◦ single/burst
Per quanto riguarda l’indirizzo, si noti che siccome non so a priori su che ABUS mi affaccio, i 16 bit di indirizzo sono i 16 bit più bassi. Il fatto che escano 16 bit impone un limite
– solo concettuale – al blocco, pari a 64KB. Da ciò consegue che solo una parte di ABUS
è gestita dal registro interno del DMA. La restante parte è memorizzata in un registro
(page register), che è settato dal driver che programma il DMA.
Quando programmiamo un canale dobbiamo dire se lavora in single transfer o in burst
transfer. Nel primo caso facciamo una richiesta di DMA, la esaudiamo e poi ritorniamo
alla CPU. Se però ho un I/O bufferizzato non ha senso tornare alla CPU: meglio dunque
operare in burst transfer. In questo caso finisco gli N trasferimenti programmati tutti in
una volta. Il trasferimento è così più efficiente perché la latenza è considerata solo una
volta e non su ogni ciclo. Si noti che non è possibile sospendere a metà un burst transfer
con DMA: di ciò si tenga conto nel dimensionamento del sistema.
9. INTERFACCIAMENTO MEMORIE DI MASSA
Generalità
Panoramica
Piatto
A) Traccia
B) Settore
C) Traccia di un settore (o viceversa)
D) Cluster
Il disco rigido è costituito da uno o più piatti in rapida rotazione e da due testine per
ogni disco (una per lato), le quali, durante il funzionamento "volano" leggendo o scrivendo i dati. La testina è sollevata dall'aria mossa dalla rotazione stessa dei dischi la cui frequenza o velocità di rotazione può superare i 15.000 giri al minuto.
In termini di prestazioni, le memorie di massa sono le più lente. Si cresce infatti del 10%
ogni anno. Rispetto alle memorie tradizionali le tempistiche sono decisamente più lente.
Negli ambienti desktop c’è molta attenzione al costo e alla potenza, mentre in quelli enterprise sono considerati maggiormente aspetti come performance e integrità dati.
Negli ultimi tempi sono state introdotte le cosiddette unità a stato solido SSD, dispositivi
di storage non meccanici in cui il tempo di ritrovamento del dato è prossimo alle frazioni
di millisecondo, in quanto non si deve attivare nessuna parte meccanica. Si basano su
una tecnologia EEPROM (Electrical Eraseable Programming). Sono silenziosi e ad elevata
velocità, ma essendo introdotti da poco non si sa con certezza se il MTBF sia più elevato
di un tradizionale HDD.
Il modello
La memoria di massa, delineata dal rettangolo blu, comunica con il sistema attraverso
un’interfaccia. Quando parliamo di interfaccia è bene distinguere l’interfaccia fisica –
che si trova dentro il case del disco e ne gestisce la fisicità – dall’interfaccia verso il bus.
Nel disco si trova anche il controller, che può avere diversi standard di interfaccia (ATA,
SATA, SCSI, USB, FC) i quali definiscono sia la connettività fisica che quella logica. Si tendono a preferire collegamenti seriali ai paralleli per evitare fenomeni di bit skew (in cui i
dati partono contemporaneamente e arrivano a tempi diversi, degradando le prestazioni), avere cavi più fini e con meno segnali da gestire.
Gli standard più diffusi sono SATA (evoluzione seriale di ATA), SAS (evoluzione seriale di
SCSI, usato in elaboratori a prestazioni elevate) e FATA (per l’ambiente enterprise).
Parametri
Il parametro principale di cui tenere conto è la latenza, somma di due contributi
T lat =T seek + T rot
dove
Tlat è il tempo di latenza del disco
Tseek è il tempo di seek, ovvero il tempo necessario a posizionare la testina sopra la
traccia desiderata
Trot è la latenza di rotazione, ovvero il tempo di mezza rotazione del disco
Più è alto il numero di rotazioni in RPM, minore è la latenza del disco.
Esempio
Calcolare il tempo medio di lettura/scrittura di un settore di 512B, dato un HDD avente
Velocità di rotazione: 5400 RPM
Tseek = 12 ms
vtrasf = 5 MB/s
Tctrl = 2 ms
La latenza si calcola facilmente utilizzando la formula sopra riportata. Per determinare il
tempo di rotazione bisogna considerare la corrispondenza 1 Hz=60 RPM , da cui Trot =
0,01/2 s = 5 ms. Il tempo di lettura lo determino dalla velocità di trasferimento.
tempo di seek
latenza di rotazione
tempo di lettura
tempo del controllore
12 ms
5 ms
< 1 ms
2 ms
In sostanza, per leggere 512 B ci vanno 20 ms. Il 90% di questo tempo è impiegato nel
“trovare” i dati.
IOPS
Un altro modo per definire le prestazioni del sistema è utilizzare il numero di comandi di
I/O per secondo che un disco è in grado di smaltire. La misura è indice delle prestazioni
di esercizio del sistema ed è aumentabile inserendo a bordo del disco delle cache non
volatili in lettura. Utilizzando cache non volatili, oltre a migliorare le tempistiche, posso
non far attivare tutte le parti meccaniche del disco, il che si traduce in minori consumi.
RAID
RAID sta per Redundant Array of Inexpensive Disk. Sono sistemi che impiegano combinazioni di 2 o più dischi per ragioni di fault tolerance e per migliorare le prestazioni.
Le combinazioni RAID più usate sono:
• RAID 1, in cui ho un mirroring dei dischi. In altre parole, i dati vengono scritti in
parallelo su due dischi.
• RAID 5, in cui oltre ai vari blocchi memorizzo anche un’informazione di parità.
Questa viene messa sui vari dischi in modo che la probabilità di guasto sia distribuita uniformemente.
• RAID 6, come RAID 5 ma con una doppia parità.
RAID 1
RAID 6
RAID 5
Organizzazione fisica e logica
Codifica dei bit sul supporto
Bisogna innanzitutto tenere presente che l’informazione 0-1 è codificata sul disco con
una certa fisicità. Il parametro di riferimento è la densità di bit per inch, dipendente dalla densità di cambiamenti di flusso per inch. Occorre un algoritmo per correlare i cambiamenti di bit ai cambiamenti di flusso.
Data una sequenza di bit si ha il problema che la rotazione non è perfettamente costante. E’ bene dunque avere un clock implicito o esplicito nell’informazione. Le codifiche devono dunque avere anche un’informazione di sincronizzazione. Ve ne sono tre: FM, MFM
e RLL.
FM (Frequency Modulation), in particolare, usa per ogni bit dagli 1 ai 2 impulsi: il primo
per identificare l’inizio del bit, il secondo alto solo se il bit è un 1. Per questo motivo ho
1,5 cambiamenti di flusso in media per ogni bit. Le tecniche MFM e RLL sono un’ottimizzazione di questo modo di procedere, assumendo che la frequenza di clock resti stabile
per brevi intervalli di tempo.
Formattazione
Un dato viene memorizzato sul disco insieme a alcune informazioni di controllo. Queste
informazioni di controllo mi dicono dove inizia la traccia e definiscono il perimetro fisico
di ciascun settore. Quelle relative al perimetro fisico hanno gli scopi di identificare il settore, rilevare eventuali errori e dare informazioni di sincronizzazione al controllore.
Traccia
Settore
Dati
Gap
Attenzione: non è detto che nella traccia X il settore Y sia fisicamente seguito dal settore
Y+1. C’è necessità di distinguere tra numerazione logica e posizionamento fisico se è
presente dell’interleaving tra i settori.
I sistemi operativi infatti vedono i dischi come suddivisi in partizioni, ciascuna delle quali
è un insieme di blocchi di settori adiacenti logicamente, altresì detti cluster. Se c’è interleaving pari a 0, allora ho corrispondenza tra adiacenza logica e fisica.
Perchè i sistemi operativi lavorano coi cluster? Perchè c’è interesse a ottimizzare i tempi
di lettura e scrittura, in quanto dobbiamo avere la minima latenza.
File system
L’organizzazione dei cluster in file è funzione del file system che abbiamo. Ci sono fondamentalmente due file system: FAT32 (File Allocation Table) e NTFS (New Technology
File System). In FAT32 non posso avere file più grandi di 4GB e dischi più grandi di 2 TB.
In NTFS ho limiti più laschi, utili per applicazioni multimediali.
FAT32 è un file system basato su una tabella in cui ogni elemento è in correlazione biunivoca con un cluster del disco. Una partizione FAT è organizzata in 4 sezioni diverse
Ogni record della FAT, che è memorizzata in due copie per questioni di ridondanza, è
composto da 5 campi:
• il numero del cluster successivo
• uno speciale campo che indica la fine della catena
• cluster danneggiato
• cluster riservato
• cluster libero
Nel direttorio è presente per ogni file il suo nome ed una serie di altri parametri, oltre al
primo elemento della FAT relativo a quel determinato file.
Organizzazione del disco
Si usa una partition table, ovvero una struttura dati che definisce la suddivisione del disco in porzioni. In ogni singola entry sono indicati inizio e fine di ogni partizione. Per ogni
partizione ho un settore di boot che contiene una serie di informazioni e il loader del SO.
Il primo settore è detto master boot record (MBR) ed è quel settore dell'hard disk composto dai primi 512 B del disco che contiene la sequenza di comandi/istruzioni necessarie all'avvio (boot) del sistema operativo, tipicamente il boot manager/boot loader del sistema. Qualunque versione FAT ha in comune i primi 36 B.
Meccanismi di indirizzamento
•
•
Cilindro-testina-settore (CHS): al driver fisico chiedo un qualcosa che sta in una
ben determinata posizione. Devo conoscere la geometria del disco.
Chiedo al disco un elemento in una data posizione logica (LBA)
La relazione tra LBA e CHS dipende dalla geometria del disco ed è fatta dal controller.
Zone bit recording
Dischi che hanno un numero variabile di settori per traccia in funzione della posizione
del cilindro. Hanno meno settori per traccia nella parte interna e più settori per traccia
nella parte esterna. Con questi dischi possiamo avere ad esempio 14 zone con un numero variabile di settori per traccia (da 792 a 370) e transfer rate più alti nelle zone ester ne rispetto a quelle interne.
Tale geometria non è compatibile con l’indirizzamento CHS dei dischi e le chiamate
BIOS, che assumono omogeneità di settori per traccia. L’INT 13 deve dunque essere
redirezionata.
Interfaccia fisica di basso livello
Il controller del disco si occupa di far girare il motorino, scrivere i dati in serie sul disco
con tutto il preambolo e la parte in chiusura... Per gestire questo ho un normale dispositivo di interfaccia (controllo, stato e dato) come visto in precedenza. E’ collegato
all’8259 in posizione 6.
L’INT 13 gestisce il controller in tre fasi:
• command phase, in cui si scrive nel controller il comando (= una sequenza di
byte) relativo alle funzioni da eseguire
• data phase, in cui si esegue il comando. Se ad esempio abbiamo a che fare con
un comando di lettura e scrittura, questo può essere fatto in DMA
• result phase, in cui il controller invia un interrupt fornendo il risultato dell’ultimo
comando eseguito
INT 13
L’INT 13 fornisce funzioni per l’accesso diretto al disco. Queste funzioni sono implementate dal BIOS e dunque possono essere usate senza bisogno di driver. A seconda del
valore contenuto nel registro AH posso chiamare differenti funzioni per i vari settori (lettura, scrittura, verifica) o per formattare una traccia. In DL specifico il disco. In DH, CL e
CH specifico i parametri CHS e in ES:BX predispongo un buffer per l’I/O.
Vengono restituiti codici per informare sull’esito dell’operazione, come ad esempio Sector Not Found.
Nelle evoluzioni del BIOS 4 bit dei settori sono stati assegnati ai cilindri, passando a parità di bit da un sistema 256/256/256 (256*256*256 = 16 M settori = 8 GB) a uno
1024/256/63. Il problema è che il disco fisico riceve dei comandi e non è detto che gli
standard tra disco e BIOS siano compatibili con gli standard tra BIOS e SO. Questo spiega perché su alcuni SO, pur avendo dischi di una certa dimensione, se ne riesca a gestire solo la metà.
10. STORAGE
Generalità
Nel seguito si andrà a parlare di quelli che gergalmente vengono chiamati “armadi”, ossia quello che nei data center viene chiamato storage. La filosofia di gestione del singolo
disco è sempre la stessa, ma qui abbiamo differenti problematiche.
• mettere insieme una numerosità di dischi per fare una massa e un volume
• avere dei protocolli tra l’armadio e il server, o i server
JBOD
Acronimo per Just a Bounch Of Disk. Usiamo i dischi “così come sono”. Quando scaliamo
come dimensioni e prestazioni abbiamo problemi di:
• capacità
• performance
• affidabilità
RAID
Questi problemi sono mitigabili collegando in array più dischi. Già affrontato il discorso
nell’unità 9. Interessante citare anche il RAID 0 che divide i dati equamente tra due o più
dischi con nessuna informazione di parità o ridondanza (operazione detta di striping).
Molti controllori di disco possono annidare alcuni livelli di RAID. Configurazioni molto diffuse sono RAID 0+1 e RAID 1+0. Quest’ultimo è più robusto perché può sopportare rotture distribuite su sistemi diversi.
Il problema delle configurazioni RAID è che le prestazioni non scalano bene se sono coinvolti un gran numero di dischi.
IOPS
E’ una quantità importante per valutare le prestazioni, ma non è l’unica. A titolo di paragone per le successive analisi, si consideri che un disco SATA a 7200 RPM può fornire
sino a 150 IOPS.
Migliorare le prestazioni
Ordinando le soluzioni per miglioramento e costo crescente, si può:
• aggiungere memoria al server
• aggiungere memoria cache al disk drive
◦ posso arrivare a picchi di 370 IOPS
• aggiungere memoria al disk controller
◦ posso arrivare a picchi di 2500 IOPS
• usare unità SSD
Storage
Lo storage rappresenta la possibilità di salvare informazioni per lunghi periodi di tempo
in infrastrutture informatizzate in grado di garantire la coerenza e la consistenza indipendentemente dalle condizioni di funzionamento dei singoli sistemi.
Il crescente diffondersi di tecnologie e risorse computazionali ha fatto si che le informazioni da salvare diventassero ogni giorno più grandi, riducendo così le potenzialità e
l'utilità offerta dai singoli strumenti di stoccaggio (dischi etc...)
Sono così nate tecnolgie che hanno avuto lo scopo di aggregare singole unità disco per
realizzare infrastrutture in cui non vi fossero limiti fisici alla quantità di spazio disco allo-
cabile, e nel contempo centralizzando tali risorse su di un unico dispositivo per ridurre il
costo di gestione di un insieme complesso di sistemi differenti. Le best practices nel settore sono:
•
•
•
•
DAS (Direct Attached Storage)
NAS (Network Attached Storage)
SAN (Storage Area Network)
IP SAN (IP Storage Area Network)
Equilibrare al meglio un sistema di storage è importante. Se ad esempio abbiamo dischi
da 400.000 IOPS e una banda di 1 Mb/s non riesco a sfruttare le potenzialità dei dispositivi. Bisogna inoltre notare che la latenza è dipendente dal carico, dalla rete e dalla distanza da percorrere. In sistemi a più alte prestazioni, la latenza rimane pressappoco costante anche in presenza di alti carichi di lavoro.
DAS
DAS è l’acronimo di Direct Attached Storage. Si tratta di una configurazione in cui il disco è direttamente collegato al computer, internamente o esternamente.
E’ l’ideale per la fornitura locale di dati a un server. E’ una soluzione a basso costo,
estremamente facile da realizzare e da configurare. Garantisce una discreta affidabilità.
I computer devono essere connessi direttamente allo storage. Ho diversi point of failure.
Ho una scalabilità “buonina” e la manutenzione richiede fermi macchina.
DAS Interno
Il disco è dedicato al mio computer, che provvede a:
• partizionare e gestire i volumi
• file system e disposizione dei dati
• accesso ai dati
I dischi sono gestiti individualmente dal SO o, in caso di RAID controller evoluti, dal controller stesso. Ultimamente si utilizzano collegamenti seriali ad alte prestazioni. Ci sono
forti limitazioni sulla lunghezza del collegamento e sulla scalabilità, in quanto posso avere un limitato numero di device.
DAS Esterno
Gestito a livello di array, perdo la visibilità del singolo disco. Può avere maggiore scalabilità. Le performance sono dipendenti dal tipo e dal numero di dischi, dalla presenza di
cache, dal tipo di bus, dal tipo di controller e protocolli utilizzati, nonché dal livello RAID.
Normalmente ho connessioni SAS o SATA. Con Fiber Channel posso avere lo storage dislocato anche a una settantina di chilometri.
NAS
NAS è l’acronimo di Network Attached Storage. Lo storage è condiviso su una infrastruttura di rete IP.
C’è un NAS Device che, per mezzo di una cosiddetta testa NAS, espone servizi di storage su una rete IP: un esempio può essere un classico media center. E’ un sistema economico perché i server non hanno bisogno di particolare HW per accedere a questo sistema. Efficiente, flessibile, con uno storage centralizzato. Semplice, scalabile. Posso aggiungere sicurezza con clustering e/o mirroring. Lo storage può essere collegato internamente o esternamente utilizzando protocolli FC, SAS, SATA.
Posso avere anche una configurazione con gateway NAS, in cui io client mi collego a un
server che fa da frontend. Il server fa da ponte e non ha all’interno i dischi, che sono collegati all’esterno (ad esempio in rete).
Rispetto a un file server, un NAS server non è una macchina general purpose, ma un si-
stema ottimizzato per la gestione di file. I servizi di storage offerti sono a livello di file: si
utilizzano protocolli come NFS (usato in ambiente Unix: consente di gestire il FS come se
fosse locale, gestendo accesso concorrente, permessi e visibilità) e CIFS (l’equivalente
per Windows: l’utente ha la percezione di aver montato un disco remoto come se fosse
locale).
SAN
SAN è l’acronimo di Storage Area Network: ho una rete dedicata allo storage. Mentre nel
caso delle NAS sulle reti IP transitano dati storage e connettività di rete, con una soluzione SAN posso avere una divisione tra le reti e quindi sui dati che vi transitano.
C’è dunque più sicurezza e attenzione per lo storage. E’ gestita sulla falsariga di una
rete IP. Costa molto perché mi servono schede di rete e hardware dedicati. Normalmente
si tende a fare ridondanza hardware mediante l’installazione di più host bus adapter per
sfruttarla al meglio e mantenere la connettività.
E’ un’ottima soluzione per dati importanti che devono essere backuppati con rapidità. La
scalabilità è ben gestita.
I componenti in gioco sono:
• Storage array, ovvero un armadio pieno di dischi con controller ridondanti ad alta
velocità. Ogni tipo di conoscenza sulla localizzazione dei ati è perduto: i server si
interfacciano con dei controller. Gli array sono ad alta ridondanza, in modo da
avere elevata fault tolerance nella gestione di business continuity (= servizi per
banche e grossi business che devono avere durata di offline imprevisti prossima
allo 0).
• Fiber Channel fabric, ovvero una rete per lo storage con tutti i suoi switch. E’ uno
spazio virtuale usato dai nodi per comunicare con tutti gli altri mediante due identificatori (Domain ID e WWN)
• Host bus adapter, ovvero una scheda di espansione per connettere un dispositivo
di storage all’interno di una SAN. Effettua funzioni di interfaccia a basso livello,
minimizzando le operazioni da delegare alla CPU.
• Cablaggio in fibra, per collegare i computer alla rete Internet
• Software di management, molto importante per gestire il controllo accessi, gli ar-
ray di dischi e i LUN (= unità di memorizzazione presentata al client)
Fibre Channel SAN Connectivity
Per lo storage utilizzo gli stessi principi del networking. I server sono dunque connessi a
due reti distinte. La maggior parte delle reti storage usa il protocollo SCSI per comunicare tra i server e i dispositivi di memorizzazione. E’ molto usato il protocollo FCP (Fiber
Channel Protocol), che consente di veicolare sulla SAN i comandi SCSI.
Ad ogni nodo di una SAN è assegnato un indirizzo unico di porta o Port ID, che viene utilizzato per far comunicare i nodi di una SAN ed è simile a un indirizzo IP. Ad ogni porta
invece è assegnato un World Wide Name WWN, identificatore univoco analogo al MAC
che identifica univocamente una porta.
Zoning
Il fabric può essere suddiviso in zone agendo sugli switch. Così facendo posso migliorare
il partizionamento e aggiungere sicurezza in quanto può non essere positivo, ad esempio, mischiare richieste Linux a richieste Windows. Si può fare per Port ID o WWN.
LUN Masking
Posso configurare i controller in modo da presentare agli host solo alcuni dischi LUN. E’
un’operazione estremamente complicata.
IP SAN
Le SAN sono ottime, ma costano care e hanno un grande impatto sull’infrastruttura. Le
IP SAN nascono per fare delle SAN mediante collegamento su rete IP. Si riesce a fare una
cosa che funziona anche a grande distanza. E’ una soluzione relativamente a basso costo. Pago un po’ in termini di latenza.
Per trasferire blocchi di dati usando TCP/IP si utilizza il protocollo iSCSI, ovvero un porting di SCSI su IP. L’incapsulamento è fatto dall’host. E’ una soluzione economica e fles-
sibile che non fa uso di HBA, ma che allo stesso tempo fornisce prestazioni inferiori rispetto a una SAN.
Fiber Channel over Ethernet
Consente di encapsulare i frame FC su Ethernet. Il problema è dettato dal fatto che
Ethernet è a livello 2, quindi diventa difficile da veicolare globalmente. Inoltre si rischiano perdite di pacchetti e non ci sono tutti i meccanismi di garanzia offerti dal TCP. Funziona bene solo in caso di reti perfette, oppure implementando standard di QoS per avere una lossless Ethernet.
Serve un Converged Network Adapter CAN che fornisce le funzionalità di NIC e HBA,
connessioni che usano la stessa infrastruttura Ethernet e switch FCoE.
Repliche
Posso replicare le informazioni sullo storage secondario in modo sincrono: quando scrivo
sia sul master che sullo slave ricevo un ACK relativo alla scrittura. Ho la certezza di aver
scritto qualcosa su entrambi i dispositivi. Alternativamente, si può procedere in modo
asincrono: lo storage primario replica i dati in maniera indipendente dall’host. E’ molto
più veloce ma non offre garanzie.
Modo sincrono
Modo asincrono
Riepilogo
DAS
NAS
SAN
+ Soluzione di basso costo
+ Semplice da configurare
+ Ambienti eterogenei
+ Storage centralizzato
+ Ambienti eterogenei
+ Gestione centralizzata
dello storage
+ Alta affidabilità e tolleranza ai guasti
+ Ottime performance
+ Storage Consolidation
+ Backup & Restore rapidi
ed efficienti
+ Altissima scalabiltà
- Storage distribuito
- No Storage Consolidation
- No High Availability
- Basse performance?
- Basse performance
- Molto costoso
- Scalabilità limitata
- Possibile congestione della
rete soprattutto durante
Backup & Restore
- Limiti dell’Ethernet
NAS vs. SAN
Non affidabile
Affidabile
Alto overhead CPU
Basso overhead CPU
Grande numero di blocchi Blocchi dati grandi
piccoli
LAN Backup
Backup non a carico delle
LAN
Accesso ai file
Diretto accesso alle LUN
Usi tipici NAS – Usi tipici SAN
* File sharing tra utenti
* Contenuti multimediali
* Device di boot per workstation
…
* Condivisione di librerie di
backup
* Mirroring a lunga distanza
* Accesso ridondato ad alta
velocità ai dischi di grandi
DB
...
11. MODO PROTETTO NELL’’ARCHITETTURA x86/32
Generalità
Il modo protetto si differenzia dal modo reale non tanto dal punto di vista dalla scrittura
delle istruzioni che rimangono le stesse, quanto per la modalità con cui le istruzioni vengono eseguite. Si declina in una serie di sottomodalità che prevedono ad esempio differenze sull’ampiezza degli offset, a seconda della scelta di operare a 32 bit o 64 bit.
Vi sono due differenze sostanziali tra modo reale e modo protetto:
• gestione della memoria
◦ ovvero, il modello di memoria che sottende l’esecuzione delle istruzioni
• gestione dei privilegi e delle priorità
◦ non è più tutto allo stesso livello: esistono ambienti con privilegi maggiori e altri con privilegi minori
In modo reale l’indirizzo è determinato a livello di istruzione come illustrato nell’unità 2.
Gli indirizzi fanno riferimento diretto alla memoria fisica, seppur con un meccanismo di
segmentazione della memoria fisica stessa, mediante 20 bit (1 MB).
Modo protetto
Nel modo protetto la cosa si complica. Si ha a che fare con tre indirizzi:
• memoria virtuale o logica, ovvero uno spazio di memoria visibile dall’architettura
del processore
• memoria lineare, ovvero uno spazio di memoria gestito dall’architettura mediante
un indirizzamento lineare
• memoria fisica, ovvero lo spazio di memoria direttamente indirizzabile dall’ABUS
• memoria reale, ovvero la porzione di memoria fisica realmente disponibile
Gli indirizzi logici sono espressi su 46 bit, il che equivale ad avere un’indirizzabilità di 64
TB sulla memoria logica/virtuale. Un indirizzo logico è composto da due parti
• un registro di segmento, su bs = 14 bit
• un registro di offset, a bo = 16/32/64 bit a seconda dell’architettura del sistema
La divisione nasce dal fatto che tale memoria di 64 TB è suddivisa in una serie di seg menti non sovrapposti: ciascuno dei 2bs = 16K segmenti è costituito da 2bo bit (nel modello a 32 bit, ad esempio, la profondità di un segmento è 4 GB). Questo modello di memoria si definisce memoria segmentata.
La memoria logica viene mappata, mediante il processo di segmentazione, in una memoria lineare in cui ciascun segmento può esservi in toto o in parte mappato. Siccome
non tutti i sistemi hanno 4 GB di memoria vi è un ulteriore passaggio, mediante la pagi-
nazione, da memoria lineare a memoria fisica. Nei processori della famiglia x86/32,
l’ABUS è di 32 bit il che vuol dire che la memoria fisica può essere, nella sua dimensione
massima, pari alla memoria lineare. In questo caso, il processo di paginazione è disattivabile.
Da memoria logica a memoria lineare
Registro di segmento
14 bit
Offset
32 bit
L’obiettivo è passare da un indirizzo su 46 bit articolato in due porzioni a un indirizzo su
32 bit. Per ottenere questo ci si avvale, segmento per segmento, di un descrittore. Un
descrittore di segmento è un’entità che contiene una serie di informazioni del segmento
stesso. Queste informazioni sono:
• bit di presenza: “il segmento è in toto o in parte mappato sulla memoria lineare?”
• indirizzo in memoria lineare: espresso su 32 bit, indica l’indirizzo a partire dal
quale la determinata porzione di segmento è portata in memoria lineare
• limite: siccome io posso portare in memoria tutto il segmento o porzioni di esso,
devo specificare quanti byte ho portato in memoria lineare
• tipo di segmento: “ho a che fare con un segmento codice o con uno di dati?”, utile perché se sono in un segmento dati non posso saltare in un segmento di codice
• permessi sul segmento
• livello di privilegio, ovvero un grado di nobiltà del segmento decrescente da 0 a 3
• referenziazione in scrittura: “il segmento è stato referenziato in scrittura?”, utile
perché se dev’essere tolto dalla memoria lineare potrei aver bisogno di riscrivere
i dati in memoria. E’ l’hardware di decodifica ad accorgersi se un segmento viene
referenziato in scrittura
Ogni descrittore occupa 8 B e in totale abbiamo 16K descrittori, tanti quanti sono i segmenti. I descrittori sono suddivisi in due grandi tabelle, GDT Global Description Table e
LDT Local Description Table, entrambe di 8 KB. Questo suggerisce che la memoria virtuale di 64 TB è suddivisa in due parti di 32 TB, una riservata al sistema e l’altra a dati e
processi utente. I descrittori della prima si trovano nella GDT, quelli della seconda nella
LDT. Gli indirizzi fisici di GDT e LDT sono contenuti in due appositi registri.
Per generare l’indirizzo lineare il processo è il seguente:
1. si prendono i 14 bit del registro
2. si va al relativo descrittore ottenendo l’indirizzo di base (= dove quel segmento è
mappato nella memoria lineare) su 32 bit
3. si somma a questi 32 bit l’offset contenuto nell’indirizzo virtuale
Supponiamo di avere una MOV AX, [EBX] e si supponga che DS = 10 e EBX = 200.
Tramite il descrittore del 10° segmento si recupera il corrispondente indirizzo di base in
memoria lineare e si somma a EBX. Prima di eseguire l’istruzione, si verifica se l’indirizzo generato rientra nella compatibilità con il limite: in caso negativo l’istruzione non viene eseguita in quanto prenderei un dato al di fuori della zona di memoria che è allocata
al mio processo. Si ottiene così un indirizzo lineare su 32 bit.
Siccome ogni istruzione eseguita in modo protetto deve fare riferimento al descrittore,
diventerebbe parecchio pesante recarsi ogni volta in memoria. Per questo, quando viene
modificato un registro di segmento, c’è una piccola cache nel processore che memorizza
il descrittore corrente.
Privilegi
Abbiamo un modello basato su un concetto: io ho tutta una serie di entità (tabelle, segmenti di codice, singole istruzioni, tabelle di sistema...) che manipolo. Ad ogni entità, in
fase di inizializzazione, viene attribuito un livello di privilegio. Nel momento in cui ad
esempio si lancia un processo (= un segmento, nel modello di memoria segmentato)
viene battezzato con un livello di privilegio. Si lavora in un modello gerarchico: il livello 0
ha il massimo privilegio.
I livelli di privilegio realizzano fondamentalmente due regole:
• qualità dei dati
◦ ogni istruzione ha un livello di privilegio relativo al livello di privilegio del processo in cui l’istruzione viene eseguita: chi è a livello L può manipolare dati a
livello L o a livelli inferiori.
• qualità del codice
◦ se chiamo una subroutine dev’essere al mio livello L o a un livello superiore
Da memoria lineare a memoria fisica
Il modo protetto prevede la gestione della paginazione, effettuata con lo schema qui riportato. La memoria è suddivisa in pagine di 4 KB.
Notevole sottolineare come nei processori di ultima generazione è possibile avere una
profondità di pagina che è maggiore dei 4 KB e che può spaziare da 2 MB a circa 1 GB.
E’ meglio avere pagine grandi o piccole? C’è un trade-off: uanto più le pagine sono piccole, tanto più posso portare molte porzioni piccole in memoria fisica (e ovviamente,
quanto più le pagine sono grandi, tanto più posso portare poche porzioni grandi in memoria fisica).
Primo livello
10 bit
Secondo livello
10 bit
Offset
12 bit
Incominciamo a capire come avviene il meccanismo di paginazione nelle architetture
x86. Ho un indirizzo lineare di 32 bit, eventualmente generato dalla segmentazione, da
tradurre in un indirizzo di memoria fisica. Se abbiamo le pagine di 4 KB, i 12 bit meno significativi determinano l’offset nella pagina. La parte più a sinistra che va sull’ABUS è
funzione dei 20 bit restanti: siccome in x86 le tabelle di paginazione sono a due livelli, i
20 bit vengono suddivisi in due parti.
Partendo dal contenuto del registro CR3 (indirizzo fisico in cui le tabelle sono contenute
in memoria), i primi 10 bit selezionano una delle 1024 locazioni, ciascuna di 8 B,
all’interno della prima tabella. Ciascuna entry è costituita da due informazioni fondamentali:
• un bit di presenza in memoria
• 20 bit che servono a memorizzare l’indirizzo di testa di una tabella
Ogni tabella del secondo livello contiene i 20 bit di testa dell’ABUS. Anche qui c’è un bit
di presenza, nonché due ulteriori informazioni che mi dicono se la pagina ha privilegio di
tipo user (livello 3) o privilegio di tipo system (livelli 0-1-2). Per accedere a una pagina di
tipo system, naturalmente, devo avere un processo con livello 0, 1 o 2.
Si noti che queste tabelle non sono poche: abbiamo 1024 ingressi della prima tabella, e
ogni ingresso è 8 B. Per ciascuna entry ho una tabella da 8 KB, il che porta a 8 MB di occupazione complessiva in memoria.
Tutto il meccanismo di calcolo dell’indirizzo da indirizzo virtuale a indirizzo fisico
dev’essere fatto in tempo reale. Come per la segmentazione, anche per la paginazione
dev’esserci un meccanismo di cache: si aggiunge dunque una Table Lookup Buffer TLB
cache, memoria associativa che contiene, per i vari indirizzi logici su 20 bit usati più recentemente, i corrispondenti 20 bit di inizio della pagina. Vi sono anche informazioni sul
livello di privilegio della pagina, sull’accessibilità di essa in lettura/scrittura e un’indicazione relativa a eventuali modifiche della pagina.
Considerazioni e conclusioni
Molte volte, in alcuni SO, il meccanismo di paginazione è l’unico attivo. La segmentazione, che per definizione prevede più segmenti, viene costruita in modo tale che tutti i
segmenti partano dallo stesso indirizzo lineare: tutti i segmenti sono sovrapposti, e quindi c’è una “falsa segmentazione”.
In fase di reset viene reso attivo il modo reale. Dopodichè, prima di passare al modo protetto, bisogna fare una serie di operazioni:
1. avere nella GDT almeno 3 descrittori: codice, stack e dati
2. inizializzare la IDT Interrupt Description Table, diversa a seconda del modo (in
modo reale contiene coppie CS:IP di 4 B, in modo protetto si scrivono descrittori
di 8 B del segmento di codice da avviare)
3. opzionalmente, se la paginazione è abilitata, scrivere in CR3 l’indirizzo delle tabelle di paginazione e inizializzarle
4. settare un bit in CR0 per dar inizio al modo protetto
5. eseguire un JMP FAR per azzerare la coda delle istruzioni caricate
6. caricare in DS, SS i selettori
Memoria in Windows
I 4 GB di memoria lineare vengono suddivisi dai sistemi operativi in due porzioni: nel
modello Windows i 2 GB più bassi sono riservati ai processi utente e cambiano quando
cambia il processo utente, mentre quelli più alti sono fissi per il sistema operativo.
Gestione HW dei task
Un’altra funzione estremamente rilevante gestita via HW è il task switching. Quando si
commuta di processo bisogna salvare il contenuto dei registri, ripristinandolo quando il
SO rilancia il processo. Siccome bisogna salvare tutti i registri della CPU, i flag, il valore
dei descrittori, il CR3, … la cosa può essere fatta direttamente in HW, il quale salva in
una porzione di memoria specifica il valore dello stato del sistema al momento in cui avviene lo switch.
Questa porzione è nota come TSS Task State Segment e contiene registri, registri di
stack, puntatore al task padre e la mappa degli I/O talvolta gestibili anche dai processi
utente. La testa di questo segmento è puntata da un registro.
12. ARCHITETTURE SUPERSCALARI E SPECULATIVE
Linee evolutive
Negli ultimi anni le evoluzioni si sono concentrate su:
• tecnologia, in quanto il livello di processo elettronico è arrivato a una larghezza di
canale di 20 nm, utilizzando un nuovo tipo di transistor Intel
• architetture, in quanto una diversa architettura di esecuzione può portare ad aumento nella velocità di esecuzione: due scelte possibili sono le architetture speculative e la out-of-order execution
• soluzioni multicore, con i modelli SMP e NUMA
Nelle architetture superscalari/speculative, si possono seguire 4 linee di miglioramento:
• minimizzare gli stalli in una singola pipeline
• eseguire più istruzioni in parallelo
• manipolare la sequenza delle istruzioni in modo da raggiungere gli obiettivi precedenti
• predire i salti condizionati
ILP Instruction Level Parallelism
A livello di singola pipeline, uno dei meccanismi è il riordino del codice: il codice in ingresso viene riorganizzato in modo che la semantica rimanga la stessa, ma gli stalli vengano minimizzati. Per far questo si possono combinare due tecniche:
• dinamica, eseguita al tempo di run-time
◦ usata dagli ultimi modelli, tra cui i Core
• statica, eseguita da compilatori che conoscono l’architettura della CPU
◦ usata dai vecchi processori e anche dagli Itanium (VLIW)
Basic block
Il concetto di base è che io devo lavorare a livello di basic block, porzioni di codice che
hanno un solo ingresso e una sola uscita.
MOV AX, BX
…
LOOP
NOT BX
Supponiamo ad esempio di avere un codice come quello riportato qui sotto. A sinistra ho
il codice ad alto livello, a destra il codice in un’architettura di tipo MIPS.
a = b + c;
LD
LD
ADD
SD
Rb, b
Rc, c
Ra, Rb, Rc
a, Ra
d = e – f;
LD
LD
SUB
SD d,
Re, e
Rf, f
Rd, Re, Rf
Rd
Ipotizzando di avere una pipeline con i seguenti stadi
IF
ID
EX
MEM
WB
se esplodiamo le istruzioni nella pipeline ci accorgiamo di andare incontro a diversi stalli
LD
LD
ADD
Rb, b
0IF
Rc, c
Ra, Rb, Rc
0ID
0IF
0EX
0ID
0IF
MEM
0EX
0ID
0WB
MEM
0ST
0WB
0EX
MEM
WB
e così via... Ci accorgiamo che nella terza istruzione C dev’essere già pronto in memoria,
ma siccome non è stata ancora fatta la fase di writeback devo includere uno stallo. Continuando a scrivere le istruzioni ci si accorge di avere quattro stalli.
Riorganizzando ad esempio il codice in:
LD
LD
LD
ADD
LD
SD
SUB
SD d,
Rb, b
Rc, c
Re, e
Ra, Rb, Rc
Rf, f
a, Ra
Rd, Re, Rf
Rd
il codice fa sempre le stesse cose, ma riesco a far sparire gli stalli. Per fare questo devo
ovviamente garantire che lo spostamento delle istruzioni non porti a modifiche nella semantica del codice: questa cosa va sotto il cappello di data and control dependance.
Data Dependencies
Un’istruzione i è dipendente da una successiva istruzione j se:
• i produce risultati utilizzati da j
• j è dipendente da k e k è dipendente da i, con k compreso tra i e j
Quando ci sono delle dipendenze si possono determinare delle alee, ovvero delle situazioni critiche. Non tutte le dipendenze dei dati generano criticità, non tutte le criticità
generano stalli.
Nei processori sequenziali, in cui non ho esecuzione in parallelo, non ho mai possibilità
di stallo.
MOV [BX], AX
ADD CX, DX
MOV [BX], SI
MOV AX, [BX]
ADD CX, DX
MOV BX, CX
In entrambi gli esempi ho dipendenza tra la prima e la terza istruzione. Nel primo perché
manipolano la stessa risorsa, nel terzo perché la prima istruzione dev’essere eseguita
prima della terza.
I rischi possono essere classificati in tre categorie:
• RAW, Read After Write: leggo un dato scritto da un’istruzione precedente
• WAW, Write After Write
• WAR, Write After Read
• RAR, Read After Read: non porta mai a criticità
WAR e WAW sono possibili specie in architetture con pipeline parallele.
Loop-level parallelism
Un altro modo di velocizzare i miei programmi è l’utilizzo del parallelismo a livello di
loop. Si consideri il for
for (i = 0; i < 1000; i++)
x[i] = x[i] + y[i];
La parte sottolineata è autoconsistente, in quanto non influisce quella successiva e non
è influenzata da quella precedente. Potrei fare una traduzione del genere
for (i =
x[i] =
x[i+1]
x[i+2]
x[i+3]
} *
0; i < 1000/4; i = i + 4) {
x[i] + y[i];
= x[i+1] + y[i+1];
= x[i+2] + y[i+2];
= x[i+3] + y[i+3];
riducendo così i salti condizionati da * al for di un fattore 4.
Branch prediction
Finora abbiamo visto come il motore del parallelismo debba andare a verificare le criticità sul codice, riorganizzando le istruzioni in modo tale da aumentare le prestazioni.
L’altro grande elemento è la branch prediction: quando nella pipeline entra un JMP condizionato, la condizione è verificabile soltanto “alla fine” della catena. Dentro alla pipeline dunque portiamo le istruzioni successive, che verranno eseguite con una probabilità
del 50%, assai bassa.
Si può predire se un salto verrà fatto o meno mediante due tecniche:
• tecniche statiche, gestite dal compilatore sulla base di esperienze precedenti
• tecniche dinamiche, gestite durante l’esecuzione del codice
Parlando delle tecniche dinamiche, queste sono basate su due tecniche:
• branch history table, piccola memoria in cui io memorizzo, per una serie di salti
(identificati con i bit bassi dell’indirizzo) cosa è stato fatto nelle esecuzioni precedenti (salto/non salto). Se la storia rappresenta bene “la verità” posso fare predizioni corrette. Se voglio fare predizioni con granularità maggiore posso aggiungere più bit.
• branch target buffer, realizzata nei processori più evoluti. Si ha una tabella composta da due campi: il primo è l’indirizzo del salto, il secondo è il valore del PC
predetto. Quando abbiamo un JMP condizionato, prendiamo il valore del PC, scorriamo la tabella e vediamo se il valore è all’interno. In caso affermativo prendiamo il valore del PC predetto. Se la predizione è sbagliata, la tabella è modificabile
secondo diverse strategie (subito o dopo due errori).
Istruzioni vettoriali
Ulteriore modo per migliorare le prestazioni. Operano su un set di dati anziché su un
dato scalare.
Out of order execution
Tutti i concetti finora visti si trovano nell’esecuzione non ordinata, in cui viene presa una
sequenza di istruzioni e sulla base della coerenza della dipendenza dei dati e della predi-
zione dei salti, le istruzioni vengono considerate a gruppi, manipolate, riordinate ed eseguite. Di norma in parallelo ad unità funzionali distinte.
MOV
ADD
MOV
MOV
ADD
MOV
AX, MEM[1000]
AX, 2
MEM[2000], AX
AX, MEM[3000]
AX, AX
MEM[3000], AX
Nell’esempio, la parte di codice in corsivo può essere eseguita in parallelo alla restante,
fatto salvo per la presenza di AX. Tuttavia, non ci sono effetti collaterali e quindi i blocchi
possono essere eseguiti in parallelo, a patto di “rinominare” AX. Se sostituisco AX a R1
nel blocco di codice inferiore posso eseguire i blocchi in parallelo. Alla fine mi devo ricordare di trasferire il contenuto di R1 in AX.
L’algoritmo più famoso che permette di fare le operazioni qui descritte è stato formulato
da Tomasulo.
13. ARCHITETTURE MULTIPROCESSORE
Architetture
Le architetture possono essere nucleate in due categorie:
• SMP, architetture a bus comune, realizzate in sistemi di non elevate prestazioni
• NUMA, architetture non a bus comune, realizzate sui server
SMP
NUMA
Le architetture SMP Symmetric MultiProcessing vengono dette anche a memoria comune: tutti i processori (non i processi, che vedono sempre un’unica memoria), che sono
simmetrici e hanno la stessa priorità nell’accesso alle risorse, vedono un’unica memoria.
Normalmente ci si appoggia a un sistema a bus comune.
Le architetture NUMA Non-Uniform Memory Access, invece, prevedono che ogni CPU abbia una sua memoria locale. Può esserci una memoria generale condivisa. I processori
poi tra loro si parlano con un sistema che difficilmente è a bus comune. La non uniformità è data dai diversi tempi di accesso alle varie zone di memoria: è necessario avere un
criterio di allocazione dei processi che “faccia le cose” vicino al mio core. Anche in questo caso i processi vedono un’unica memoria, mentre il sistema operativo schedula processi computazionali e memoria allocandoli ai singoli nodi.
Gestione delle cache
Uno dei problemi più rilevanti in SMP e NUMA è la coerenza tra cache. Un dato, che può
essere contenuto in due cache diverse di due processori diversi, dev’essere coerente fra
le due cache. Il problema è notevole.
Dal punto di vista delle cache, queste possono essere:
• dedicate al processore
• di tipo condiviso, quando sono usate da più core
◦ possono essere on chip o off chip
E’ meglio ricorrere a cache dedicate o cache condivise? Fermo restando che la L1 solitamente è dedicata in quanto dev’essere piccola e veloce per far lavorare al meglio la pipeline. Altri livelli di memoria possono essere condivisi e hanno l’ovvio limite che se
sono usate da un processore, l’altro non può usarle.
Vi sono però tutta una serie di vantaggi:
• si riduce la sottoutilizzazione: se P1 e P2 hanno due cache dedicate, può darsi che
la cache di P1 sia sempre piena e oggetto di diversi swap, mentre quella di P2
mezza vuota
• si riduce la difficoltà di mantenere le cache coerenti
•
se c’è un dato che serve a entrambi i processori, è unico e non duplicato
Indipendentemente da dove stiano le cache, avere un’architettura multiprocessore a
bus comune pone una serie di problemi:
• N processori competono per un unico bus: c’è bisogno di un arbitro, inevitabilmente si introducono latenze
• quando arriva una interruzione, a chi viene mandata?
• coerenza delle cache
Il bus comune FSB può essere sostituito da due diverse connessioni (una verso la DRAM,
l’altra verso chipset di I/O e altri processori). Questa tecnologia è stata introdotta da
AMD sotto il nome HyperTransport e successivamente da Intel con il termine QPI.
Coerenza cache
Un breve reminder: le cache possono essere realizzate mediante due filosofie, ovvero
direct mapping e set associative.
In generale, una cache è fondamentalmente una memoria composta da tre componenti
fondamentali:
• associatività
• dati
• elementi
TAG
DATA
CONTROL BIT
E’ organizzata per linee, dove ogni linea è composta da un tag (l’elemento di associatività che collega il dato indirizzo con la data linea) e da una serie di byte associati alla li nea. Il numero di byte è determinato dall’ampiezza di un ciclo di burst (4 cicli di bus) e
dal parallelismo del DBUS. Se abbiamo un DBUS a 64 bit, ogni linea è 32 byte.
Sono inoltre presenti una serie di informazioni di controllo:
• ci sono dei dati validi nella linea?
• qual è il più vecchio da cacciare via in caso di miss e successivo write?
• la linea è coerente con altre cache e con la memoria?
TAG
INDEX
DISPLACEMENT
Quando arriva un indirizzo sull’ABUS, questo viene processato in tre parti:
• DISPLACEMENT, il byte dentro la linea a cui faccio riferimento o da cui devo partire (se ho un MOV AH, … prendo un byte, se ho un MOV AX, … ne prendo due).
Se il mio ABUS è 64 bit, questo campo è grande 6 bit.
• INDEX, individua la linea
• TAG, individua l’associatività
Segnali di controllo delle cache
Vediamo quali sono i segnali che due (in un caso più generale, N) processori si scambiano per la gestione del bus e delle cache.
In un contesto a singolo processore, i segnali che vengono utilizzati sono CACHE EN,
CACHE e FLUSH. Questi segnali vanno a finire al controllore delle cache: se la porzione di
dati (= una pagina) è cacheabile, la MMU va a vedere se l’indirizzo è cacheabile andando a verificare nella TLB. In caso affermativo si risponde con CACHE.
Posso anche dire se non voglio cachare qualcosa, come ad esempio la memoria video,
utilizzando il segnale CACHE EN. Questi segnali entrano in gioco anche nel firing di un
ciclo di burst.
Il segnale di FLUSH serve a portare coerenza con tutte le cache, perché magari devo
fare uno switching di processo.
Come avviene operativamente? Supponiamo di avere a che fare con una MOV AX, mem.
Se mem non è in cache parte il primo ciclo di bus di (almeno) 2 periodi di clock e si verifica la possibilità di aggiornare la cache, valutando i bit di controllo CACHE e CACHE EN.
Nel caso in cui i bit di controllo diano il nulla osta ma non vi siano linee di cache libere
deve essere rimossa una vecchia linea di cache.
Una cosa analoga avviene per il write, tenendo presente che ci possono essere due politiche: write-back e write-through. Supponiamo di essere in un sistema monoprocessore
e di fare una MOV mem, AX. In termini di prestazioni sarebbe ovviamente molto comodo
scrivere solo nella cache, ma questo genererebbe discrasie tra cache e memoria. Le
strategie per ovviare a questo sono:
• write-through: devo scrivere in memoria? Non lo faccio in cache.
• write-back: scrivo in cache e quando la linea dev’essere tolta viene messa in memoria
Gestione della cache in sistemi multiprocessore
In sistemi multiprocessore le cose sono molto più complicate. Se P1 fa qualcosa sulla
sua L1 o sulla memoria, P2 non può ignorarlo. Ipotizziamo ad esempio che si operi con
write-through e che P2 legga “durante la notte dei tempi” la cella 100, portandosela nella sua L1. P1 a sua volta fa una write sulla cella 100: operando in write-through, la cache
viene bypassata e la scrittura viene fatta direttamente in memoria. P2 lo deve ben sapere! Questo perché se P2 legge la cella 100 andrà a leggere il valore in cache.
In termini sintetici, questo equivale a dire che quando viene fatta un’operazione sul bus,
l’altro processore dev’esserne informato. L’operazione di osservazione del bus da parte
del processore che non ne ha il controllo si chiama snooping. Quando P1 fa un ciclo di
bus deve dunque mandare un segnale a P2 dicendogli che è attiva una transazione sul
bus. P2, se lo ritiene opportuno, prenderà le opportune contromisure.
Serve dunque un ulteriore segnale di SNOOP e l’ABUS ora è portato anche in ingresso.
Nel caso reale, i segnali presenti gestiscono il bus per appropriarsene (BUSRQ, BUSACK:
chi non ha il bus chiede se è possibile averlo, e l’altro risponde) e la coerenza della cache (PHIT, PHITM: controllati da chi fa snooping). Supponiamo dunque che P1 controlli il
bus e che a un certo punto P2 lo richieda: dopo essersi scambiati BUSRQ e BUSACK, P2
accede al bus e P1 attiva snooping (= manda i segnali).
Possono determinarsi diverse situazioni:
• PHIT/PHITM non attivi: tutto regolare
• PHIT attivo, PHITM non attivo: “io P1 ho quell’area di memoria nella mia cache,
ma tu P2 stai facendo una lettura. Tutto bene, devo solo ricordarmi che il dato poi
sarà in più cache”
• PHIT attivo, PHITM attivo: l’esempio riportato nell’introduzione al problema. P1
blocca tutto e ha nuovamente il controllo. A questo punto aggiorna il dato in memoria e ripassa il controllo a P2.
Tutto questo è gestito in HW in modo automatico e trasparente.
Protocollo MESI
Per gestire le cache in sistemi SMP viene realizzato un protocollo che si attiva nelle fasi
di snooping e determina le operazioni che devono essere fatte, attribuendo a ciascuna
linea di cache un valore tra quattro. Si può dimostrare che una linea di cache può infatti
essere in uno di questi quattro stati:
• Modified, ovvero la linea è valida ma il valore che contiene è stato modificato dal
processore e non è più coerente col valore della memoria (e quindi, potenzialmente, anche col valore delle altre cache)
• Exclusive, ovvero la linea è valida e non è presente in altre cache
• Shared, ovvero la linea è valida ed è presente anche in altre cache
• Invalid, ovvero la linea è vuota o mi sono accorto che qualcun altro l’ha modificata
Quando si opera con le cache ci si muove tra questi quattro stati in due FSM, una per il
processore che possiede il bus e l’altro per il processore che fa lo snooping.
Coerenza cache multi-livello
Tutto quanto abbiamo detto è valido, ma bisogna tenere presente che attualmente si lavora con sistemi di cache multi-livello, le quali portano ulteriori complicazioni. Perchè
però mettere ulteriori livelli di cache quando potrei semplicemente ingrandirle? Perchè i
tempi di accesso non sono indipendenti dalla dimensione di una memoria: più questa è
grande, più il tempo di accesso cresce secondo un fattore quadratico.
Naturalmente nascono di nuovo problemi di coerenza – in cui anche le operazioni di
READ possono generare criticità – perché la L1 vede la L2 e la L2 vede la memoria. Per
la verità si può dimostrare che, se si soddisfa la proprietà dell’inclusione, il protocollo
MESI riesce a garantire la coerenza. Affinchè si possa parlare di inclusione, devono essere valide queste due condizioni:
• se un blocco di memoria è in L1, dev’essere anche in L2
• se un blocco è modified in L1, dev’essere modified anche in L2
14. ARCHITETTURE SERVER SMP - NUMA
Generalità
Per definizione, un server è un sistema a multiprocessore. Il sistema è articolato su due
livelli:
• processori all’interno di un chip
• interconnessione tra chip
Il mercato consumer infatti è monocore, oppure multicore ma monochip. Sui server, invece, abbiamo un’architettura multicore e multichip.
SMP vs NUMA
Qual è il problema che si pone nelle architetture SMP? Al crescere del numero di core o
di processori si ha un intasamento pauroso sul bus e sulla memoria, in quanto tutti condividono un’unica risorsa per l’accesso. Normalmente, poi, i tempi di esecuzione sui singoli core tendono a diminuire ma i tempi di accesso alla memoria restano costanti. Inoltre, il crescente bisogno di memoria tende ad aumentare maggiormente gli stalli.
Si cerca dunque di risolvere questi problemi attraverso architetture diverse ricorrendo
allo schema NUMA, dove ogni CPU ha connessa una sua memoria privata. Dentro al singolo chip siamo in una situazione SMP. In un’architettura server le cose seguono dunque
questo schema
Ben si badi che sotto ai singoli core ci sono livelli di cache, e il tutto porta a ulteriori
complicazioni nella gestione delle cache. Immaginando poi l’aggiunta di un terzo
“nodo”, i collegamenti tra nodi devono essere point-to-point per evitare criticità.
Architetture classiche
L’architettura dei vecchi server low level (e dei laptop) è simile a quella riportata, in cui
si ha un processore – anche multicore –, un northbridge e un southbridge, la memoria, i
vari PCI collegati al gestore dell’I/O...
Architetture avanzate
Prima
Dopo
Nei server a più alto livello possiamo avere situazioni monochip-mononodo col northbridge e alcuni livelli di memoria portati nel chip. Questo, ovviamente, porta a miglioramenti nell’accesso alla memoria.
SMP
NUMA
Un’altra evoluzione, sulle architetture multinodo, è stata quella di risolvere i problemi di
accesso al bus e alla memoria propri delle architetture SMP. Nelle architetture NUMA il
nodo è costituito dal processore e dalla sua memoria, e ciascun nodo è collegato all’altro
tramite canali point-to-point monodirezionali ad altissima velocità.
Naturalmente poi si possono gestire anche i dispositivi di I/O. Le periferie, in particolare,
non sono simmetriche come è giusto che sia: se collego un disco lo collego a “uno solo”.
L’AMD è stata la prima a far uscire questa tecnologia (HyperTransport) con due link unidirezionali, ciascuno a parallelismo variabile in modo da essere dimensionato dalla velocità supportata dalla destinazione. Link a 32 bit sono in grado di viaggiare a 26 GB/s, in
modo da riuscire ad abbattere i tempi anche nell’accesso alle memorie remote. Questi
meccanismi di collegamento point-to-point tra nodi sono particolarmente utili nei supercomputer, dove non si può minimamente sognare una gestione a bus comune.
Si noti che sui link viaggiano dei protocolli, con header-dati-etc... che determinano le
operazioni che devono essere fatte.
Un’ulteriore evoluzione è data dalle Direct Connect Architecture, implementata su server a medio-alte prestazioni, in cui si fa un solo hop da un processore a un qualunque altro processore. Il miglioramento lo sfrutto al meglio se ci sono processi condivisi.
Interessante citare anche la categoria di processori AMD Fusion, in cui la crescita di transistor sul chip è adoperata inserendo dentro al chip non sistemi di interconnessione, ma
funzionalità diverse. Questa linea di produzione ha integrato in un unico chip la CPU e
l’unità grafica, in modo tale che ci sia un unico sistema che su un unico chip svolga sia
funzioni tradizionali che funzioni grafiche evolute. La cosa è ottima in sistemi orientati
alla grafica.
15. EVOLUZIONE ARCHITETTURE INTEL x86/64
Evoluzione tecnologica delle architetture x86
Un po’ di storia. Si tenga conto che le architetture a 64 bit nascono nel 2009, anche se
allora le applicazioni e molti sistemi operativi non erano ancora coerenti con le architetture. Per cui, per dover vendere, le architetture a 64 bit dovevano essere totalmente
compatibili con le architetture a 32 bit. In generale, l’hardware anticipa in modo significativo i miglioramenti del mondo software.
Le evoluzioni hanno riguardato l’architettura e la microelettronica: creare una nuova architettura porta allo sforzo di riduzione delle dimensioni che a sua volta può portare a
una nuova architettura, e così via...
Le ultime architetture realizzate sono state la Netburst e la Hyperthreading.
Processore superscalare Pentium
La prima significativa architettura superscalare è stata la prima versione del Pentium.
Per garantire la superscalarità erano presenti due pipeline, una di tipo Y e una di tipo U.
Dopo aver decodificato l’istruzione, essa poteva essere indirizzata su una delle due pipeline se era possibile effettuare parallelismo.
Non posso ad esempio parallelizzare due istruzioni che hanno un vincolo di dipendenza,
come
MOV AX, [BX]
ADD AX, AX
Vi è una control ROM per “suddividere” in micro-operazioni le operazioni di tipo CISC e
un branch target buffer per riuscire ad effettuare l’operazione di predizione dei salti. Errori nella predizione dei salti hanno penalità significative.
NetBurst
Venuta alla ribalta col Pentium 4. Caratterizzata da alcuni elementi significativi, tra cui il
fatto che la visione è quella di un’architettura CISC, ma internamente funziona come un
RISC. Per questi motivi:
• tutte le istruzioni CISC sono esplose in micro-operazioni
• ogni micro-operazione ha lunghezza fissa di 118 bit
Inoltre:
• si realizza l’esecuzione out-of-order, in cui si prendono istruzioni Assembler, si
esplodono in micro-operazioni eseguite con criteri di parallelismo indipendenti
dall’ordine stabilito per poi ricomporle e tornare alla sequenza originale
• c’è un branch target buffer
• le pipeline hanno un numero di stadi, il che fa aumentare il throughput
Core
Nelle architetture Core si è introdotto il concetto dell’HyperThreading, in cui all’interno
del singolo die sono presenti più CPU. In questo modo, associando un thread a una CPU
e un altro thread a un’altra CPU si riesce ad avere un parallelismo di thread. Nella singola CPU dunque ho Instruction Level Parallelism ILP (= più pipeline operanti in parallelo) e
Thread Level Parallelism TLP tra le CPU.
In un i7 abbiamo L1 e L2 dedicate, mentre la L3 è shared.
Sviluppo tick tock
Il modello di sviluppo è legato al fatto che si realizza una certa architettura con una data
tecnologia. Dopodichè la stessa tecnologia, una volta consolidata, viene adoperata per
fare un’innovazione architetturale. L’architettura poi rimane costante, ma la tecnologia
diventa più sottile, e così via... Oggi si viaggia sui 22 nm.
Evoluzione delle architetture
In PC di bassa fascia si ha sostanzialmente un’architettura come quella riportata. Con
l’introduzione dei Core2 si può ottenere un’ulteriore evoluzione con canali di comunicazione più veloci, ma scheda grafica e memorie sempre attaccate all’equivalente del northbridge.
Core 2
Core i Series
L’ultima evoluzione, i Core i Series, vede memoria e grafica portata all’interno del die.
Non c’è più un FSB, ma dei collegamenti diretti con quello che una volta era il southbridge, ovvero il chip di gestione degli I/O.
Con dei canali di comunicazione i processori possono dialogare tra loro: la cosa richiede
canali di comunicazione estremamente veloci. Questa tecnologia è chiamata Intel QuickPath QPI e porta alla realizzazione di architetture NUMA, in quanto ogni processore ha la
sua memoria.
Monochip - multicore
Multichip-multicore
Multichip-multicore
Tri-gate transistors
Il primo chip a usare i 22 nm è Ivy Bridge. Questi sono realizzati attraverso dei transistor
che hanno delle caratteristiche multidimensionali.
Non si è soltanto ridotta la larghezza di canale, ma è cambiata proprio la tecnologia dei
transistor. Quali sono i vantaggi?
• sono più piccoli
• consumano molto meno
Evoluzione architetturale: X86/64
Quando si parla di un’architettura a n bit, in generale bisogna tenere presente che l’n
deve far riferimento a due ambiti:
• dati: quanto maggiore è il numero di bit che vengono gestiti contemporaneamente, maggiore è la velocità con cui riesco a trattare certi tipi di dato
• indirizzi: un’architettura a n bit implica maggior profondità di memoria che il programmatore vede
Queste architetture non nascono in casa Intel, ma sono un gran botto di AMD. Questo
perché l’Intel aveva un’architettura a 64 bit (IA-64), basata sul processore Itanium
(Intel/HP). Tuttavia IA-64 era un’architettura per server e non c’era compatibilità a livello
di codice con le architetture x86, al contrario di AMD che fece un’estensione dell’architettura.
Da un punto di vista di risorse hardware, l’x86/64 ha registri tradizionali a 64 bit. La filosofia AH/AL 8 bit, AX 16 bit, EAX 32 bit è stata estesa con l’introduzione di rEAX. Questa
cosa vale anche per il program counter. Per rendere la macchina più efficiente dal punto
di vista prestazionale, dato che le architetture x86 erano limitate in termini di registri, il
numero di registri general purpose è stato incrementato di 8. Rimangono i registri di
segmento per avere compatibilità con il passato.
Dati e indirizzi
Per garantire compatibilità, le architetture a 64 bit possono operare in due modalità:
• long mode
◦ Il modello è tutto a 64 bit: gira codice nativo a 64 bit, oppure a 16 o 32 in
modo protetto. Il sistema operativo è a 64 bit.
• legacy mode
◦ Il sistema operativo non è a 64 bit e i processi possono girare a 16 o 32 bit.
Esiste sempre il modo reale.
Nella gestione degli indirizzi, bisogna fare una distinzione tra indirizzi logici e indirizzi fisici. Un’architettura x86/64 prevede indirizzi logici a 64 bit con uno spazio di memoria di
16 EB (exabyte). Nelle attuali implementazioni, in funzione del processore, si può avere
una gestione su 48 bit (indirizzamento logico 256 TB) e in futuro a 56 bit (indirizzamento
logico 65536 TB). Quando si arriverà ai 64 bit nascerà il problema di far girare i processi
a 48 bit. Ciò comporta la creazione di modelli di memoria del genere
in cui si “tranciano” i bit più alti, estendendoli come in figura col valore dell’ultimo bit significativo, in modo da gestire meno indirizzi e mantenere la divisione user/system, nonché tutte le varie considerazioni fatte quando si è parlato di modo protetto.
Memoria fisica
Ho la possibilità di avere ABUS da 36, 40 e 48 bit. La memoria fisica è dunque più grande della memoria logica. Questo è quello che si chiama Physical Address Extension PAE,
in cui posso avere potenzialmente 64 GB di RAM. Il passaggio tra indirizzo logico e indirizzo fisico viene gestito a livello di paginazione.
Al contrario della paginazione tradizionale – in cui si avevano pagine di 4 KB e indirizzi
spezzati in tre parti per consultare due tabelle con entry da 32 bit – sono state fatte delle tabelle con entry a 64 bit che occupano lo stesso spazio in memoria, aggiungendo
un’ulteriore tabella.
Nelle architetture a 64 bit, cambiano dunque sia indirizzo logico (quello visto dai processi) che indirizzo fisico (quello visto dalla paginazione): i due sono scorrelati, e il correlato
tra i due è dato dalle tabelle di paginazione, tanto più multilivello quanto più è lungo
l’indirizzo.