di  -  mercoledì 4 marzo 2009

Non poche volte m’è capitato di trovarmi di fronte a delle novità particolarmente difficili da digerire e che ho velocemente marchiato come roba “inutile”, di cui se ne può fare a meno, o addirittura frutto della mente deviata di qualcuno che non aveva niente di meglio da fare.

Tipica posizione questa di persone poco capaci, svogliate e/o che non vogliono staccarsi dalle immutabili “certezze” acquisite, magari difendendole a spada tratta a ogni occasione. Col tempo si matura la consapevolezza che le difficoltà vanno affrontate e risolte, con un po’ di impegno, buona volontà, ma soprattutto la giusta apertura mentale.

E’ il caso dell’80286 di Intel: microprocessore a 16 bit che ha introdotto, con la modalità protetta, una profonda rivoluzione nell’architettura x86, di cui però inizialmente non riuscivo a percepire le potenzialità e addirittura consideravo un’autentica assurdità, abituato com’ero al semplice e lineare modello offerto dalla Motorola col 68000 e compagnia bella…

In effetti le differenze rispetto a quest’ultima (e a tante altre architetture prive del concetto di segmentazione della memoria che, quindi, offrono un accesso diretto, o per meglio dire lineare a essa) sono notevoli, mentre confrontato all’8086 sono più di sostanza che di forma, ben “nascoste” nei famigerati segmenti, che con questa nuova CPU hanno cambiato nome in selettori, mantenendo invariato però l’utilizzo.

Più precisamente, l’80286 introduce una nuova modalità di funzionamento del microprocessore chiamata protetta, etichettando il normale funzionamento dei precedenti processori (8086 e 80186) come modalità reale. In modalità protetta la CPU cambia radicalmente il funzionamento dei segmenti (diventati, appunto, selettori) per l’accesso alla memoria (ho già parlato di segmentazione in precedenza, quando ho introdotto l’8086, per cui non mi soffermerò sull’argomento), che hanno assunto la seguente forma:

Il valore a 16 bit è stato suddiviso in tre gruppi di informazioni. Partendo da destra verso sinistra, abbiamo il livello di privilegio del selettore, a quale tabella esso fa riferimento, e l’indice nella tabella.

Il livello di privilegio riguarda la modalità richiesta per l’uso del selettore, fra le quattro messe a disposizione dall’80286. Già, perché, a differenza di tanti altri microprocessori che offrono un meccanismo a due livelli (modalità utente, usata per le applicazioni, e supervisore, usata per kernel, driver e librerie di sistema), Intel ha pensato bene di aggiungerne un altro paio per definire in maniera più granulare (quindi “fine”) i meccanismi di protezione per l’accesso a particolari risorse.

A prima vista sembra la solita, inutile, complicazione ma, riflettendoci bene, è così scocciante per un programmatore? No, perché non siamo tenuti a impostare a manina ogni singolo selettore per le nostre applicazioni, visto che è il compilatore utilizzato che si fa carico di tutto. Al più potremmo essere interessati se dovessimo lavorare al codice di sistema, ma anche qui, a meno di non lavorare con le primitive del kernel, scrivendo driver e librerie di sistema non siamo tenuti a occuparci di queste informazioni.

In buona sostanza, la flessibilità ottenuta da questo meccanismo non comporta conseguenze di cui tenere conto quando si lavora normalmente (tranne per gli sviluppatori del kernel). Ma non sono tutte rose e fiori, purtroppo. Per rappresentare i quattro livelli di privilegio sono necessari due bit, per cui si riduce il numero di selettori utilizzabili da un’applicazione, che ammontano a 8192 per i selettori locali e altrettanti per quelli globali (quindi 16384 in tutto), e ciò comporta un grosso limite al numero di risorse allocabili da un’applicazione e dal sistema.

Il secondo campo di un selettore indica a quale insieme appartiene: se locale all’applicazione (LDT, Local Descriptor Table) o globale al sistema (GDT, Global Descriptor Table). Nelle tabelle si fa riferimento alla parola “descrittore” perché ogni elemento riporta (“descrive”) le informazioni associate al selettore in questione:


Senza soffermarci troppo (perché la spiegazione richiederebbe non poco tempo e conoscenze tecniche), è sufficiente dire che viene specificato l’indirizzo base della memoria fisica a cui si riferisce il selettore (Base), la sua dimensione (Limit, in byte), il tipo di segmento (Type, per codice, stack e dati) e alcuni flag accessori che sono utili per lo più al kernel (ad esempio per implementare la memoria virtuale).

E’ importante, però, mettere in chiaro che l’esecuzione è fortemente condizionata dai valori presenti nei descrittori. Giusto per fare un esempio, il codice potrà essere eseguito esclusivamente su selettori nel cui tipo sia stato indicato, appunto, che si tratta di codice, pena l’interruzione dell’esecuzione e il sollevamento di un’eccezione. Da ciò si capisce anche il perché la modalità venga chiamata “protetta”: esistono dei granitici meccanismi di protezione delle risorse con cui si sta lavorando.

Il terzo e ultimo campo specifica l’indice del selettore (dalla tabella di quelli locali o globali, come detto qui sopra) che si vuole utilizzare. Quindi il codice, lo stack e i dati di un’applicazione saranno allocati in opportuni e ben precisi selettori, che verranno combinati con un offset per accedere alla locazione di memoria effettiva, come riportato nel seguente schema:


Lo schema è simile a quanto avveniva coi segmenti, dove l’indirizzo finale veniva generato da un’opportuna combinazione del segmento e dell’offset. Qui l’offset ha lo stesso significato, ma cambia quello del segmento, poiché il selettore da solo non è sufficiente a determinare immediatamente l’indirizzo base da sommare all’offset per ricavare l’indirizzo fisico desiderato.

Dal selettore è necessario individuare intanto la tabella dei descrittori da utilizzare, e sfruttando l’indice si ottiene finalmente accesso alle informazioni del descrittore. Da qui si controlla innanzitutto se il livello di privilegio presente è “accettabile” (generando un’eccezione in caso contrario), si confronta l’offset col limite per vedere se lo si sta superando (generando anche qui un’eccezione, nel caso), e finalmente se tutti i controlli sono stati soddisfatti si preleva l’indirizzo base sommandolo all’offset per generare l’indirizzo fisico per accedere alla memoria.

Un meccanismo abbastanza complicato e che ai tempi definivo, appunto, contorto, ma che ha ben ragione di esistere e devo ammettere che sono rimasto molto affascinato dal sistema di protezione che viene messo in piedi utilizzandolo. In ogni caso si tratta di operazioni che vengono effettuate internamente dalla CPU per cui, per quanto “astruso”, i programmatori possono benissimo ignorarlo.

Ma il sistema di protezione ideato da Intel non finisce qui. I selettori, in particolare sono quelli “globali”, sono stati usati per implementare altri sofisticati meccanismi chiamati gate che regolano in maniera rigida l’esecuzione di interrupt (interrupt gate), processi (task gate) e le chiamate al sistema operativo (call gate).

Anche qui, senza scendere nei dettagli, nel caso in cui si verifica un’interruzione o un’eccezione (sia esso esterna, cioé provocata da una periferica, o interna, provocata dal codice), viene prelevato un opportuno selettore della GDT (l’interrupt gate, appunto) che specifica in maniera precisa quale codice dev’essere eseguito, e le necessarie restrizioni.

I task gate vengono, invece, utilizzati per memorizzare lo stato di un processo e semplificare il passaggio dall’uno all’altro (i programmatori del kernel ringrazieranno sicuramente).

Infine per le call gate il meccanismo è analogo, ma il relativo gate offre in più la possibilità di specificare se e quanti parametri passare dall’applicazione al sistema operativo, sfruttando i relativi stack per la copia di questi valori. Questo meccanismo è, però, stato sostituito nei microprocessori più moderni da apposite istruzioni per richiamare molto più velocemente le API del s.o..

Già, perché, e questa è sicuramente la nota dolonte che ne ha frenato l’uso, tutti questi meccanismi hanno un costo molto elevato in termini di velocità di esecuzione. In particolare quello dei gate è estremamente oneroso in termini di cicli di clock consumati per completare l’operazione, visto che si arrivava a superare anche i 200 (sì, proprio duecento).

Per questo motivo i programmatori hanno snobbato per parecchio tempo la modalità protetta, preferendole la modalità reale. Giusto il tempo necessario a Intel per migliorarne l’implementazione, grazie anche al fatto che la tecnologia consentiva di integrare più transistor nei chip da dedicare a questo scopo.

Da lì in poi si è andato ad affermare l’utilizzo dei famigerati DOS extender che permettevano di lanciare applicazioni in modalità protetta. Il limite dei 640KB del DOS era, infatti, ancora lì, e la memoria oltre il MB degli 8086 era decisamente troppo allettante per non pensare di sfruttarla. Sì, perché con l’80286 si poteva arrivare a indirizzare ben 16MB di RAM e fino a 4GB di memoria virtuale!

14 Commenti »

I commenti inseriti dai lettori di AppuntiDigitali non sono oggetto di moderazione preventiva, ma solo di eventuale filtro antispam. Qualora si ravvisi un contenuto non consono (offensivo o diffamatorio) si prega di contattare l'amministrazione di Appunti Digitali all'indirizzo info@appuntidigitali.it, specificando quale sia il commento in oggetto.

  • # 1
    Banjo
     scrive: 

    Grande il Dos4gw necessario per giocare a Doom!

  • # 2
    Banjo
     scrive: 

    Per un momento nel titolo lo ho letto… cagate…!

  • # 3
    Alberto
     scrive: 

    Sempre stato convinto che l’architettura x86 sia un cesso con successo commerciale.. Nulla a che vedere con l’eleganza di un Arm o di un 680×0.

  • # 4
    Cesare Di Mauro
     scrive: 

    Non v’è dubbio che a livello di ISA e generale struttura 680×0 e ARM sono di gran lunga preferibili (in ambiti diversi: la prima per sistemi desktop / workstation, la seconda per sistemi embedded; mia opinione personale, ovviamente), ma di x86 ho trovato interessante due cose: il modello di protezione (come traspare dall’articolo) e la rapida introduzione ed evoluzione delle estensioni SIMD.

  • # 5
    zando
     scrive: 

    mah, la complessità dell’architettura Intel è estrema, ma necessaria per mantenere la retrocompatibilità. Provate a leggervi i manuali della Intel, ve ne renderete conto

  • # 6
    Cesare Di Mauro
     scrive: 

    Potevano anche trovare soluzioni migliori per mantenere la retrocompatibilità, e al contempo estendere l’architettura in maniera più flessibile.

  • # 7
    Alberto
     scrive: 

    Cesare.. beh.. de facto le SIMD se non ricordo male furono introdotte per prime dalla Ti. La prima volta che le usai fu’ su un alpha.. ma onestamente non ricordo il periodo quindi potrebbe anche essere che intel lo abbia fatto nello stesso periodo. Mi ripeto.. ho lavorato su intel sino ai 486 e su motorola sino la 68060, ma non c’era il ben che minimo paragone. Nemmeno saltando in modalita’ protetta. Motorola era enormemente piu’ efficiente e lato programmatore enormemente piu’ flessibile.. tutto IMHO eh.

  • # 8
    Cesare Di Mauro
     scrive: 

    Parli con un Amighista sfegatato che ancora adora l’architettura Motorola 680×0 (penso che traspaia pure dai numerosi riferimenti ed elegi che ho sparso in alcuni articoli). ;)

    Però trovo la modalità protetta introdotto da Intel molto interessante.

    Per quanto riguarda le SIMD, le ha inventate per primo Sir Cray per i suoi arcinoti supercomputer, ma quando ne ho parlato ho fatto riferimento al mercato dei desktop.

  • # 9
    National Semiconductor 16032: il perfetto (e sconosciuto) CISC a 32 bit? - Appunti Digitali
     scrive: 

    […] anche il concetto di modulo, che è assimilabile a quello di call gate e task gate degli x86 (dal 286 in poi), e che serve a individuare un blocco di dati e di codice similmente allo stato di un […]

  • # 10
    Virtualizzazione a basso costo col Motorola 68010 - Appunti Digitali
     scrive: 

    […] nello stesso anno dell’80286 di Intel (il 1982), ma costituito dalla metà dei transistor (e, quindi, molto più economico), […]

  • # 11
    Compaq Deskpro 386: compatibilità ad alta velocità - Appunti Digitali
     scrive: 

    […] due fasce di prodotto: il PC/XT, lanciato nel 1983, basato su CPU 8088, e il PC/AT (1984), con CPU 80286 e adattatore video […]

  • # 12
    1983: 10MB di backup su audiocassetta! - Appunti Digitali
     scrive: 

    […] il Lisa, il PC/XT di IBM (evoluzione del primo PC), il Macintosh, il PC/AT (1984) basato su CPU Intel 80286, quindi il Compaq Deskpro 386, primo sistema basato su CPU Intel […]

  • # 13
    Pluto
     scrive: 

    @Cesare
    Secondo te un emulatore come DOSEMU o DOSBOX riescono ad emulare un processo re 286 in tutti i sui dettagli come uno vero, o hanno un comportamento in alcuni casi differente?

  • # 14
    Cesare Di Mauro (Autore del post)
     scrive: 

    Veramente dovrebbero farlo già senza problemi.

Scrivi un commento!

Aggiungi il commento, oppure trackback dal tuo sito.

I commenti inseriti dai lettori di AppuntiDigitali non sono oggetto di moderazione preventiva, ma solo di eventuale filtro antispam. Qualora si ravvisi un contenuto non consono (offensivo o diffamatorio) si prega di contattare l'amministrazione di Appunti Digitali all'indirizzo info@appuntidigitali.it, specificando quale sia il commento in oggetto.