Estratto del documento

11. Gestione I/O e interruzioni

Architettura di un calcolatore

Un calcolatore è costituito dalle seguenti componenti:

  • CPU, che ha il compito di eseguire le istruzioni;
  • Memoria, che contiene le istruzioni ed i dati;
  • Periferiche di I/O, come mouse, tastiera, USB, schermi, …, che permettono di interagire con il calcolatore;
  • Bus dati, bus indirizzi, bus di controllo, ovvero dei canali di comunicazione per le componenti del calcolatore.

N.B: i dispositivi di I/O e la CPU procedono in modo asincrono, ognuno alla propria velocità.

L’interfaccia è l’intermediario tra il dispositivo I/O ed il bus. Tale interfaccia deve fornire:

  • Registri di appoggio per i dati da inviare/ricevere, detti DREG;
  • Registri per i comandi alla periferica, detti CREG;
  • Registri per leggere lo stato della periferica, ovvero per tenere traccia dello stato della periferica e di eventuali errori, detti SREG.

La CPU, per accedere ai precedenti registri delle interfacce, può utilizzare due diverse modalità:

  • I/O mappato a memoria, ovvero una parte dello spazio di indirizzamento della memoria viene riservato per la comunicazione con i dispositivi, ad esempio la memoria video.
  • I/O isolato, ovvero la CPU utilizza delle particolari istruzioni per interagire con i dispositivi attraverso delle porte di I/O. Tali porte sono identificate attraverso un numero, detto indirizzo. In questo caso, lo spazio di indirizzamento sarà distinto da quello della memoria.

In particolare, in questo caso, le istruzioni vengono eseguite con dei cicli di bus del tutto analoghi a quelli di accesso alla memoria, ma vengono utilizzate due linee di comando dette IORC, I/O Read Command, ed IOWC, I/O Write Command, per eseguire rispettivamente una lettura ed una scrittura.

Ad esempio, per il processore x86 utilizzeremo le seguenti istruzioni:

IN AL, PORT, PORT IORC: si accede alla porta indicata dal numero e si asserisce per leggere il valore contenuto nella porta. Tale valore viene scritto sul bus dati e viene successivamente inviato al registro AL.

OUT PORT, AL, AL: si scrive sul bus dati il valore contenuto sul registro. Successivamente, si accede alla porta indicata dal numero PORT e si asserisce IOWC per scrivere il valore contenuto nel bus dati.

Entrambe.

Poiché possiamo avere più interfacce, una per ogni dispositivo I/O, dobbiamo capire con quale interfaccia vogliamo comunicare. Questo può essere fatto attraverso la decodifica degli indirizzi.

La decodifica degli indirizzi identifica se viene richiesto l’utilizzo di una specifica porta di un’interfaccia e, in caso positivo, definisce se la richiesta è in lettura o in scrittura.

Una volta acceduti all’interfaccia I/O interessata, si possono attivare le porte I/O per interagire sul bus dati. Tali porte di I/O saranno quindi accessibili anche alle periferiche.

Es: in questo caso, specificheremo l’indirizzo della porta attraverso 8 bit … ) in ingresso ad una porta NAND. ( 7 0 Perciò, accediamo alla porta solo se abbiamo la configurazione in figura: sono posti ad 1, indicano il valore F, i bit, ovvero i bit 7 6 5 4 3 2, sono posti a 0 ed il bit 1 è posto a 1.

N.B: il bit 0 può essere posto ad 0 o a 1, a seconda se vogliamo accedere rispettivamente alla porta F2, infatti avremo 3 2 1 0 = 0010, o F3, avremo 3 2 1 0 = 0011. �����

Il segnale, che segnala l’accesso alla porta, sarà asserito solo se leggiamo sul bus indirizzi la precedente configurazione, che indicano i valori F2 o F3 a seconda del valore di ������.

Attiva il segnale 0, ovvero la porta 0, P0, in lettura. ������� 0

Attiva il segnale 1, ovvero la porta 1, P1, in scrittura, W. �����

N.B: ����� è un segnale in logica negativa. In questo modo possiamo stabilire a quale porta vuole accedere il processore. ������� ��������

Per definire se vogliamo accedere in lettura oppure in scrittura dobbiamo utilizzare rispettivamente i segnali e. �����

N.B: quando otteniamo un segnale diverso da quello specificato in figura, allora il segnale non sarà attivato e questo indica che non stiamo comunicando con quella specifica porta.

Porte I/O

Vediamo adesso come avviene la gestione delle porte I/O da parte dell’interfaccia.

  • Porta di ingresso, dalla periferica: si realizza con un buffer 3-state, ovvero si pone l’uscita in un terzo stato di attesa nel caso in cui non sia attivo l’Output Enable, OE, la cui uscita viene abilitata quando viene selezionata la porta e viene asserito IORC, ovvero si legge l’istruzione IN. Porta di uscita.
  • Porta di uscita, verso la periferica: si realizza con dei latch che memorizzano il dato proveniente dal bus dati quando viene selezionata la porta e viene asserito IOWC, ovvero si legge l’istruzione OUT. Porta di ingresso 2.

In genere, non abbiamo nessuna sincronizzazione tra il programma in esecuzione, che solitamente è più veloce, e le periferiche, che sono molto più lente, perciò la CPU deve attendere. Possiamo definire 3 tecniche fondamentali per la gestione delle periferiche:

  • Gestione a controllo di programma;
  • Gestione sotto controllo di interruzione;
  • Gestione tramite accesso diretto alla memoria o con processori I/O.

N.B: le prime due tecniche sono utilizzate per lo più con i dispositivi lenti, ovvero che forniscono dati più lentamente rispetto alla velocità della CPU, mentre l’ultima tecnica si utilizza principalmente quando un dispositivo genera molti dati contemporaneamente.

Vediamo in dettaglio queste tecniche.

Gestione a controllo di programma

Consideriamo, ad esempio, un’interfaccia di uscita verso una stampante, che è un dispositivo molto lento. Sarà necessario un protocollo di hand-shaking dall’interfaccia verso la stampante, che si basa su 2 segnali:

  • DAV, Data AVailable, indica che il dato è disponibile;
  • DAC, Data ACknowledge, indica che il dato è stato acquisito e quindi possiamo inviare un nuovo dato.

Vediamo quindi come possiamo realizzare questa interfaccia di uscita: ����� come abbiamo visto, il segnale viene asserito se sul bus indirizzi è stata specificata la porta interessata, in questo caso dobbiamo verificare se sul bus indirizzi è contenuto il valore E1.

Notiamo che questa interfaccia presenta 2 porte, una di uscita, PORTW, e una di entrata, PORTR, che hanno lo stesso indirizzo.

Nel caso dell’istruzione OUT, invio del dato verso la stampante, sarà attivato il segnale �������� e quindi sarà selezionata la porta di scrittura �����������, attraverso il segnale, per acquisire i dati, provenienti dal bus dati, sul latch. Una volta memorizzati i dati, comunicheremo alla stampante l’invio dei dati attraverso il segnale ������ così che questa si possa preparare alla ricezione dei dati scritti nel latch.

Una volta ricevuto i dati, la stampante comunica il segnale ������ che pone lo stato del flip-flop a zero, si fa un clear, CLR.

N.B: tale flip-flop viene sempre inizializzato ad 1, poiché è il valore in ingresso. Perciò, quando il suo stato viene posto a 0 significa che la stampante ha ricevuto correttamente i dati, segnale ������.

Nel caso dell’istruzione IN, ricezione dei dati dalla stampante, sarà attivato il segnale �������. Tale segnale asserirà l’uscita del buffer 3-state che comunicherà al bus dati il valore contenuto nel flip-flop, ovvero pone nel bit 0 di AL lo stato della stampante, nel caso di ricezione del segnale ������ si pone tale bit a 0.

Vediamo quindi come possiamo realizzare un sottoprogramma di gestione della stampa.

Si inserisce nel registro SI l’offset del buffer da stampare e nel registro CX il numero di byte da stampare. Perciò otteniamo:

USO: MOV SI, <offset BUFFER>
MOV CX, <n>
CALL STAMPA
…
STAMPA:
MOV AL, [SI] ;si invia ad AL il carattere da stampare
OUT E1h, AL ;si invia alla stampante il carattere da stampare
ATTESA: IN AL, E1h La CPU attende il segnale DAC=0 dalla stampante, avremo un tempo della CPU sprecato.
AND AL, 1
JNZ ATTESA
INC SI
LOOP STAMPA
RET

N.B: la CPU attende attivamente il segnale DAC=0, tale procedura si chiama attesa attiva, ovvero legge continuamente il valore dello stato finché non rileva il valore 0 in AL. Questo implica uno spreco di tempo della CPU, poiché resta in attesa dello stato, dobbiamo cercare di evitare questa situazione.

Una volta che abbiamo verificato la ricezione del dato alla stampante, allora proseguiamo all’invio di un altro dato incrementando il valore del registro SI, prende il byte successivo, e rieseguendo il processo di stampa, decrementando il valore di CX. 3

Gestione sotto controllo di interruzione

Abbiamo visto che con la precedente tecnica, durante l’attesa del segnale DAC, la CPU risulta bloccata. In realtà, potremmo eseguire ulteriori operazioni durante questo momento di attesa, in questo modo possiamo evitare di far sprecare tempo alla CPU, e richiamare la CPU soltanto quando viene asserito il segnale DAC dalla stampante.

Questa tecnica può essere realizzata sollevando un’interruzione, ovvero, se il segnale DAC viene comunicato mentre la CPU sta eseguendo un’altra operazione, allora si interrompe l’operazione in esecuzione e si comunica il cambiamento dello stato. Al termine di questa segnalazione, la CPU riprende la sua operazione.

Perciò, all’interruzione, viene eseguita una routine di servizio, detta interrupt handler, che invierà l’eventuale nuovo dato alla stampante, dopo di che la CPU riprenderà l’esecuzione da dove era stata interrotta.

L’esecuzione della routine avviene tra l’esecuzione di due istruzioni. In questo modo non avremo più un’attesa attiva.

Vediamo un modello semplificato del sistema di interruzione. Ipotizziamo che il sistema piloti una sola periferica.

La linea INTR in ingresso alla CPU indica una richiesta di interruzione. Inoltre, all’interno della CPU esiste un flag IE, Interrupt Enable, che indica l’abilitazione delle interruzioni.

Al termine dell’esecuzione di un’istruzione, la CPU controlla se è arrivata una interruzione IINTR. In caso positivo, la CPU deve azzerare IE, in modo che non possa essere interrotta, e fare il fetch dell'istruzione contenuta all'indirizzo 0, senza modificare il contenuto del Program Counter, PC.

Tale istruzione all'indirizzo 0 dovrà essere un’istruzione di CALL alla procedura di gestione degli interrupt. In questo modo, salviamo nello stack il valore contenuto nel PC, per permettere alla CPU di continuare ad eseguire le istruzioni da dove era stata interrotta, e chiamiamo la procedura di gestione degli interrupt.

La procedura terminerà con l’istruzione IRET, che oltre ad eseguire il RET alla procedura principale, abilita anche le interruzioni.

Possiamo quindi modificare l’interfaccia descritta per la gestione a controllo di programma per adattarla alla gestione dei dispositivi di I/O tramite interruzione, in modo tale che l’interfaccia generi un’interruzione quando viene segnalato il DAC dalla stampante.

Notiamo che viene aggiunto un registro IENFF di tipo D, accessibile in scrittura per attivare o disattivare le interruzioni. Ovvero, quando è attivo ������������, tramite il segnale di selezione ������� oppure tramite il segnale di scrittura ��������, allora il registro IENFF leggerà sul bus dati il contenuto al bit 0, DB0. Perciò, a seconda del valore contenuto in DB0, possiamo capire se è stata generata un’interruzione oppure no.

Dunque, quando viene asserito il segnale ������, allora �SFF conterrà il valore 0 e quindi l’uscita sarà 1.

Quindi, se IENFF contiene il valore 1, allora possiamo generare un interrupt INTR verso la CPU. Ovvero, quando è asserito ������ otteniamo INTR=1.

Al contrario, quando è asserito ������ avremo INTR=0, ovvero l’interruzione è disattivata.

L’esecuzione della routine di servizio dell’interrupt deve essere trasparente rispetto al programma interrotto. Perciò, alla posizione 0 viene inserita l’istruzione CALL alla routine di gestione dell’interruzione.

Tale routine di gestione dell’interruzione deve:

  • Salvare sullo stack la parola di stato, PSW, e tutti i registri utilizzati;
  • Trasferire il prossimo dato;
  • Disasserire la richiesta dell’interruzione;
  • Ripristinare i registri;
  • Ritornare al programma interrotto, tramite IRET che, come abbiamo visto, abilita anche l’interruzione.

Possiamo realizzare tale routine di servizio nel seguente modo:

USO: MOV SI, <offset BUFFER>
MOV CX, <n>
CALL STAMPA
…
STAMPA: ;inizializza una variabile di memoria BUSY ad 1
MOV BUSY, 1 ;inizializza una variabile di memoria IND il contenuto di SI
MOV IND, SI ;inizializza una variabile di memoria COUNT la dimensione del buffer da stampare contenuta in CX
MOV COUNT, CX
MOV AL, [SI] ;invia ad AL il primo carattere da stampare
OUT DPORT, AL ;si invia all’interfaccia il primo valore da stampare tramite la porta dati (DPORT) 4
;si setta AL ad 1
MOV AL, 1 ;si abilita l’interruzione
OUT CPORT, AL
RET

Mentre, possiamo realizzare la routine di gestione dell’interruzione nel seguente modo, come abbiamo detto, alla posizione 0 avremo una chiamata ad INTSTAMP:

INTSTAMP:
PUSH PSW
PUSH AX Si salva sullo stack la parola di stato (PSW) ed il contenuto dei 3 registri utilizzati
PUSH CX
PUSH SI ;si scrive in SI il valore che avevamo salvato in IND dell’ultimo carattere che avevamo inviato
MOV SI, IND ;si incrementa SI, così conosciamo il prossimo valore da stampare
INC SI ;si scrive in CX il valore che avevamo salvato in COUNT
MOV CX, COUNT ;si decrementa la dimensione dei dati da stampare CX
DEC CX ;se CX è arrivato a 0, non abbiamo più elementi da stampare, vai all’etichetta FINE
JZ FINE
MOV AL, [SI] ;si invia ad AL l’elemento da stampare
OUT DPORT, AL ;si invia all’interfaccia il valore di AL da stampare
;salviamo l’attuale valore di SI in IND, si memorizza per poterlo riusare alla prossima interruzione
MOV IND, SI ;salviamo il valore di CX in COUNT, si memorizza per poterlo riusare alla prossima interruzione
MOV COUNT, CX
ESCI: POP SI
POP CX Si ripristina lo stato iniziale facendo il POP in ordine inverso
POP AX
POP PSW
IRET
FINE: ;una volta terminata la stampa si setta BUSY a 0
MOV BUSY, 0 ;si mette in AL il valore 0
MOV AL, 0 ;si invia alla porta di controllo dell’interruzione (CPORT) il valore 0, ovvero si disattiva l’interrupt
OUT CPORT, AL ;si ripristina lo stato iniziale facendo il POP
JMP ESCI

Ipotizziamo di eseguire 2 volte la procedura di stampa:

MOV SI, MSG1
MOV CX, 10
CALL STAMPA
…
MOV SI, MSG2
MOV CX, 5
CALL STAMPA

In questo caso, potremmo procedere alla seconda stampa solo se è già terminata la prima stampa. Questa situazione però non è garantita e dovremmo verificarla. Un modo per fare questa verifica è quella di aggiungere un controllo sul valore di BUSY, si controlla se BUSY=0 poiché in tal caso avremmo la garanzia che la prima stampa sia terminata:

WAIT: MOV AL, BUSY
JNZ WAIT

Questa verifica però comporta un’attesa attiva, che, come abbiamo già visto, comporta un tempo sprecato dalla CPU.

Un modo per evitare questo problema può essere l’utilizzo di code, si inseriscono i buffer da stampare in memoria così che l’interrupt handler possa verificare quale carattere deve stampare. Questo sistema viene gestito in modo trasparente dal sistema operativo.

Fino ad adesso, però, abbiamo considerato il caso di un’interruzione generata da una sola periferica. Vediamo però cosa accade quando abbiamo più periferiche che possono generare interruzioni. In questo caso si possono verificare i seguenti problemi:

  • Riconoscere quale periferica ha generato l’interruzione;
  • Scegliere quale routine di servizio dobbiamo eseguire per gestire la richiesta di quella particolare periferica;
  • Nel caso di richieste contemporanee, dobbiamo stabilire quale periferica ha priorità maggiore, gestione delle priorità;
  • Gestire l’interrompibilità della routine di servizio da parte di una periferica a priorità maggiore.

Vediamo quindi come possiamo gestire più periferiche, ognuna con la propria interfaccia. Ogni periferica indica la sua eventuale richiesta di interruzione attraverso un bit del registro ISR, Interrupt Service Request.

La routine di servizio stabilisce quale periferica ha generat

Anteprima
Vedrai una selezione di 16 pagine su 71
Appunti Sistemi Operativi Pag. 1 Appunti Sistemi Operativi Pag. 2
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 6
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 11
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 16
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 21
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 26
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 31
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 36
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 41
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 46
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 51
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 56
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 61
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 66
Anteprima di 16 pagg. su 71.
Scarica il documento per vederlo tutto.
Appunti Sistemi Operativi Pag. 71
1 su 71
D/illustrazione/soddisfatti o rimborsati
Acquista con carta o PayPal
Scarica i documenti tutte le volte che vuoi
Dettagli
SSD
Scienze matematiche e informatiche INF/01 Informatica

I contenuti di questa pagina costituiscono rielaborazioni personali del Publisher Delba1998 di informazioni apprese con la frequenza delle lezioni di Sistemi operativi e studio autonomo di eventuali libri di riferimento in preparazione dell'esame finale o della tesi. Non devono intendersi come materiale ufficiale dell'università Università degli Studi di Firenze o del prof Bellini Pierfrancesco.
Appunti correlati Invia appunti e guadagna

Domande e risposte

Hai bisogno di aiuto?
Chiedi alla community