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.