In questa serie di tutorial, assumo che tu, il lettore, sia familiare con i meccanismi della modalita' protetta dell' Intel 80x86 quali il modo virtual 8086, la paginazione, GDT,LDT,IDT. Se non ne conosci il funzionamento ti consiglio di leggere prima la documentazione Intel a questo indirizzo: http://developer.intel.com/design/pentium/manuals/
Window 95 e' un sistema operativo multithreaded funzionante al livello di privilegio piu' alto, ring 0. Tutti i programmi applicativi funzionano invece a ring 3, il piu' basso livello di privilegio. Questa situazione determina che i programmi applicativi sono limitati in quello che possono fare all'interno del sistema. Essi non possono eseguire istruzioni CPU privilegiate, accedere direttamente alle porte I/O e cosi' via. Avrete sicuramente familiarita' con questi tre grossi componenti di sistema: gdi32, kernel32 e user32. Proteste essere portati a pensare che questi tre importanti moduli debbano funzionare a ring 0. Ma in realta' essi funzionano a ring 3, come tutte le altre applicazioni. Cio' significa che essi non hanno piu' privilegi che, diciamo, la calcolatrice di Windows o il gioco del campo minato. La vera potenza del sistema operativo e' sotto il controllo del virtual machine manager (VMM) e dei virtual device drivers (VxD).
Tutto questo non sarebbe accaduto se il DOS non
avesse reso lo scenario piu' complicato. Durante l'era Windows 3.1, c'erano
molti programmi DOS di successo sul mercato. Windows 3.x doveva essere in
grado di farli funzionare accanto ai normali programmi Windows altrimenti
sarebbe fallito commercialmente.
Questo
dilemma non era semplice da risolvere. I programmi DOS e Windows sono
drasticamente differenti l'uno dall'altro. I programmi DOS sono "scorretti" in
quanto pensano di controllare ogni cosa nel sistema:
tastiera, CPU, memoria, hd disks, ecc. Essi non sanno come cooperare cogli altri
programmi mentre i programmi Windows (a quel tempo) si basano sul multitasking
cooperativo, ovverosia ogni programma Windows deve cedere il controllo agli
altri programmi attraverso GetMessage o PeekMessage.
La soluzione e' far funzionare ogni programma DOS in una
virtual 8086 machine mentre tutti gli altri programmi Windows funzionano in
un'altra virtual machine chiamata system virtual
machine. Windows e' responsabile di dare
tempo CPU ad ogni virtual machine in maniera ciclica. Percio' sotto Windows 3.x,
i programmi Windows utilizzano il multitasking cooperativo invece le
virtual machines utilizzano il multitasking preemptive.
Ma cos'e' una
virtual machine? Una virtual machine e' un artificio creato
solamente dal software. Una virtual machine reagisce ai programmi in esecuzione
come se fosse una vera macchina. Ne segue che un programma non sa di funzionare
in una virtual machine e non se ne preoccupa. Percio', fintanto che la virtual
machine risponde al programma esattamente come
una vera macchina, essa puo' essere considerata come una cosa reale. Si puo'
pensare all'interfaccia tra una macchina reale e il suo software come ad una
sorta di API. Questa inusuale API consiste di interrupts, chiamate al
BIOS, e porte I/O. Se Windows puo' in qualche modo emulare perfettamente
questa API, il programma in esecuzione nella virtual machine si comportera'
esattamente come se fosse eseguito in una macchina reale.
Questo e' il punto in cui VMM e VxDs entrano in scena. Windows
per coordinare e supervisionare le virtual machines (VMs) necessita' di un
programma dedicato allo scopo. Questo programma e' il Virtual Machine
Manager.
Come potete vedere, VMM e' il primo VxD ad
essere caricato in memoria. Esso crea la system virtual machine ed inizializza
gli altri VxDs. Esso, inoltre, fornisce numerosi services agli altri VxDs.
Il modo di operare di VMM e dei
VxDs e' differente dagli altri programmi normali.
Questi sono per la maggior parte del tempo dormienti. Mentre i programmi
applicativo sono in esecuzione nel sistema, questi VxDs sono inattivi. Essi sono
svegliati quando si verificano interrupts/faults/eventi che necessitano della
loro attenzione.
VMM non e' rientrante. Questo significa che i VxDs devono
sincronizzare il loro accesso ai services di VMM. Ci sono delle situazioni in
cui non e' sicuro chiamare i servizi di VMM come quando un interrupt hardware e'
in elaborazione. Durante quel periodo VMM non puo' tollerare rientranza. Come
per i VxD anche voi dovete stare estremamente attenti a quello che state
facendo. Ricordate che non c'e' nessun altro che
si preoccupa di eventuali errori del vostro codice. A ring 0 siete
assolutamente nelle vostre mani.
I VxDs statici sono quelli che vengono caricati durante la fase di boot del sistema e restano caricati fino alla chiusura del sistema stesso. Questo tipo di VxD risale al periodo di Windows 3.x. I VxDs dinamici sono invece disponibili solo in Windows 9x. I VxDs dinamici possono essere caricati/scaricati dalla memoria solo quando necessari. La maggior parte di essi sono VxDs che gestiscono le periferiche Plug and Play che sono caricate dal Configuration Manager e dall' Input Output Supervisor. Potete inoltre caricare/scaricare i VxDs dimanici direttamente dalle vostre applicazioni win32.
Control Messages: Quando si verifica qualche evento di interesse, VMM invia messaggi di controllo a TUTTI i VxDs caricati nel sistema. In questa logica i messaggi di controllo sono come i windows messages delle applicazioni Windows a ring-3. Ogni VxD possiede una funzione, chiamata device control procedure , che riceve ed interagisce con i control messages. Esistono circa 50 o poco piu' control messages di sistema. La ragione per cui non esistono molti messaggi di controllo e' che spesso ci sono parecchi VxD caricati nel sistema e dato che ognuno di essi viene interrotto ad ogni messaggio di controllo, se ce ne fossero troppi il sistema verrebbe sovraccaricato fino ad un blocco totale. Ad ogni modo voi vorrete gestire solo i messaggi realmente importanti collegati alle VMs, ad esempio quando una VM e' creata, distrutta e cosi' via. In aggiunta ai messaggi di controllo del sistema, un VxD puo' definire i propri control messages personalizzati che possono poi essere utilizzati per comunicare con altri VxDs che li comprendono.
Service APIs : Un VxD, compreso VMM, normalmente esporta un insieme di funzioni pubbliche che possono essere invocate dagli altri VxDs. Queste funzioni sono chiamate servizi del VxD. Il meccanismo di chiamata dei VxD services e piuttosto differente da quello delle applicazioni a ring-3. Ogni VxD che esporta servizi DEVE possedere un ID number unico. Ciascuno puo' ottenere questi IDs dalla Microsoft. Un ID e' un numero a 16-bit che identifica univocamente un VxD. Ad esempio:
Come potete vedere quello della VMM ha ID pari ad 1, VPICD ha ID 3, e cosi' via. VMM utilizza questo ID univoco per trovare il VxD che esporta i VxD services richiesti. Inoltre dovete selezionare il servizio che volete chiamare anche attraverso il suo indice nella tabella dei servizi. Quando un VxD esporta dei servizi VxD, esso salva l'indirizzo degli stessi in una tabella locale. VMM utilizzera' poi l'indice fornito per localizzare l'indirizzo del servizio desiderato dalla tabella dei servizi. Per esempio se volete chiamare GetVersion, che e' il primo servizio della VMM, dovete specificare 0 (l'indice e' zero-based). Il reale meccanismo di una chiamata ad un servizio VxD implica poi un int 20h. Il vostro codice effettua un int 20h seguito da una valore DWORD che e' composto dal device ID e dall'indice del servizio. Cosi', per esempio, se volete chiamare il servizio numero 1 che e' esportato da un VxD che possiede un device ID di 000Dh, il codice dovrebbe essere come questo:
La WORD alta della DWORD che segue l'istruzione int 20h contiene il device ID. La WORD bassa e' invece l'indice zero-based nella tabella dei servizi.
Potete capire che questa operazione
e' piuttosto costosa in termini di tempo macchina. VMM deve sprecare tempo
sia a localizzare il VxD che l'indirizzo del
servizio desiderato. Di conseguenza VMM imbroglia un pochino. Dopo che il primo int 20h ha
successo, VMM fissa il collegamento. Per
fissare il collegamento si intende che VMM rimpiazza l' int 20h e la DWORD
sucessiva con una chiamata diretta al servizio. Cosi' il precedente codice
dell' int 20h verrebbe trasformato in :
call dword ptr
[VxD_Service_Address]
Questo trucco funziona perche' int
20h+dword occupa 6 bytes di spazio, che e' esattamente lo stesso
dell'istruzione call dword ptr. In questo modo
le chiamate sucessive sono piu' efficenti. Questo metodo ha pero' i suoi pro e
contro. Di positivo c'e' che esso riduce il carico di lavoro di VMM e del VxD
loader dato che essi non devono risolvere TUTTE le chiamate al momento del caricamento. Le
chiamate che non sono mai eseguite rimarranno immutate. Di non proprio positivo
c'e' che esso rende il rilascio del VxD impossibile. Dal momento che VMM risolve
le chiamate con gli indirizzi effettivi dei servizi del VxD, il VxD che fornisce
questi servizi non puo' essere scaricato dalla memoria. Non esiste un
meccanismo per il ripristino dei
collegamenti. Corollario di questo fatto e' che i VxD dinamici non sono indicati
come fornitori di VxD services.
Callbacks: Le callbacks, o meglio le funzioni
callback, sono funzioni del VxD che esistono per essere chiamate dagli altri
VxDs. Non fate confusione fra callbacks e servizi. Le callabacks non sono
pubbliche come i servizi. Esse sono funzioni private di cui un VxD
fornisce, in specifiche situazioni, l'indirizzo ad un'altro VxD. Ad esempio,
quando un VxD sta' servendo un interrupt hardware, il VxD non puo' chiamare
alcun VxD services altrimenti, visto che VMM non e' rientrante, potrebbe
causare un page fault. Il VxD potrebbe invece passare l'indirizzo di una delle
sue funzioni (la callback) alla VMM, cosicche' VMM possa chiamare la funzione
quando e' in grado di tollerare i page faults. Il VxD puo' quindi continuare col
suo lavoro mentre la sua funzione callback e' invocata. L'idea della callback
non e' esclusiva dei VxD. Molte API di Windows le utilizzano allo stesso
modo. Il miglior esempio e' forse la window procedure. Voi normalmente
specificate l'indirizzo della window procedure nelle struttura WNDCLASS o
WNDCLASSEX e passate questa a Windows con una chiamata a RegisterClass o
RegisterClassEx. A questo punto, quando ci saranno messagi per la finestra,
Windows chiamera' la vostra window procedure. Un'altro esempio e' una procedura
di hook di Windows. La vostra applicazione passa a Windows l'indirizzo della
procedura di hook, in tal modo Windows la chiamera' solo quando si sara'
verificato un evento a cui l'applicazione e' interessata.
I tre metodi sopraelencati sono per
la comunicazione fra VxDs. Esistono anche altre interfaccie per la modalita'
V86, il modo protetto e per le applicazioni Win32.
Traduzione italiana a cura di: Kill3xx - kaneda27@hotmail.com
Torna alla pagina principale |