Caricare documenti e articoli online 
INFtub.com è un sito progettato per cercare i documenti in vari tipi di file e il caricamento di articoli online.


 
Non ricordi la password?  ››  Iscriviti gratis
 

REALIZZAZIONE DI UN DRIVER

informatica



REALIZZAZIONE DI UN DRIVER


Oggi vedremo più da vicino le routine che rappresentano i 'mattoni di costruzione' mediante i quali  vengono realizzati i driver. Ricordiamo che i driver che possono essere eseguiti nel contesto del processo che li richiede (il processo utente effettua una chiamata al kernel per poter eseguire il driver, e quindi accedere alla periferica) o nel contesto di un processo driver appositamente incaricato, con il quale i processi utente si sincronizzano, stabilendo un rapporto cliente-servitore. In entrambi i casi il kernel deve disporre di determinate procedure necessarie per scrivere questi driver. Per schematizzare la cosa, supporremo di fare riferimento ad un necessaire di tali procedure costituito da una procedura per l'avvio dell'operazione di scrittura sulla periferica, una per l'arresto della scrittura, una per effettuare un'operazione di input, una per effettuare un'operazione di output, una per acquisire uno stato.




Il codice in pascal-like di tali procedure è il seguente:


Procedure Start_IO (p: dispositivo_periferico) ;

begin

< esecuzione delle istruzioni macchina per t 626i81g rasferire nel registro

di controllo dell'interfaccia i bit di attivazione del dispositivo

e di abilitazione dell'interfaccia ad operare ad interruzione > ;

end ;


Procedure Halt_IO (p: dispositivo_periferico) ;

begin

< esecuzione delle istruzioni macchina per t 626i81g rasferire nel registro

di controllo dell'interfaccia i bit di disattivazione del dispositivo > ;

end ;


Procedure Get (p: dispositivo_periferico; var d: dato) ;

begin

d : = < registro buffer dell'interfaccia > ;

end ;


Procedure Put (p: dispositivo_periferico; d: dato) ;

begin

< registro buffer dell'interfaccia > : = d ;

end ;


Procedure Get_status (p: dispositivo_periferico; var s: dato) ;

begin

s : = < registro di stato dell'interfaccia > ;

end ;



Tali procedure sono scritte nel modo più generale possibile. Non è possibile dire di più, in quanto la loro reale implementazione dipende ovviamente dalla struttura della periferica ed in particolare da quella della sua interfaccia. Per scrivere queste routine in ASSEMBLER, bisogna conoscere esattamente gli indirizzi associati ai registri di interfaccia e la funzione svolta da ciascuno di essi, onde potervi inviare quelle determinate sequenze di byte che permetteranno alla periferica di partire, fermarsi eccetera.

Se il processo utente si rivolge direttamente al Kernel, le procedure del Kernel verificano se l'utente può accedere alla periferica (e cioè se questa è libera o occupata), e in caso affermativo vengono attivate le routine necessarie ad avviare l'operazione.

Se invece si usa la tecnica del processo driver, le procedure di dialogo con la periferica fanno parte di quest'ultimo, e dunque il processo utente deve rivolgersi al processo driver chiedendo l'esecuzione di una di tali procedure. Ad esempio un modo di procedere per realizzare la comunicazione tra processo utente e processo driver è quello di usare una struttura monitor: in questo caso potremmo sfruttare i metodi già visti per la cooperazione tra processi nel modello a memoria comune. Si userebbe un buffer circolare per lo scambio delle informazioni tra i due processi, una procedura SEND che permette al processo utente di caricare un elemento del buffer centrale con i dati da dare in output, una RECEIVE per prelevare dal buffer i dati da dare in input o in output ed una procedura interna al monitor che permette al driver di prendere i dati dal buffer circolare e mandarli in output.

In questo caso il processo driver si farebbe carico di realizzare la maggioranza delle operazioni. In particolare, dovrebbe fare lui le operazioni di START I/O, HALT I/O, PUT e GET e utilizzerebbe il monitor solo per scambiare informazioni con il processo utente che richiede il servizio di I/O, e in particolare per fra da ponte tra il buffer circolare ed il buffer dell'interfaccia [1]. Si noti dunque che in questo caso si ha lo svantaggio di dovere effettuare più copie del messaggio di input o output, ma anche il vantaggio di 'esplodere il parallelismo'. Mentre ad esempio il processo driver esegue uno spostamento di caratteri dal buffer privato verso il buffer della periferica, in parallelo un altro processo utente può caricare il buffer circolare del monitor. Se invece anche tutte le routine summenzionate (START I/O etc.) vengono poste all'interno del monitor, si risparmia un'operazione di copia (tali routine accederebbero infatti direttamente al buffer del monitor), ma si riduce di molto il parallelismo, visto che all'interno di un monitor può esserci al massimo un processo attivo per volta.


Quale che sia la tecnica scelta, il kernel deve prevedere il fatto che nel momento in cui giunge dalla periferica un segnale di interrupt di fine I/O, e si entra quindi nella ISR, questa deve far ripartire il processo nel cui contesto il driver viene eseguito (rispettivamente, si tratterà del processo driver oppure di quel particolare processo utente che è entrato nel Kernel chiedendo di eseguire il processo driver). Abbiamo già visto che nel caso più semplice si suppone di avere una procedura wait_for_interrupt, che è una WAIT su di un semaforo privato con la quale il processo che esegue il codice di entrata si arresta, e una SIGNAL nel ramo della ISR nel quale si entra in occasione dell'interrupt, in modo tale che il processo arrestato possa riprendere la esecuzione.

Un'ottimizzazione potrebbe consistere nel mettere nella ISR, piuttosto che una semplice SIGNAL, una specializzazione di quest'ultima, e precisamente una procedura di risposta alla interruzione.

Infatti una SIGNAL ordinaria inserita nella ISR avrebbe semplicemente l'effetto di inserire il processo che ha eseguito la wait_for_interrupt (e quindi il processo driver nel modello che più ci interessa) nella coda dei processi ready. È bene invece che la coda del driver abbia la massima priorità, e che quindi sia riattivata immediatamente all'arrivo dell'interruzione di ritorno, rimpiazzando il processo che nel frattempo era stato posto in esecuzione nella CPU. Infatti, l'esecuzione della coda del driver ha anche l'effetto di liberare la periferica, la quale diversamente potrebbe rimanere bloccata per troppo tempo. Con una SIGNAL normale diminuisce il THROUGHPUT, cioè il numero di lavori sviluppati nell'unità di tempo. Con la procedura che andiamo a presentare adesso, invece, si 'esplode meglio' il parallelismo tra le operazioni sulla unità centrale e sulla periferica.



Procedure Risposta_interruzione (sp: indice_semaforo) ;

var k: indice_processi;


Procedure Inserimento_testa ;

var j : indice_processi ;

begin

j : = processo_in_esecuzione ;

descrittore_processo j .successivo : = coda_processi_pronti .primo ;

coda_processi_pronti .primo : = j ;

end ;


begin

Salvataggio_stato ;

Inserimento_testa ;

with semafori sp do

begin

if coda.primo <> 0 then

begin

Prelievo (k, coda) ;

processo_in_esecuzione : = k ;

Ripristino_stato ;

end ;

else

contatore : = contatore + 1 ;

end

end ;



Si noti che sarebbe superfluo, benché formalmente corretto, usare una SIGNAL CON PRERILASCIO (cfr. nota 19 a pag.37); non ci si pone cioè il problema di paragonare la priorità del processo sbloccante e quella del processo sbloccato, in quanto si dà per scontato che lo sbloccante sia meno prioritario rispetto allo sbloccato [2]. Il processo interrotto deve perdere il controllo della CPU, e quindi si effettua il salvataggio dello stato, ma esso viene poi inserito in testa alla coda dei processi ready, anziché nella posizione che sarebbe dettata dalla sua priorità, dimodoché il processo interrotto riparta non appena termina l'esecuzione del driver. Questa operazione viene realizzata dalla routine Inserimento_testa.

A questo punto viene sbloccato il processo che si era arrestato su quel semaforo privato con la Wait_for_interrupt. Da notare che il ramo else non dovrebbe mai essere percorso, dato che se viene eseguita la procedura Risposta_interruzione un qualche processo deve pur essersi messo in attesa sulla coda del semaforo; tuttavia, la procedura ha un carattere più generale e prevede anche il caso di risposta alla interruzione di un timer (attivazione di un processo dopo un certo intervallo di tempo) nel qual caso potrebbe effettivamente accadere che non ci sia nessun processo ad aspettare.


Le cose si semplificano notevolmente ricorrendo ad una procedura di risposta ad interruzione più banale:


Procedure Risposta_interruzione (p: dispositivo_periferico) ;

Procedure Inserimento_testa ;

begin

< idem >

end ;


begin

Salvataggio_stato ;

Inserimento_testa ;

processo_in_esecuzione : = driver p

Ripristino_stato ;

end ;


In questo caso si rinuncia a fare qualunque analisi, e in risposta al segnale di interruzione si attiva direttamente il processo driver p . Driver è un vettore contenente i driver delle varie periferiche. Il suo elemento di posto p viene ripristinato, con un'ulteriore ottimizzazione dei tempi..


In UNIX non esiste il concetto di processo driver, ma le operazioni di ingresso uscita possono essere realizzate solo da parte del processo utente mediante chiamate al kernel. La ISR possiede tanti rami quanti sono i driver, e in ognuno di essi è presente la coda del rispettivo driver, la quale viene eseguita. Dopodiché si torna al processo utente.

Nei fatti, il processo utente è penetrato verso l'interno del SO, strato per strato, mediante una sequenza di chiamate, fino ad entrare nel kernel e a lanciare una operazione periferica; quindi dopo l'esecuzione di tale operazioni la stessa sequenza di chiamate dovrà essere eseguita, ma a ritroso e nel modo più rapido possibile. Il processo utente all'interno del Kernel assume una priorità altissima rispetto a quella che deterrebbe rimanendo al suo esterno.




Presumibilmente mediante un terzo buffer, nel seguito indicato come buffer privato, che è situato fra gli altri due.

L'osservazione non sembra molto pertinente. Infatti, non è il processo P attualmente in esecuzione presso la CPU ad eseguire la SIGNAL. P riceve un segnale di interruzione dalla periferica per effetto di un'operazione di I/O che era stata eseguita in precedenza da un'altro processo. In conseguenza, si attiva la ISR nel contesto della quale si esegue la SIGNAL.




Privacy




Articolo informazione


Hits: 1652
Apprezzato: scheda appunto

Commentare questo articolo:

Non sei registrato
Devi essere registrato per commentare

ISCRIVITI



Copiare il codice

nella pagina web del tuo sito.


Copyright InfTub.com 2024