INGRESSO E USCITA G Frosini Ingresso e uscita

  • Slides: 83
Download presentation
 INGRESSO E USCITA G. Frosini – Ingresso e uscita Slide 1

INGRESSO E USCITA G. Frosini – Ingresso e uscita Slide 1

PRIMITIVE DI I/O (1) • Corpi dei processi: – non possono utilizzare le routine

PRIMITIVE DI I/O (1) • Corpi dei processi: – non possono utilizzare le routine predefinite di I/O valide per un ambiente sequenziale, perché non gestiscono le operazioni di I/O in modo atomico; – se nel mezzo di un’operazione di I/O arriva un’interruzione, un eventuale nuovo processo potrebbe effettuare la stessa operazione di I/O, mentre il sistema interfaccia-dispositivo è in uno stato non consistente. • Nucleo multiprogrammato: – occorre definire nuove routine di I/O. • Operazioni di I/O: – sincrone o asincrone, a seconda che il processo che le esegue attenda la loro terminazione prima di proseguire, oppure prosegua compiendo altre elaborazioni. • Ambiente multiprogrammato: – utilizzo delle operazioni di I/O sincrone (il processo che le esegue si blocca per divenire di nuovo pronto quando l’operazione è terminata. G. Frosini – Ingresso e uscita Slide 2

PRIMITIVE DI I/O (2) • Operazioni di I/O: – realizzate con particolari primitive, dette

PRIMITIVE DI I/O (2) • Operazioni di I/O: – realizzate con particolari primitive, dette di I/O, che non effettuano commutazioni di contesto (non lavorano sulle code dei processi); – possono essere eseguite con le interruzioni (mascherabili) abilitate; – gestiscono la sincronizzazione (sospensione in attesa di fine operazione di I/O) e la mutua esclusione (sull’operazione di I/O) utilizzando i semafori; • tenere il più possibile le interruzioni (mascherabili) abilitate, oltre a rappresentare un requisito generale, costituisce un particolare vantaggio quando le operazioni di I/O avvengono a interruzione di programma. • Primitive di I/O: – utilizzano le istruzioni di I/O, ed hanno quindi lo stesso livello di privilegio di queste. • Ipotesi: – primitive e istruzioni di I/O hanno livello di privilegio sistema, ma non fanno parte del nucleo (per il quale le interruzioni sono disabilitate). G. Frosini – Ingresso e uscita Slide 3

PRIMITIVE DI I/O (3) • Linguaggio C++: – una primitiva di I/O (prim_io_i) corrisponde

PRIMITIVE DI I/O (3) • Linguaggio C++: – una primitiva di I/O (prim_io_i) corrisponde a un sottoprogramma di interfaccia (scritto in Assembler); – esso esegue un’interruzione software (INT $io_tipo_i) con la messa in esecuzione della primitiva di I/O vera e propria (a_prim_io_i); – la primitiva vera e propria termina con ritorno da interruzione, restituendo il controllo al sottoprogramma di interfaccia, il quale a sua volta ritorna al processo utente ; – se una primitiva di I/O viene invocata da un processo utente, si ha un innalzamento del livello di privilegio e un cambio di pila. • Interruzioni software utilizzate per le primitive di I/O: – coinvolgono gate di tipo trap, che quindi non disabilitano le interruzioni (mascherabili) (non modificano il flag IF). G. Frosini – Ingresso e uscita Slide 4

PRIMITIVE DI I/O (4) • Chiamata di una primitiva di I/O da parte di

PRIMITIVE DI I/O (4) • Chiamata di una primitiva di I/O da parte di un processo utente e sottoprogramma di interfaccia: // utente. cpp extern "C" prim_io_i(/* parametri formali */); void proc_io(int h) // codice di un processo utente { … prim_io_i(/* parametri attuali */); . . . } # utente. s. TEXT. GLOBAL prim_io_i: rit_io_i: prim_io_i # sottoprogramma di interfaccia INT $io_tipo_i # gate di tipo trap RET G. Frosini – Ingresso e uscita Slide 5

PRIMITIVE DI I/O (5) • Struttura della primitiva vera e propria (routine a_prim_io_i) :

PRIMITIVE DI I/O (5) • Struttura della primitiva vera e propria (routine a_prim_io_i) : # sistema. s. TEXT. EXTERN a_prim_io_i: c_prim_io_i # routine INT $io_tipo_i # salvataggio dei registri # eventuale verifica e ricopiamento dei parametri CALL c_prim_io_i # ripulitura della pila # ripristino dei registri IRET // sitema. cpp extern "C" void c_prim_io_i(/* parametri formali */) { /*. . . */ } G. Frosini – Ingresso e uscita Slide 6

PRIMITIVE DI I/O (6) • Routine c_prim_io: – possono utilizzare le primitive semaforiche sem_wait()

PRIMITIVE DI I/O (6) • Routine c_prim_io: – possono utilizzare le primitive semaforiche sem_wait() e sem_signal(). – a livello sistema avremo quindi: // sitema. cpp extern "C" natl sem_ini(int val); extern "C" void sem_wait(natl sem); extern "C" void sem_signal(natl sem); # sistema. s. TEXT. GLOBAL sem_ini: rit_si: . GLOBAL sem_wait: rit_w: . GLOBAL sem_signal: rit_s: sem_ini INT $tipo_si RET sem_wait INT $tipo_w RET sem_signal INT $tipo_s RET G. Frosini – Ingresso e uscita Slide 7

OPERAZIONE DI I/O • Operazione di I/O: – – supponiamo trasferisca byte e utilizzi

OPERAZIONE DI I/O • Operazione di I/O: – – supponiamo trasferisca byte e utilizzi il meccanismo di interruzione; specifica un buffer di memoria e quanti byte trasferire; attiva una specifica interfaccia (routine start_io()); esegue una operazione di wait su un semaforo di sincronizzazione bloccandosi in attesa che l’operazione sia terminata. • Interfaccia attivata: – scambia i singoli byte con il dispositivo esterno e genera una richiesta di interruzione ogni volta che è pronta per effettuare il trasferimento di un byte con il processore; – l’istruzione di I/O che svuota il registro buffer di ingresso / riempie il registro buffer di uscita costituisce la risposta per l’interfaccia, e fa ripartire il meccanismo di scambio di un nuovo byte con il dispositivo. • Operazione di trasferimento di n byte: – implica la generazione di n richieste di interruzione. G. Frosini – Ingresso e uscita Slide 8

DRIVER DI INTERRUZIONE (1) • Interruzione: – manda in esecuzione un driver, che gestisce

DRIVER DI INTERRUZIONE (1) • Interruzione: – manda in esecuzione un driver, che gestisce sia le richieste intermedie che quella finale; – il driver, se rileva che l’operazione è terminata, disattiva l’interfaccia (routine halt_io()) ed esegue un’operazione di signal sul semaforo su cui il processo utente si era bloccato, rendendolo di nuovo pronto. G. Frosini – Ingresso e uscita Slide 9

DRIVER DI INTERUZIONE (2) • Processo utente P 1: – l’operazione di wait blocca

DRIVER DI INTERUZIONE (2) • Processo utente P 1: – l’operazione di wait blocca P 1 su un semaforo di sincronizzazione; – lo schedulatore manda in esecuzione un nuovo processo utente P 2; – il driver interrompe P 2 (o il processo in esecuzione in quel momento) e, a fine operazione, fa divenire di nuovo pronto P 1. • Buffer di memoria specificato da P 1: – deve risiedere nello spazio comune, dovendo essere utilizzato dal driver quando P 1 è bloccato ed è in esecuzione P 2. • Driver: – utilizza un puntatore (punt) che contiene l’indirizzo di memoria del byte da scambiare, e un contatore (cont) che specifica il numero di byte ancora da trasferire; – ogni volta che va in esecuzione, deve aggiornare il contatore e verificare se il trasferimento da effettuare è o meno l’ultimo. In ogni caso trasferisce un byte, aggiorna il puntatore e richiama lo schedulatore. G. Frosini – Ingresso e uscita Slide 10

DESCRITTORI DI OPERAZIONE DI I/O • Interfacce: – sono risorse condivise; – se vi

DESCRITTORI DI OPERAZIONE DI I/O • Interfacce: – sono risorse condivise; – se vi sono più processi che vogliono compiere la stessa operazione di I/O utilizzando una medesima interfaccia, la parte dell’interfaccia coinvolta nell’operazione va utilizzata in mutua esclusione; – per ogni operazione di I/O viene definito un descrittore, nel quale risiedono (oltre ad altre informazioni) due semafori necessari per la sua gestione, uno di mutua esclusione e uno di sincronizzazione. • Ipotesi: – un’interfaccia è in grado di effettuare contemporaneamente operazioni di ingresso e operazioni di uscita, e possiede piedini distinti per le due richieste di interruzione; – schematizzata come un insieme di 4 registri, un buffer di ingresso RBR, un buffer di uscita TBR, un registro di controllo CTRI per l’abilitazione delle richieste di interruzione in ingresso e un registro di controllo CTRO per l’abilitazione delle richieste di interruzione in uscita. G. Frosini – Ingresso e uscita Slide 11

TIPO DES_IO • Tipo descrittore di operazione di I/O (des_io): – struttura con un

TIPO DES_IO • Tipo descrittore di operazione di I/O (des_io): – struttura con un campo indreg (contiene gli indirizzi dei registri dell’interfaccia utilizzati), due campi mutex e sincr (contengono un semaforo di mutua esclusione e un semaforo di sincronizzazione (indici)), due campi cont e punt (contengono i parametri dell’operazione). // sistema. cpp struct interf_reg { union { ioaddr i. RBR, i. TBR; } in_out; union { ioaddr i. CTRI, i. CTRO; } ctr_io; struct des_io { interf_reg indreg; natl mutex; natl sincr; natl cont; addr punt; }; }; G. Frosini – Ingresso e uscita Slide 12

DESINTI E DESINTO (1) • Interfaccia: – si hanno due descrittori (desinti e desinto)

DESINTI E DESINTO (1) • Interfaccia: – si hanno due descrittori (desinti e desinto) di tipo des_io, uno per le operazioni di ingresso e uno per le operazioni di uscita; – ogni descrittore contiene (tra l’altro) gli indirizzi i. RBR e i. CTRI per le operazioni di ingresso, e i. TBR e i. CTRO per le operazioni di uscita; – se le interfacce sono T, si ha un array di T elementi per i descrittori di ingresso e un analogo array per i descrittori di uscita. G. Frosini – Ingresso e uscita Slide 13

DESINTI E DESINTO (2) // sistema. cpp extern des_io desinti[T]; extern des_io desinto[T]; #

DESINTI E DESINTO (2) // sistema. cpp extern des_io desinti[T]; extern des_io desinto[T]; # sistema. s. DATA. GLOBAL desinti: desinto: desinti desinto. SPACE BYTE_IO # T*sizeof(des_io) • Descrittori: – devono essere inizializzati, prima di poter essere utilizzati: • il campo indreg, con gli indirizzi i. RBR e i. CTRI per i descrittori di operazioni di ingresso, e con gli indirizzi i. TBR e i. CTRO per i descrittori di operazioni di uscita; • i campi mutex e sincr facendo uso della primitiva sem_ini(). G. Frosini – Ingresso e uscita Slide 14

SOTTOPROGRAMMI DI UTILITA’ (1) • Sottoprogrammi di utilità, realizzati in Assembler e utilizzabili anche

SOTTOPROGRAMMI DI UTILITA’ (1) • Sottoprogrammi di utilità, realizzati in Assembler e utilizzabili anche da routine C++: # sistema. s. TEXT. GLOBAL inputb: inputb #. . . RET. GLOBAL go_input: #. . . RET. GLOBAL halt_input: #. . . RET # sottoprogrammi analoghi per l’uscita # outputb, go_output, halt_output G. Frosini – Ingresso e uscita Slide 15

SOTTOPROGRAMMI DI UTILITA’ (2) // la definizione dei sottoprogrammi è fatta in Assembler //

SOTTOPROGRAMMI DI UTILITA’ (2) // la definizione dei sottoprogrammi è fatta in Assembler // CONi: opportune costanti // sistema. cpp … extern "C" void inputb(ioaddr reg, natb& a) ; // { a = *reg } extern "C" void go_input(ioaddr i_ctr) ; // { natb al = CON 1; *i_ctr = al } extern "C" void halt_input(ioaddr i_ctr) ; // { natb al = CON 2; *i_ctr = al } extern "C" void outputb(natb a, ioaddr reg) ; // { *reg = a } extern "C" void go_output(ioaddr i_ctr) ; // { natb al = CON 3; *i_ctr = al } extern "C" void halt_output(ioaddr i_ctr) ; // { natb al = CON 4; *i_ctr = al } G. Frosini – Ingresso e uscita Slide 16

OPERAZIONE DI LETTURA (1) • Primitiva di I/O per la lettura di n byte

OPERAZIONE DI LETTURA (1) • Primitiva di I/O per la lettura di n byte (read_n()): – parametri: • numero d’ordine dell’interfaccia da utilizzare; • indirizzo del buffer di memoria nel quale devono essere immessi i byte; • numero di byte da leggere. // utente. cpp natb buff_r[N_r]; extern "C" void read_n(natl interf, natb vetti[], natl quanti); void proc_int(int h) // codice di un processo utente { natl interf, nn; … read_n(interf, buff_r, nn); … } G. Frosini – Ingresso e uscita Slide 17

OPERAZIONE DI LETTURA (2) # utente. s. TEXT. GLOBAL read_n: rit_r: # sistema. s.

OPERAZIONE DI LETTURA (2) # utente. s. TEXT. GLOBAL read_n: rit_r: # sistema. s. TEXT. EXTERN a_read_n: read_n INT RET # sottoprogramma di interfaccia $io_tipo_r # gate di tipo trap c_read_n # routine INT $io_tipo_r # salvataggio dei registri # verifica e ricopiamento dei parametri CALL c_read_n # ripulitura della pila # ripristino dei registri IRET G. Frosini – Ingresso e uscita Slide 18

OPERAZIONE DI LETTURA (3) // sistema. cpp void start_in(des_io* p_desi, natb vetti[], natl quanti);

OPERAZIONE DI LETTURA (3) // sistema. cpp void start_in(des_io* p_desi, natb vetti[], natl quanti); extern "C" void c_read_n(natl interf, natb vetti[], natl quanti) { des_io* p_desi; p_desi = &desinti[interf]; sem_wait(p_desi -> mutex); start_in(p_desi, vetti, quanti); sem_wait(p_desi-> sincr); sem_signal(p_desi -> mutex); } void start_in(des_io* p_desi, natb vetti[], natl quanti) { p_desi -> cont = quanti; p_desi -> punt = vetti; go_input(p_desi -> indreg. ctr_io. i. CTRI); } G. Frosini – Ingresso e uscita Slide 19

DRIVER DI INGRESSO (1) • Interruzione causata da un’interfaccia di ingresso: – manda in

DRIVER DI INGRESSO (1) • Interruzione causata da un’interfaccia di ingresso: – manda in esecuzione uno specifico driver; – esistono tanti driver diversi quanti sono i piedini del controllore di interruzione relativi alle richieste di interruzione (meccanismo di interruzione vettorizzato), e ogni driver, sia driver_i, opera su uno specifico descrittore di I/O, sia il k-mo elemento di desinti[]; – il driver è una routine di nucleo e può lavorare sulle code dei processi: esso viene quindi eseguito con le interruzioni disabilitate. • Driver di ingresso: – costituito da una routine Assembler, che richiama una funzione C++ (questa opera direttamente sul descrittore di ingresso associato a una specifica interfaccia). G. Frosini – Ingresso e uscita Slide 20

DRIVER DI INGRESSO (2) # sistema. s. TEXT. EXTERN int_tipoi_i: c_driverin_i CALL salva_stato CALL

DRIVER DI INGRESSO (2) # sistema. s. TEXT. EXTERN int_tipoi_i: c_driverin_i CALL salva_stato CALL c_driverin_i CALL invia. EOI CALL carica_stato IRET // sistema. cpp extern "C" void c_driverin_i(void) // opera su desinti[k] { natb k ; // k = …; natb cc; proc_elem* lavoro; des_sem* s; des_io* p_desi; p_desi = &desinti[k]; p_desi->cont--; if (p_desi->cont == 0) { halt_input(p_desi->indreg. ctr_io. i. CTRI); s = &array_dess [p_desi->sincr]; s->counter++; if (s->counter <= 0) { rimozione_lista(s->pointer, lavoro); inserimento_lista(pronti, lavoro); inspronti(); schedulatore(); // preemption } } inputb(p_desi->indreg. in_out. i. RBR, cc); *static_cast<natb*>(p_desi->punt) = cc; p_desi->punt = static_cast<natb*>(p_desi->punt) + 1; } G. Frosini – Ingresso e uscita Slide 21

DRIVER DI INGRESSO (3) • Azioni compiute dal driver alla fine dell’operazione (oltre alla

DRIVER DI INGRESSO (3) • Azioni compiute dal driver alla fine dell’operazione (oltre alla funzione halt_input()): – sono equivalenti a una sem_signal(); – in un driver che effettua il salvataggio dello stato (nel descrittore del processo in esecuzione), l’utilizzo diretto della primitiva non può essere effettuato, in quanto questa salverebbe di nuovo lo stato nel descrittore del processo in esecuzione, distruggendo le informazioni precedenti. • Nota: – un processo utente, quando invoca una primitiva di I/O, abilita l’interfaccia a generare richieste di interruzione per mezzo della funzione start_in(); – può accadere che il driver vada in esecuzione per l’ultima volta prima che il processo utente esegua la primitiva sem_wait() sul semaforo di sincronizzazione – • quando il processo utente è in esecuzione, le interruzioni mascherabili sono abilitate, e prima che questo esegua la sem_wait() potrebbe inserirsi un altro processo; in questo caso il driver incrementa il contatore del semaforo sincr, portandolo a 1, per cui il processo utente, quando esegue la primitiva sem_wait() su sincr non si blocca. G. Frosini – Ingresso e uscita Slide 22

OPERAZIONE DI SCRITTURA • Riportiamo solo la c_primitiva: // sistema. cpp void start_out(des_io* p_deso,

OPERAZIONE DI SCRITTURA • Riportiamo solo la c_primitiva: // sistema. cpp void start_out(des_io* p_deso, natb vetto[], natl quanti); extern "C" void c_write_n(natl interf, natb vetto[], natl quanti) { des_io* p_deso; p_deso = &desinto[interf]; sem_wait(p_deso -> mutex); start_out(p_deso, vetto, quanti); sem_wait(p_deso-> sincr); sem_signal(p_deso -> mutex); } void c_start_out(des_io* p_deso, natb vetto[], natl quanti) { p_deso -> cont = quanti; p_deso -> punt = vetto; go_output(p_deso -> indreg. ctr_io. i. CTRO); } G. Frosini – Ingresso e uscita Slide 23

DRIVER DI USCITA # sistema. s. TEXT. EXTERN int_tipoo_i: CALL c_driverout_i salva_stato CALL c_driverout_i

DRIVER DI USCITA # sistema. s. TEXT. EXTERN int_tipoo_i: CALL c_driverout_i salva_stato CALL c_driverout_i CALL invia. EOI CALL carica_stato IRET // sistema. cpp extern "C" void c_driverout_i(void) // opera su desinto[k] { natb k ; // k = …; natb cc; proc_elem* lavoro; des_sem* s; des_io* p_deso; p_deso = &desinto[k]; p_deso->cont--; if (p_deso->cont == 0) { halt_output(p_deso->indreg. ctr_io. i. CTRO); s = &array_dess [p_deso->sincr]; s->counter++; if (s->counter <= 0) { rimozione_lista(s->pointer, lavoro); inserimento_lista(pronti, lavoro); inspronti(); schedulatore(); // preemption } } cc = * static_cast<natb*>(p_deso->punt); // trasferimento outputb(cc, p_deso->indreg. in_out. i. TBR); p_deso->punt = static_cast<natb*>(p_deso->punt) + 1; } G. Frosini – Ingresso e uscita Slide 24

REGISTRO DI CONTROLLO UNICO (1) • Schematizzazione di un’interfaccia gestita a interruzione di programma:

REGISTRO DI CONTROLLO UNICO (1) • Schematizzazione di un’interfaccia gestita a interruzione di programma: – in grado di effettuare contemporaneamente ingresso e uscita; – costituita da 4 registri interni, RBR, TBR, CTRI e CTRO. • Registro di controllo unico (come le interfacce seriali o le interfacce a blocchi presenti sui Personal Computer ), sia CTR: – esistono bit distinti, siano bi e bo, per l’abilitazione dell’interfaccia a effettuare richieste di interruzioni per buffer di ingresso pieno o per buffer di uscita vuoto; – ciascuna delle due funzioni start_in() e start_out(), per abilitare un’interfaccia, deve: 1. leggere il contenuto di CTR in un registro di appoggio; 2. porre a 1 il bit bi o il bit bo (lasciando invariati gli altri bit) nel registro di appoggio; 3. scrivere il nuovo contenuto del registro di appoggio in CTR. G. Frosini – Ingresso e uscita Slide 25

REGISTRO DI CONTROLLO UNICO (2) • Con le interruzioni (mascherabili) abilitate potrebbe verificarsi la

REGISTRO DI CONTROLLO UNICO (2) • Con le interruzioni (mascherabili) abilitate potrebbe verificarsi la seguente sequenza di eventi: – un processo di ingresso P 1 compie l’azione 1), poi viene interrotto; – va in esecuzione un processo di uscita P 2, che compie le azioni 1), 2) e 3), poi viene interrotto; – va nuovamente in esecuzione il processo P 1, che compie le azioni 2) e 3), rendendo nulla l’azione 3) effettuata da P 2. • Registro di controllo unico: – occorre quindi proteggerlo in mutua esclusione. • Soluzione più utilizzata: – mutua esclusione che riguarda tutta l’interfaccia; – utilizzo di descrittori di interfaccia (con indirizzi di tutti i registri interni) invece che di descrittori delle singole operazione di lettura e di scrittura. G. Frosini – Ingresso e uscita Slide 26

DISPOSITIVI A BLOCCHI • Interfaccia di I/O: – detta a blocchi quando l'unità di

DISPOSITIVI A BLOCCHI • Interfaccia di I/O: – detta a blocchi quando l'unità di trasferimento dati non è il byte, ma una sequenza di byte, di lunghezza prefissata, denominata blocco. • Dispositivo a blocchi: – memorizza un certo numero di blocchi, ciascuno identificato da un indirizzo (sul dispositivo); – letture e scritture di uno o più blocchi. • Esempio: – unità a disco rigido; – i blocchi sono chiamati settori: • ciascun settore è composto (tipicamente) da 512 byte. • Modo di funzionamento utilizzato: – interruzione di programma. G. Frosini – Ingresso e uscita Slide 27

INTERFACCIA A BLOCCHI • Interfaccia per un dispositivo a blocchi: – un solo piedino

INTERFACCIA A BLOCCHI • Interfaccia per un dispositivo a blocchi: – un solo piedino per le richieste di interruzione (in grado di compiere una sola operazione alla volta) e provvista di un buffer interno avente capacità di un blocco. • • • quattro registri di 8 bit, un registro buffer BR da cui leggere o in cui scrivere i vari byte del blocco, un registro di controllo CTR per abilitare l’interfaccia a inviare richieste di interruzione (sia in lettura che in scrittura), un registro di comando CMD per specificare il tipo di operazione (lettura o scrittura), un registro di conteggio SCR per specificare il numero dei blocchi coinvolti nell’operazione; un registro di indirizzo di blocco BAR di 32 bit, per specificare l'indirizzo (sul dispositivo) del primo blocco che si desidera leggere o scrivere; lettura di CMD: risposta a una richiesta di interruzione (l’interfaccia rimuove la richiesta). G. Frosini – Ingresso e uscita Slide 28

OPERAZIONE DI LETTURA • Azioni iniziali: – scrivere l'indirizzo (sul dispositivo) del primo blocco

OPERAZIONE DI LETTURA • Azioni iniziali: – scrivere l'indirizzo (sul dispositivo) del primo blocco in BAR, scrivere il numero di blocchi in SCR, abilitare l'interfaccia (tramite CTR), scrivere il codice del comando di lettura in CMD (costante CMD_READ): • quest'ultima operazione avvia la lettura. • Interfaccia: – trasferisce ogni blocco dal dispositivo nel buffer interno; – genera una richiesta di interruzione per buffer di ingresso pieno. • Routine di interruzione: – notifica che la richiesta di interruzione è stata accettata, leggendo CMD; – copia il blocco in memoria principale eseguendo una sequenza di letture dal registro BR; – se l’interfaccia è abilitata, alla fine di questa operazione effettua un novo trasferimento dispositivo-interfaccia. G. Frosini – Ingresso e uscita Slide 29

OPERAZIONE DI SCRITTURA • Azioni iniziali: – scrivere l'indirizzo (sul dispositivo) del primo blocco

OPERAZIONE DI SCRITTURA • Azioni iniziali: – scrivere l'indirizzo (sul dispositivo) del primo blocco in BAR, scrivere il numero di blocchi in SCR, abilitare l'interfaccia (tramite CTR), scrivere il codice del comando di scrittura in CMD (costante CMD_WRITE); – scrivere il contenuto del primo blocco da trasferire nel buffer interno dell'interfaccia, eseguendo una sequenza di scritture in BR; • il termine di quest'ultima operazione avvia il trasferimento dal buffer interno al dispositivo. • Interfaccia: – trasferisce ogni blocco dal buffer interno al dispositivo; – genera una richiesta di interruzione quando il buffer interno diviene vuoto. • Routine di interruzione: – notifica che la richiesta di interruzione è stata accettata leggendo CMD; – copia un nuovo blocco da memoria principale nel buffer interno eseguendo una sequenza di scritture nel registro BR. G. Frosini – Ingresso e uscita Slide 30

DESCRITORI DI INTERFACCE A BLOCCHI // sistema. cpp. . . struct interfb_reg { ioaddr

DESCRITORI DI INTERFACCE A BLOCCHI // sistema. cpp. . . struct interfb_reg { ioaddr i. BR, i. CTR, i. CMD, i. SCR; ioaddr i. BAR; }; struct desb_io { interfb_reg indreg; natl mutex; natl sincr; natb cont; // numero di blocchi addr punt; // indirizzo di un byte in memoria }; extern desb_io desintb[TB]; . . . # sistema. s. DATA …. GLOBAL desintb: . SPACE BYTE_BIO # TB*sizeof(desb_io). . . • Descrittori: – devono essere inizializzati prima di essere utilizzati. G. Frosini – Ingresso e uscita Slide 31

SOTTOPROGRAMMI DI UTILITA’ (1) # sistema. s. . TEXT …. GLOBAL outputl: . GLOBAL

SOTTOPROGRAMMI DI UTILITA’ (1) # sistema. s. . TEXT …. GLOBAL outputl: . GLOBAL inputbb: . GLOBAL outputbb: #. . GLOBAL gob_inout: . GLOBAL haltb_inout: outputl #. . . RET inputbb #. . . RET outputbb RET gob_inout #. . . RET haltb_inout #. . . RET G. Frosini – Ingresso e uscita Slide 32

SOTTOPROGRAMMI DI UTILITA’ (2) // la definizione dei sottoprogrammi è fatta in Assembler //

SOTTOPROGRAMMI DI UTILITA’ (2) // la definizione dei sottoprogrammi è fatta in Assembler // CONi: opportune costanti // sistema. cpp. . . // CONBi: opportune costanti extern " C " void outputl(natl p, ioaddr reg); // { *reg = p; } extern "C" void inputbb(ioaddr reg, natb quanti, natb p[]) ; // { for (int i = 0; i < quanti; i++) p[i] = *reg; } extern "C" void outputbb(natb p[], natb quanti, ioaddr reg) ; // { for (int i = 0; i < quanti; i++) *reg = p[i]; } extern "C" void gob_inout(ioaddr ctr) ; // { natb al = CONB 1; *ctr = al } extern "C" void haltb_inout(ioaddr ctr) ; // { natb al = CONB 2; *ctr = al } G. Frosini – Ingresso e uscita Slide 33

PRIMITIVE DI I/O A BLOCCHI (1) • Primitive di I/O: readb_n() e writeb_n() (numero

PRIMITIVE DI I/O A BLOCCHI (1) • Primitive di I/O: readb_n() e writeb_n() (numero blocchi da trasferire: N_r e N_w). // utente. cpp. . . natb buffb_r[N_r * 512]; natb buffb_w[N_w * 512]; extern "C" void readb_n(natl interf, natb vetti[], natl primo, natb quanti); extern "C" void writeb_n(natl interf, natb vetto[], natl primo, natb quanti); void proc_b(int h) // codice di un processo utente { natl interf, pp; natb nn; . . . readb_n(interf, buffb_r, pp, nn); . . . writeb_n(interf, buffb_w, pp, nn); . . . } G. Frosini – Ingresso e uscita Slide 34

PRIMITIVE DI I/O A BLOCCHI (2) # utente. s …. TEXT. GLOBAL readb_n: rit_br:

PRIMITIVE DI I/O A BLOCCHI (2) # utente. s …. TEXT. GLOBAL readb_n: rit_br: . GLOBAL writeb_n: rit_bw: readb_n INT RET writeb_n INT RET # sottoprogramma di interfaccia $io_tipob_r # gate di tipo trap # sottoprogramma di interfaccia $io_tipob_w # gate di tipo trap G. Frosini – Ingresso e uscita Slide 35

PRIMITIVE DI I/O A BLOCCHI (3) # sistema. s. . TEXT. EXTERN c_readb_n a_readb_n:

PRIMITIVE DI I/O A BLOCCHI (3) # sistema. s. . TEXT. EXTERN c_readb_n a_readb_n: # routine INT $io_tipob_r # salvataggio dei registri # verifica e ricopiamento dei parametri CALL c_readb_n # ripulitura della pila # ripristino dei registri IRET. EXTERN c_writeb_n a_writeb_n: # routine INT $io_tipob_w # salvataggio dei registri # verifica e ricopiamento dei parametri CALL c_writeb_n # ripulitura della pila # ripristino dei registri IRET G. Frosini – Ingresso e uscita Slide 36

PRIMITIVE DI I/O A BLOCCHI (4) // sistema. cpp. . . void startb_in(desb_io* p_des,

PRIMITIVE DI I/O A BLOCCHI (4) // sistema. cpp. . . void startb_in(desb_io* p_des, natb vetti[], natl primo, natb quanti); extern "C" void c_readb_n(natl interf, natb vetti[], natl primo, natb quanti) { desb_io* p_des; p_des = &desintb[interf]; sem_wait(p_des -> mutex); startb_in(p_des, vetti, primo, quanti); sem_wait(p_des-> sincr); sem_signal(p_des -> mutex); } void startb_in(desb_io* p_des, natb vetti[], natl primo, natb quanti) { p_des -> cont = quanti; p_des -> punt = vetti; outputl(primo, p_des -> indreg. i. BAR); outputb(quanti, p_des -> indreg. i. SCR); gob_inout(p_des -> indreg. i. CTR); outputb(CMD_READ, p_des -> indreg. i. CMD); } G. Frosini – Ingresso e uscita Slide 37

PRIMITIVE DI I/O A BLOCCHI (5) // sistema. cpp. . . void startb_out(desb_io* p_des,

PRIMITIVE DI I/O A BLOCCHI (5) // sistema. cpp. . . void startb_out(desb_io* p_des, natb vetto[], natl primo, natb quanti); extern “C” void c_writeb_n(natl interf, natb vetto[], natl primo, natb quanti) { desb_io* p_des; p_des = &desintb[interf]; sem_wait(p_des -> mutex); startb_out(p_des, vetto, primo, quanti); sem_wait(p_des-> sincr); sem_signal(p_des -> mutex); } void startb_out(desb_io* p_des, natb vetto[], natl primo, natb quanti) { p_des -> cont = quanti; p_des -> punt = vetto + DIM_BLOCK; outputl(primo, p_des -> indreg. i. BAR); outputb(quanti, p_des -> indreg. i. SCR); gob_inout(p_des -> indreg. i. CTR); outputb(CMD_WRITE, p_des -> indreg. i. CMD); outputbb(vetto, DIM_BLOCK, p_des -> indreg. i. BR); } G. Frosini – Ingresso e uscita Slide 38

DRIVER DI I/O A BLOCCHI (1) • Interruzioni generate da un’interfaccia a blocchi: –

DRIVER DI I/O A BLOCCHI (1) • Interruzioni generate da un’interfaccia a blocchi: – driver che lavora sulle code dei processi; – interruzioni (mascherabili) disabilitate. # sistema. s. . TEXT. EXTERN int_tipob_i: c_driverb_i CALL salva_stato CALL c_driverb_i CALL invia. EOI CALL carica_stato IRET # coinvolto gate di tipo interrupt G. Frosini – Ingresso e uscita Slide 39

DRIVER DI I/O A BLOCCHI (2) // sistema. cpp. . . extern "C" void

DRIVER DI I/O A BLOCCHI (2) // sistema. cpp. . . extern "C" void c_driverb_i(void) // opera su desintb[k] { natb k, lav; // k = … proc_elem* lavoro; des_sem* s; desb_io* p_des; p_des = &desintb[k]; p_des->cont--; if (p_des->cont == 0) { haltb_inout(p_des -> indreg. i. CTR); s = &array_dess [p_des->sincr]; s->counter++; if (s->counter <= 0) { rimozione_lista(s->pointer, lavoro); inserimento_lista(pronti, lavoro); inspronti(); schedulatore(); // preemption } } inputb(p_des -> indreg. i. CMD, lav); // ack richiesta di interruzione if (lav == CMD_READ) inputbb(p_des -> indreg. i. BR, DIIM_BLOCK, static_cast<natb*>(p_des -> punt)); else if (p_des->cont != 0) outputbb(static_cast<natb*>(p_des -> punt), DIM_BLOCK, p_des -> indreg. i. BR); p_des->punt = static_cast<natb*>(p_des->punt) + DIM_BLOCK; } G. Frosini – Ingresso e uscita Slide 40

DRIVER E PROCESSI ESTERNI (1) • Driver: – può anche essere realizzato come insieme

DRIVER E PROCESSI ESTERNI (1) • Driver: – può anche essere realizzato come insieme di un handler e un di un processo esterno (o processo driver); – quando arriva un’interruzione esterna va in esecuzione uno specifico handler, il quale esegue una commutazione di contesto, inserendo il processo interrotto nella lista dei processi pronti e mandando in esecuzione (forzatamente) il processo esterno che gestisce i trasferimenti richiesti; • il processo interrotto, quello in esecuzione, avrà la precedenza più alta; – si hanno i processi interni o processi utente e i processi esterni associati ai descrittori di I/O, che hanno livello di privilegio maggiore di quello dei processi utente. • Handler: – routine di nucleo che gira con le interruzioni (mascherabili) disabilitate, in quanto opera sulle code dei processi. • Processo esterno: – gira invece con le interruzioni abilitate (come qualunque processo). G. Frosini – Ingresso e uscita Slide 41

DRIVER E PROCESSI ESTERNI (2) • Realizzazione di un driver come handler + processo

DRIVER E PROCESSI ESTERNI (2) • Realizzazione di un driver come handler + processo esterno: – particolarmente utile quando il driver risulta lungo; – il codice di un processo esterno può far liberamente uso delle primitive di nucleo, cosa che non può fare un driver; – un handler coinvolge gate di tipo interrupt, che producono automaticamente la disabilitazione delle interruzioni (mascherabili); – le interruzioni (mascherabili) vengono riabilitate con la messa in esecuzione di un processo esterno (nuovo valore nel registro EFLAG). • Processo esterno: – attivato per effetto di un’interruzione (esterna); – gira con le interruzioni abilitate; • annidamento delle interruzioni; – alla termine dell’esecuzione, deve inviare il controllore di interruzione la configurazione EOI (End Of Interrupt). G. Frosini – Ingresso e uscita Slide 42

LIVELLO DI PRIVILEGIO I/O • Livello di privilegio I/O: – i processi esterni hanno

LIVELLO DI PRIVILEGIO I/O • Livello di privilegio I/O: – i processi esterni hanno lo stesso livello di privilegio delle istruzioni di I/O (il livello I/O è determinato dal campo IOPL del registro EFLAG); – nel livello I/O sono anche posizionati i descrittori di I/O e le primitive di I/O. • Due file, io. s e io. cpp: – contengono le strutture dati e le routine di ingresso/uscita; – costituiscono un modulo software, che non deve essere collegato né col modulo utente né col modulo sistema; – livello di privilegio del modulo i/O: sistema. • Posizionamento degli handler: – modulo sistema. G. Frosini – Ingresso e uscita Slide 43

ATTIVAZIONE DEI PROCESSI ESTERNI • Processi esterni: – individuati da identificatori che, tramite la

ATTIVAZIONE DEI PROCESSI ESTERNI • Processi esterni: – individuati da identificatori che, tramite la GDT, conducono ai loro descrittori; – attivati, in fase di inizializzazione, con una primitiva activate_pe(), analoga a quella vista per i processi utente (ir è il numero d’ordine del piedino di ingresso del controllore di interruzione a cui e collegato il piedino dell’interfaccia tramite cui avvengono le richieste di interruzione associate al processo esterno da attivare): natl activate_pe(void f(int), int a, natl prio, nal liv, natb ir); – tale primitiva inserisce il processo in una apposita lista dei processi bloccati in attesa di un determinato tipo di interruzione, lista costituita da un solo elemento di tipo proc_elem , individuata da un apposito puntatore; – ci sarà un array di P puntatori a_p[P] (P è il numero di piedini del controllore di interruzione), un puntatore a_p[i] per ogni processo esterno. G. Frosini – Ingresso e uscita Slide 44

STRUTTURA DI UN HANDLER • Struttura di un handler: – viene attivato da un’interruzione

STRUTTURA DI UN HANDLER • Struttura di un handler: – viene attivato da un’interruzione esterna e manda in esecuzione un processo esterno. # sistema. s. DATA … a_p. SPACE . TEXT …. EXTERN inspronti handler_i: CALL MOVL CALL IRET • P*4 salva_stato inspronti $i, %ECX a_p(, %ECX, 4), %EAX, esecuzione carica_stato # vi sono al più P handler (i va da 0 a P-1) # i ha uno specifico valore per ogni handler # mette nel puntatore esecuzione # il contenuto del puntatore a_p[i] associato all’handler Meccanismo di interruzione vettorizzato: – esistono al più tanti handler quanti sono i piedini del controllore di interruzione a cui sono collegate le interfacce di ingresso/uscita, e corrispondentemente un ugual numero di processi esterni. G. Frosini – Ingresso e uscita Slide 45

UTILIZZO DELLE SEMAFORICHE • Processi esterni: – possono utilizzare le primitive di nucleo, in

UTILIZZO DELLE SEMAFORICHE • Processi esterni: – possono utilizzare le primitive di nucleo, in particolare le primitive sem_wait() e sem_signal(): // io. cpp extern "C" natl sem_ini(int val); extern "C" void sem_wait(natl sem); extern "C" void sem_signal(natl sem); # io. s. TEXT. GLOBAL sem_ini: rit_si: . GLOBAL sem_wait: rit_w: . GLOBAL sem_signal: rit_s: sem_ini INT $tipo_si RET sem_wait INT $tipo_w RET sem_signal INT $tipo_s RET # sottoprogramma di interfaccia G. Frosini – Ingresso e uscita Slide 46

PRIMITIVA WFI() • Processo esterno: – per il trasferimento di n byte va in

PRIMITIVA WFI() • Processo esterno: – per il trasferimento di n byte va in esecuzione n volte; – l’ultima di queste volte, pone nella lista dei processi pronti il processo utente che si era bloccato in attesa del termine dell’operazione; – alla fine di ogni esecuzione invia al controllore di interruzione la configurazione EOI, e si blocca in attesa della prossima interruzione. • Azioni di un processo esterno alla fine di ogni esecuzione: primitiva di nucleo wfi() (Wait For Interrupt). – salvataggio dello stato attuale; – invio della configurazione EOI; – richiamo dello schedulatore (esso seleziona il processo interrotto nelle esecuzioni intermedie, quello interrotto o quello divenuto pronto nell’esecuzione finale); – caricamento dello stato del nuovo processo. G. Frosini – Ingresso e uscita Slide 47

STRUTTURA DI UN PROCESSO ESTERNO (1) • Stuttura di un processo esterno: // io.

STRUTTURA DI UN PROCESSO ESTERNO (1) • Stuttura di un processo esterno: // io. cpp extern "C" void wfi(); void estern(int h) {. . . for(; ; ) {. . . wfi(); } } // codice di un processo esterno – un processo esterno si blocca utilizzando la primitiva wfi() (che salva lo stato del processo relativo alla istruzione successiva alla INT presente nel sottoprogramma di interfaccia della primitiva wfi()): – – • si blocca ogni volta che si sono concluse le azioni relative a un singolo trasferimento; una nuova esecuzione forzata del processo (che avviene per effetto dello stesso handler) lo fa ripartire dalla istruzione RET del sottoprogramma di interfaccia, che produce il ritorno al punto successivo alla chiamata del sottoprogramma wfi(): • tale punto (parentesi graffa chiusa) produce un salto all’inizio del for. un processo esterno ha quindi una struttura ciclica. G. Frosini – Ingresso e uscita Slide 48

STRUTTURA DI UN PROCESSO ESTERNO (2) • Nota: – mentre è in esecuzione un

STRUTTURA DI UN PROCESSO ESTERNO (2) • Nota: – mentre è in esecuzione un processo esterno, si può avere una interruzione a precedenza maggiore di quella sotto servizio; – lo handler che va in esecuzione pone il processo interrotto nella lista dei processi pronti; – nella lista dei processi pronti, pertanto, sono presenti anche processi esterni, nonostante che questi, quando si bloccano, non effettuino esplicitamente l’inserimento in alcuna lista. • Descrizione delle operazioni di I/O facendo uso dei processi esterni: – utilizzo delle stesse strutture dati e degli stessi sottoprogrammi di utilità utilizzati facendo uso dei driver. G. Frosini – Ingresso e uscita Slide 49

PRIMITIVA WFI() (1) • Primitiva wfi(): – come tutte le altre primitive fa parte

PRIMITIVA WFI() (1) • Primitiva wfi(): – come tutte le altre primitive fa parte del nucleo; – il sottoprogramma di interfaccia richiama la primitiva vera e propria (a_wfi) – questa effettua il salvataggio dello stato del processo esterno che la invoca, invia poi la configurazione EOI, richiama lo schedulatore e carica lo stato del nuovo processo. # io. s. TEXT. GLOBAL wfi: rit_wfi: wfi INT RET $tipo_wfi G. Frosini – Ingresso e uscita Slide 50

PRIMITIVA WFI() (2). TEXT. EXTERN a_wfi: schedulatore # routine INT $tipo_wfi CALL salva_stato //

PRIMITIVA WFI() (2). TEXT. EXTERN a_wfi: schedulatore # routine INT $tipo_wfi CALL salva_stato // non avviene l’inserimento in lista pronti CALL invia. EOI CALL schedulatore CALL carica_stato IRET • Chiamata del sottoprogramma invia. EOI: – deve far parte della primitiva wfi(); – diversamente, dopo l’invio di EOI e prima della primitiva wfi(), potrebbe giungere al processore una nuova richiesta di interruzione dalla stessa fonte: • ricordare che non si possono avere più istanze aperte dello stesso processo. G. Frosini – Ingresso e uscita Slide 51

ESECUZIONE DI UN PROCESSO ESTERNO • Handler: – manda in esecuzione un processo esterno

ESECUZIONE DI UN PROCESSO ESTERNO • Handler: – manda in esecuzione un processo esterno eseguendo una istruzione IRET: • in seguito alle azioni dello handler: MOVL a_p(, %ECX, 4), %EAX # mette nel puntatore esecuzione MOVL CALL %EAX, esecuzione # il contenuto del puntatore a_p[i] associato all’handler carica_stato tale istruzione agisce sulla pila sistema del processo esterno individuato da esecuzione. • Prima volta: – la pila sistema del processo esterno è stata predisposta dalla primitiva activate_pe(); • questa ha inserito in pila il valore di EIP relativo al punto iniziale del codice del processo esterno. • Altre volte: – la pila sistema del processo esterno è stata predisposta di nuovo dalla precedente esecuzione del processo esterno stesso, precisamente dalla istruzione INT del sottoprogramma di interfaccia della primitiva wfi(). G. Frosini – Ingresso e uscita Slide 52

INTERRUZIONE DI UN PROCESSO ESTERNO • Processo esterno: – viene eseguito con le interruzioni

INTERRUZIONE DI UN PROCESSO ESTERNO • Processo esterno: – viene eseguito con le interruzioni (mascherabili) abilitate. • Interruzione esterna: – manda in esecuzione un driver (o un handler), col cambio di livello di privilegio da io a sistema, che richiama il sottoprogramma salva_stato; – – • nella pila sistema del processo esterno interrotto vengono immesse nuove informazioni, con un valore di EIP relativo alla istruzione che segue il punto di interruzione; • nel descrittore del processo esterno interrotto viene memorizzato (tra l’altro) un ESP relativo alla cima di tale pila sistema; senza una successiva predisposizione della pila sistema e del descrittore del processo esterno, una nuova esecuzione del processo esterno stesso (prodotta dall’handler) lo farebbe ripartire dalla istruzione che segue il punto di interruzione; quindi, il processo esterno deve fare in modo tale che una sua nuova esecuzione (prodotta dall’handler) faccia eseguire il codice del processo esterno stesso in modo che avvenga un nuovo trasferimento: • questo si ottiene con una primitiva (wfi()) e una struttura ciclica. G. Frosini – Ingresso e uscita Slide 53

HANDLER PER INGRESSO DATI • Interruzione causata da un’interfaccia di ingresso: – va in

HANDLER PER INGRESSO DATI • Interruzione causata da un’interfaccia di ingresso: – va in esecuzione uno specifico handler; – questo manda forzatamente in esecuzione un ben determinato processo esterno. • # sistema. s. TEXT. EXTERN handleri_j: inspronti CALL MOVL CALL IRET salva_stato inspronti $i, %ECX a_p(, %ECX, 4), %EAX # mette nel puntatore esecuzione %EAX, esecuzione # il contenuto del puntatore # associato all’handler carica_stato G. Frosini – Ingresso e uscita Slide 54

PROCESSI ESTERNI DI INGRESSO • Interfacce a byte uguali fra loro: – tutti i

PROCESSI ESTERNI DI INGRESSO • Interfacce a byte uguali fra loro: – tutti i processi esterni di ingresso compiono le stesse elaborazioni; – i corpi di tutti i processi esterni di ingresso possono essere costituiti da istanze della stessa funzione; – il parametro attuale che discrimina la specifica istanza è stato messo nella pila di livello I/O del processo esterno dalla activate_pe(). // io. cpp void esterni(int h) { natb a ; des_io* p_desi ; p_desi = & desinti[h]; for(; ; ) { p_desi-> cont--; if (p_desi ->cont == 0) halt_input(p_desi -> indreg. ctr_io. i. CTRI); inputb(p_desi -> indreg. in_out. i. RBR, a); // prelievo *static_cast<natb*>(p_desi-> punt) = a; // memorizzazione p_desi->punt = static_cast<natb*>(p_desi-> punt) + 1; if (p_desi->cont == 0) sem_signal(p_desi->sincr); wfi(); } } G. Frosini – Ingresso e uscita Slide 55

PROCESSI ESTERNI DI USCITA • Interfacce a byte uguali fra loro: – tutti i

PROCESSI ESTERNI DI USCITA • Interfacce a byte uguali fra loro: – tutti i processi esterni di uscita compiono le stesse elaborazioni; – i corpi di tutti i processi esterni di uscita possono essere costituiti da istanze della stessa funzione, e il parametro attuale discrimina la specifica istanza. // io. cpp void esterno(int h) { natb a ; des_io* p_deso ; p_deso = & desinto[h]; for (; ; ) { p_deso -> cont--; if (p_deso ->cont == 0) halt_output(p_deso -> indreg. ctr_io. i. CTRO); a = *static_cast<natb*>(p_deso-> punt); // caricamento outputb(a, p_deso -> indreg. in_out. i. TBR); // invio p_deso -> punt = static_cast<natb*>(p_deso-> punt) + 1; if (p_deso -> cont == 0) sem_signal(p_deso -> sincr); wfi(); } } G. Frosini – Ingresso e uscita Slide 56

PROCESSI ESTERNI PER INTERFACCE A BLOCCHI • Interfacce a blocchi uguali fra loro: –

PROCESSI ESTERNI PER INTERFACCE A BLOCCHI • Interfacce a blocchi uguali fra loro: – tutti i processi esterni compiono le stesse elaborazioni; – i corpi di tutti i processi esterni sono costituiti da istanze della stessa funzione. // io. cpp void esternb(int h) { natb lav; desb_io* p_des = &desintb[h]; for(; ; ) { p_des->cont--; if (p_des->cont == 0) haltb_inout(p_des -> indreg. i. CTR); inputb(p_des -> indreg. i. CMD, lav); // ack richiesta di interruzione if (lav == CMD_READ) inputbb(p_des -> indreg. i. BR, DIIM_BLOCK, static_cast<natb*>(p_des -> punt)); else if (p_des->cont != 0) outputbb(static_cast<natb*>(p_des -> punt), DIM_BLOCK, p_des -> indreg. i. BR); p_des->punt = static_cast<natb*>(p_des->punt) + DIM_BLOCK; if (p_des->cont == 0) sem_signal(p_des->sincr); wfi(); } } G. Frosini – Ingresso e uscita Slide 57

ACCESSO DIRETTO ALLA MEMORIA • Accesso diretto alla memoria (DMA): – realizzato dalle funzioni

ACCESSO DIRETTO ALLA MEMORIA • Accesso diretto alla memoria (DMA): – realizzato dalle funzioni PCI utilizzando il meccanismo di bus mastering. • Funzione PCI predisposta per operare in DMA: – deve essere esplicitamente abilitata, scrivendo una opportuna quantità nel Command Register (di 16 bit, spazio di configurazione, offset 4). • Funzione PCI abilitata a funzionare in DMA: – gestisce un blocco dello spazio di IO; – il blocco è costituito da 3 registri (di 32 bit): • BMCMD (scrittura): avvia un trasferimento e specifica il suo verso (dalla funzione PCI alla memoria o viceversa); • BMSTR (lettura): contiene informazioni di stato alla fine di un’operazione di bus mastering; • BMDTPR (scrittura): memorizza l’indirizzo fisico della cosiddetta tabella dei descrittori di buffer. G. Frosini – Ingresso e uscita Slide 58

FUNZIONE PCI CHE OPERA IN DMA G. Frosini – Ingresso e uscita Slide 59

FUNZIONE PCI CHE OPERA IN DMA G. Frosini – Ingresso e uscita Slide 59

DESCRITTORE DI BUFFER • Descrittore di buffer (PRD: Phisical Region Descriptor): – due parole

DESCRITTORE DI BUFFER • Descrittore di buffer (PRD: Phisical Region Descriptor): – due parole lunghe: • indirizzo fisico di un buffer di memoria; • lunghezza del buffer (16 bit meno significativi della parola lunga, che indicano il numero di byte) ) e un bit (il più significativo della parola lunga, detto flag EOT) che specifica se il descrittore è o meno l’ultimo. G. Frosini – Ingresso e uscita Slide 60

AZIONI DELLA FUNZIONE PCI (1) • Funzione PCI: – gestisce una specifica interfaccia; –

AZIONI DELLA FUNZIONE PCI (1) • Funzione PCI: – gestisce una specifica interfaccia; – registro buffer di ingresso e registro buffer di uscita dell’interfaccia: • supponiamo che siano di 32 bit. • Trasferimento dei dati: • avviene con transazioni PCI tra la funzione PCI e il ponte Ospite-PCI; • il ponte trasforma ogni transazione in cicli (sul bus locale) di lettura o di scrittura con la memoria. • Operazione preliminare effettuata dalla funzione PCI: – all’avvio del trasferimento, preleva dalla memoria il descrittore di buffer puntato da BMDTPR, memorizza l’indirizzo, il numero di byte e il flag in esso contenuti in registri interni BUFF, DIM ed EOT. G. Frosini – Ingresso e uscita Slide 61

AZIONI DELLA FUNZIONE PCI (2) • Operazioni cicliche effettuate dalla funzione PCI: 1. quando

AZIONI DELLA FUNZIONE PCI (2) • Operazioni cicliche effettuate dalla funzione PCI: 1. quando si rendono disponibili dei dati da trasferire, siano N (N, multiplo di 4, viene memorizzato in un registro interno), essa effettua una transazione PCI di scrittura in memoria, all'indirizzo specificato da BUF; 2. ciclicamente, in ogni fase dati della transazione trasferisce 4 byte, decrementando N e DIM di 4 e incrementando BUF di 4 (per predisporre la fase di indirizzamento della eventuale transazione successiva); quando almeno uno tra N e DIM diventa 0, fa terminare la transazione PCI e passa al punto successivo. 3. se DIM è diverso da zero torna al punto 1. , altrimenti esamina il valore di EOT: • • se EOT vale 0, i) incrementa BMDTPR di 8, ii) preleva dalla memoria un nuovo descrittore di buffer, memorizzando nuovi valori in BUF, DIM e EOT, e iii) torna al punto 1. se EOT vale 1, i) scrive nel registro BMSTR e ii) invia una richiesta di interruzione. G. Frosini – Ingresso e uscita Slide 62

SPAZIO VIRTUALE E SPAZIO FISICO • Presenza, nel registro BMDTPR e in un descrittore

SPAZIO VIRTUALE E SPAZIO FISICO • Presenza, nel registro BMDTPR e in un descrittore di buffer, di un indirizzo fisico: – consente di non dover effettuare, nella funzione PCI, la traduzione da indirizzo virtuale a indirizzo fisico, come invece avviene nel processore ad opera della MMU. • Osservazione: – all’interno di una pagina, la contiguità nello spazio virtuale si mantiene anche nello spazio fisico; – la contiguità di pagine virtuali non comporta necessariamente la contiguità delle corrispondenti pagine fisiche. • Condizione sufficiente: – i descrittori di buffer devono essere contenuti in un’unica pagina di memoria; – ogni buffer deve essere contenuto in un’unica pagina di memoria; • nel caso di un numero di byte da trasferire superiore alle dimensioni di una pagina, si deve utilizzare una tabella dei descrittori di buffer costituita da più descrittori. • Ipotesi semplificativa: – un solo descrittore di buffer (e un solo buffer). G. Frosini – Ingresso e uscita Slide 63

DESCRITTORI DMA • Assunzioni: – le interfacce gestite da funzioni PCI operanti in DMA

DESCRITTORI DMA • Assunzioni: – le interfacce gestite da funzioni PCI operanti in DMA sono M; – ciascuna interfaccia può effettuare (in tempi diversi) operazioni di ingresso o di uscita; – un descrittore DMA per ogni funzione PCI (il registro BMCMD è unico per l’ingresso e per l’uscita). • Tipo dmadescrittore e array di M descrittori: io. cpp struct dmadescrittore { ioaddr i. BMCMD, i. BMSTR, i. BMDTPR; natl mutex, sincr; natl prd[2]; natl stato; }; extern dmadescrittore dmades[M]; # io. s. ALIGN 4096 . DATA. GLOBAL dmades: . SPACE DMA_BYTE # allineamento a 2**12 # M * sizeof(dmadescrittore) # tutti i descrittori devono essere contenuti in una pagina G. Frosini – Ingresso e uscita Slide 64

INIZIALIZZAZIONE DEI DESCRITTORI • Inizializzazione di un descrittore DMA: – inizializzazione dei campi i.

INIZIALIZZAZIONE DEI DESCRITTORI • Inizializzazione di un descrittore DMA: – inizializzazione dei campi i. BMCMD, i. BMSTR, i. BMDTPR, per mezzo di una lettura del registro base utilizzato dalla funzione PCI (nello spazio di configurazione); – inizializzazione del registro BMDTPR, con l’indirizzo di memoria dell’array prd contenuto nel descrittore DMA stesso; – – • • tale indirizzo deve essere un indirizzo fisico; inizializzazione del campo mutex, facendo uso della primitiva sem_ini(), che pone a 1 il contatore del semaforo coinvolto; inizializzazione del campo sincr, facendo uso della primitiva sem_ini(), che pone a 0 il contatore del semaforo coinvolto. Ipotesi: – tutti i descrittori DMA stanno in una pagina: • l’array prd di ogni descrittore DMA si trova certamente all’interno di una pagina. G. Frosini – Ingresso e uscita Slide 65

PRIMITIVE DI I/O IN DMA • Operazione in DMA: – effettuata da una primitiva

PRIMITIVE DI I/O IN DMA • Operazione in DMA: – effettuata da una primitiva di I/O, che specifica: • • i) l’interfaccia coinvolta; ii) l’indirizzo (virtuale) del buffer di memoria utilizzato; iii) il numero di parole lunghe da trasferire; iv) l’indirizzo di una variabile nella quale deve essere lasciata un’informazione di stato riguardante l’operazione effettuata. • Trasformazione di un indirizzo da virtuale a fisico: – richiede l’accesso alle tabelle di corrispondenza (operazione che non può essere fatta da livello I/O); – effettuata attraverso una primitiva di nucleo trasforma(): addr trasforma(addr ind_virtuale); G. Frosini – Ingresso e uscita Slide 66

PRIMITIVA DI NUCLEO trasforma() // io. cpp extern "C" addr trasforma(addr ind_virtuale); # io.

PRIMITIVA DI NUCLEO trasforma() // io. cpp extern "C" addr trasforma(addr ind_virtuale); # io. s. TEXT. GLOBAL trasforma: rit_tra: # sistema. s. TEXT a_trasforma: trasforma INT $tipo_tra RET # sottoprogramma di interfaccia # gate di tipi interrupt # routine INT $ tipo_tra # salvataggio dei registri # … # ripristino dei registri IRET G. Frosini – Ingresso e uscita Slide 67

BUFFER DI MEMORIA (1) • Primitiva che effettua un’operazione in DMA: – prevede che

BUFFER DI MEMORIA (1) • Primitiva che effettua un’operazione in DMA: – prevede che il processo che la invoca, sia P 1, si blocchi su un semaforo di sincronizzazione; – esecuzione di un altro processo, sia P 2. • Buffer di memoria coinvolto dalla primitiva, nel quale vengono immesse le parole lunghe o dal quale vengono prelevate le parole lunghe dalla funzione PCI che opera in DMA: – non deve necessariamente risiedere nello spazio di indirizzamento comune; – il meccanismo DMA opera su indirizzi fisici (e non virtuali); – gli indirizzi fisici non variano col variare del processo in esecuzione; • non ha quindi rilevanza che lo spazio di indirizzamento virtuale privato di P 1 non sia accessibile mentre è in esecuzione P 2. • Buffer di memoria: – gli indirizzi fisici non sono mai in grado di dar luogo a page-fault; – il buffer, quindi, non può essere soggetto a rimpiazzamento (deve essere residente in memoria centrale quando è in esecuzione P 2). • Memoria cache: – supponiamo che, quando avviene un accesso diretto alla memoria centrale, sia completamente gestita dal controllore di memoria cache. G. Frosini – Ingresso e uscita Slide 68

BUFFER DI MEMORIA (2) • Buffer nelle operazioni DMA: – non deve essere all’interno

BUFFER DI MEMORIA (2) • Buffer nelle operazioni DMA: – non deve essere all’interno di una pagina virtuale; – soluzione sicura: allineato alla pagina, e con dimensione inferiore alla pagina; – definito in Assembler, nella sezione dati (parte utente comune); • Ipotesi di lavoro (semplificata): – parti comuni non rimpiazzabili; – buffer automaticamente residente. • Se l’ipotesi precedente non fosse valida, occorrerebbe una primitiva di nucleo resident(). G. Frosini – Ingresso e uscita Slide 69

SOTTOPROGRAMMI DI UTILITA’ (1) # io. s. TEXT. GLOBAL BMCMD dmago_in: RET. GLOBAL BMCMD

SOTTOPROGRAMMI DI UTILITA’ (1) # io. s. TEXT. GLOBAL BMCMD dmago_in: RET. GLOBAL BMCMD dmago_out: #. . . RET. GLOBAL leggi_stato: #. . . RET dmago_in # scrittura in #. . . dmago_out # scrittura in leggi_stato # lettura di BMSTS NOTA: La scrittura in BMDTPR (dell’indirizzo fisico corrispondente all’indirizzo virtuale PRD contenuto nel desrittore DMA ) viene fatta in fase di inizializzazione. G. Frosini – Ingresso e uscita Slide 70

SOTTOPROGRAMMI DI UTILITA’ (2) // io. cpp extern "C " void dmago_in(dmadescrittore* p_dmades); //

SOTTOPROGRAMMI DI UTILITA’ (2) // io. cpp extern "C " void dmago_in(dmadescrittore* p_dmades); // *(p_dmades-> i. BMCMD) = LAV 0; extern " C " void dmago_out(dmadescrittore* p_dmades); // *(p_dmades-> i. BMCMD) = LAV 1; extern " C " natl leggi_stato(dmadescrittore* p_dmades); // return *(p_dmades->i. BMSTS) ; void componi_prd(dmadescrittore* p_dmades, addr iff, natw quanti) { p_dmades->prd[0] = reinterpret_cast<natl>(iff); // prd e’ un array di 2 natl p_dmades->prd[1] = 0 x 80000000 | quanti; // EOT posto a 1 } G. Frosini – Ingresso e uscita Slide 71

LETTURA IN DMA (1) // utente. cpp extern natl buff_rr[]; // globale perché definito

LETTURA IN DMA (1) // utente. cpp extern natl buff_rr[]; // globale perché definito in Assembler // definito in Assembler per l’allineamento extern "C " bool resident(addr ind_virt, natl quanti); extern "C" void dmaleggi(natl dmainterf, natl vetti[], natw quante, natl& st); // quante è il numero di parole lunghe void proc_dma(int h) // codice di un processo utente { natl d_interf; natw nn; natl risu; // bool ris = resident(buff_rr, 4096); se lo spazio comune fosse soggetto a rimpiazzamento. . . dmaleggi(d_interf, buff_rr, nn, risu); … } G. Frosini – Ingresso e uscita Slide 72

LETTURA IN DMA (2) # utente. s. DATA. ALIGN 4096. GLOBAL buf_rr: . SPACE.

LETTURA IN DMA (2) # utente. s. DATA. ALIGN 4096. GLOBAL buf_rr: . SPACE. TEXT. GLOBAL dmaleggi: INT rit_dr: RET # allineamento a 2**12 4096 $dma_tipo_r # sottoprogramma di interfaccia # gate di tipo trap # io. s. TEXT. EXTERN c_dmaleggi a_dmaleggi: # routine INT $dma_tipo_r # salvataggio dei registri # verifica e ricopiamento dei parametri CALL c_dmaleggi # ripulitura della pila # ripristino dei registri IRET G. Frosini – Ingresso e uscita Slide 73

LETTURA IN DMA (3) // io. cpp void dmastart_in(dmadescrittore* p_dmades, natl vetti[], natw quante);

LETTURA IN DMA (3) // io. cpp void dmastart_in(dmadescrittore* p_dmades, natl vetti[], natw quante); extern "C" void c_dmaleggi(natl dmainterf, natl vetti[], natw quante, natl& st) { dmadescrittore* p_dmades; p_dmades = &dmades[dmainterf]; sem_wait(p_dmades -> mutex); dmastart_in(p_dmades, vetti, quante); sem_wait(p_dmades -> sincr); st = p_dmades->stato; sem_signal(p_dmades -> mutex); } extern "C " addr trasforma(addr ind_virtuale); void dmastart_in(dmadescrittore* p_dmades, natl vetti[], natw quante) { addr iff = trasforma(vetti); componi_prd(p_dmades, iff, quante*4); dmago_in(p_dmades); // scrittura in BMCMD } G. Frosini – Ingresso e uscita Slide 74

SCRITTURA IN DMA • Funzione c_dmascrivi: // io. cpp dmastart_out(dmadescrittore* p_dmades, natl vetto[], natw

SCRITTURA IN DMA • Funzione c_dmascrivi: // io. cpp dmastart_out(dmadescrittore* p_dmades, natl vetto[], natw quante); void c_dmascrivi(natl dmainterf , natl vetto[], natw quante, natl& st) { dmadescrittore* p_dmades; p_dmades = &dmades[dmainterf]; sem_wait(p_dmades -> mutex); dmastart_out(p_dmades, vetto, quante); sem_wait(p_dmades->sincr); st = p_dmades->stato; sem_signal(p_dmades -> mutex); } extern "C" addr trasforma(addr ind_virtuale); dmastart_out(dmadescrittore* p_dmades, natl vetto[], natw quanti) { addr iff = trasforma(vetto); componi_prd(p_dmades, iff, quante*4); dmago_out(p_dmades); //scrittura in BMCMD } G. Frosini – Ingresso e uscita Slide 75

PROCESSO ESTERNO DMA • A fine operazione, la funzione PCI genera una richiesta di

PROCESSO ESTERNO DMA • A fine operazione, la funzione PCI genera una richiesta di interruzione (unica per l’ingresso e per l’uscita). – va in esecuzione uno specifico handler, e quindi (tramite questo) un processo esterno. • Codice comune a tutti i processi esterni per fine operazione DMA: // io. cpp void dmaestern(int h) // codice comune a tutti i processi esterni in DMA { dmadescrittore* p_dmades; p_dmades = &dmades[h]; for (; ; ) { p_dmades->stato = leggi_stato(p_dmades); // lettura dello stato, risposta alla richiesta di interruzione sem_signal(p_dmades -> sincr); wfi(); } } G. Frosini – Ingresso e uscita Slide 76

INTERFACCE A BLOCCHI IN DMA • Funzione PCI: – gestisce sia l’interfaccia a blocchi

INTERFACCE A BLOCCHI IN DMA • Funzione PCI: – gestisce sia l’interfaccia a blocchi (blocco di I/O di 8 byte), sia il bus mastering (blocco di I/O di 12 byte) G. Frosini – Ingresso e uscita Slide 77

REGISTRI DELL’INTERFACCIA A BLOCCHI • Registri BR, CTR, SCR e CMD dell’interfaccia: – non

REGISTRI DELL’INTERFACCIA A BLOCCHI • Registri BR, CTR, SCR e CMD dell’interfaccia: – non vengono gestiti dal programmatore, – la funzione PCI gestisce in proprio tali registri: • i dati da trasferire o trasferiti in DMA (32 bit) vengono automaticamente prelevati o immessi (byte a byte) nel registro BR; • il numero di settori coinvolti nell’operazione è determinato dal numero di byte da trasferire contenuto nel descrittore di buffer (per semplicità un solo descrittore); • il verso del trasferimento viene specificato dal registro BMCMD; • l’interruzione viene generata automaticamente a fine operazione. • Registro BAR dell’interfaccia: – viene gestito dal programmatore, scrivendovi l’indirizzo (sul dispositivo) del primo blocco interessato al trasferimento. G. Frosini – Ingresso e uscita Slide 78

DESCRITTORI DI INTERFACCE A BLOCCHI IN DMA // io. cpp. . . struct interfreg

DESCRITTORI DI INTERFACCE A BLOCCHI IN DMA // io. cpp. . . struct interfreg { ioaddr i. BAR; }; struct dmadescrittoreb { dmareg idma; interfreg iinterf; natl prd[2]; natl mutex; natl sincr; natl stato; }; extern dmadescrittoreb dmadesb[DD]; // array definito nel file io. s • I descrittori vengono inizializzati prima di essere utilizzati. • Nota: – i descrittori DMA sono tanti quante le funzioni PCI che gestiscono un’interfaccia a blocchi G. Frosini – Ingresso e uscita Slide 79

LETTURA A BLOCCHI IN DMA (1) // utente. cpp. . . extern natl buffb_rr[];

LETTURA A BLOCCHI IN DMA (1) // utente. cpp. . . extern natl buffb_rr[]; //definito in Assembler per l’allineamento extern "C" void dmaleggib(natl dmainterfb, natl vetti[], natl primo, natb quanti, natl& st); // quanti e’ il numero di blocchi … void proc_dmab(int h) // codice di un processo utente { natl d_interfb; natl inizio; natb nn; natl risu; … // bool ris = resident(buffd_rr, 4096); se lo spazio comune fosse soggetto a rimpiazzamento dmaleggib(d_interfb, buffb_rr, inizio, nn, risu); } # utente. s. DATA. ALIGN . GLOBAL buffb_rr: 4096 buffb_rr. space . TEXT. GLOBAL dmaleggib: rit_dbr: dmaleggib INT RET # allineamento a 2**12 4096 $dma_tipob_r # sottoprogramma di interfaccia # gate di tipo trap G. Frosini – Ingresso e uscita Slide 80

LETTURA A BLOCCHI IN DMA (2) # io. s …. EXTERN a_dmaleggib: c_dmaleggib #

LETTURA A BLOCCHI IN DMA (2) # io. s …. EXTERN a_dmaleggib: c_dmaleggib # routine INT $dma_tipob_r # salvataggio dei registri # verifica e ricopiamento dei parametri CALL c_dmaleggib # ripulitura della pila # ripristino dei registri IRET G. Frosini – Ingresso e uscita Slide 81

LETTURA A BLOCCHI IN DMA (3) // io. cpp … extern "C" addr trasforma(addr

LETTURA A BLOCCHI IN DMA (3) // io. cpp … extern "C" addr trasforma(addr ind_virtuale); void dmastartb_in(dmadescrittoreb* p_dmadesb, natl vetti[], natl primo, natw qq) { outputl(primo, p_dmadesb->iinterf. i. BAR); addr iff = trasforma(vetti); componi_prd(p_dmadesb, iff, qq); dmagob_in(p_dmadesb); // scrittura in BMCMD } extern "C" void c_dmaleggib(natl dmainterfb, natl vetti[], natl primo, natb quanti, natl& st) { dmadescrittoreb* p_dmadesb; p_dmadesb = &dmadesb[dmainterfb]; sem_wait(p_dmadesb -> mutex); dmastartb_in(p_dmadesb, vetti, primo, quanti*DIM_BLOCK); // quanti (natb: numero di settori) trasformato in numero di byte (natw) sem_wait(p_dmadesb -> sincr); st = p_dmadesb->stato; sem_signal(p_dmadesb -> mutex); } G. Frosini – Ingresso e uscita Slide 82

PROCESSO ESTERNO DMA A BLOCCHI • A fine operazione, la funzione PCI genera una

PROCESSO ESTERNO DMA A BLOCCHI • A fine operazione, la funzione PCI genera una richiesta di interruzione (unica per l’ingresso e per l’uscita). – va in esecuzione uno specifico handler, e quindi (tramite questo) un processo esterno. • Codice comune a tutti i processi esterni per fine operazione DMA a blocchi: // io. cpp void dmaesternb(int h) // codice comune a tutti i processi esterni in DMA { dmadescrittoreb* p_dmadesb; p_dmadesb = &dmadesb[h]; for (; ; ) { p_dmadesb->stato = leggi_stato(p_dmadesb); // lettura dello stato, risposta alla richiesta di interruzione sem_signal(p_dmadesb -> sincr); wfi(); } } G. Frosini – Ingresso e uscita Slide 83