La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

G. Frosini – Ingresso e uscita Slide 1

Presentazioni simili


Presentazione sul tema: "G. Frosini – Ingresso e uscita Slide 1"— Transcript della presentazione:

1 G. Frosini – Ingresso e uscita Slide 1

2 G. Frosini – Ingresso e uscita Slide 2
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

3 G. Frosini – Ingresso e uscita Slide 3
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

4 G. Frosini – Ingresso e uscita Slide 4
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

5 G. Frosini – Ingresso e uscita Slide 5
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 # sottoprogramma di interfaccia prim_io_i: INT $io_tipo_i # gate di tipo trap rit_io_i: RET G. Frosini – Ingresso e uscita Slide 5

6 G. Frosini – Ingresso e uscita Slide 6
PRIMITIVE DI I/O (5) Struttura della primitiva vera e propria (routine a_prim_io_i) : # sistema.s .TEXT .EXTERN c_prim_io_i a_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

7 G. Frosini – Ingresso e uscita Slide 7
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 sem_ini: INT $tipo_si rit_si: RET .GLOBAL sem_wait sem_wait: INT $tipo_w rit_w: RET .GLOBAL sem_signal sem_signal: INT $tipo_s rit_s: RET G. Frosini – Ingresso e uscita Slide 7

8 G. Frosini – Ingresso e uscita Slide 8
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

9 DRIVER DI INTERRUZIONE (1)
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

10 DRIVER DI INTERUZIONE (2)
Processo utente P1: l’operazione di wait blocca P1 su un semaforo di sincronizzazione; lo schedulatore manda in esecuzione un nuovo processo utente P2; il driver interrompe P2 (o il processo in esecuzione in quel momento) e, a fine operazione, fa divenire di nuovo pronto P1. Buffer di memoria specificato da P1: deve risiedere nello spazio comune, dovendo essere utilizzato dal driver quando P1 è bloccato ed è in esecuzione P2. 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

11 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 in uscita. G. Frosini – Ingresso e uscita Slide 11

12 G. Frosini – Ingresso e uscita Slide 12
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 iRBR, iTBR; } in_out; union { ioaddr iCTRI, iCTRO; } ctr_io; }; struct des_io { interf_reg indreg; natl mutex; natl sincr; natl cont; addr punt; }; G. Frosini – Ingresso e uscita Slide 12

13 G. Frosini – Ingresso e uscita Slide 13
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 iRBR e iCTRI per le operazioni di ingresso, e iTBR e iCTRO 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

14 G. Frosini – Ingresso e uscita Slide 14
DESINTI E DESINTO (2) // sistema.cpp extern des_io desinti[T]; extern des_io desinto[T]; # sistema.s .DATA .GLOBAL desinti .GLOBAL desinto desinti: .SPACE BYTE_IO # T*sizeof(des_io) desinto: .SPACE BYTE_IO Descrittori: devono essere inizializzati, prima di poter essere utilizzati: il campo indreg, con gli indirizzi iRBR e iCTRI per i descrittori di operazioni di ingresso, e con gli indirizzi iTBR e iCTRO 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

15 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 go_input: #... .GLOBAL halt_input halt_input: #... # sottoprogrammi analoghi per l’uscita # outputb, go_output, halt_output G. Frosini – Ingresso e uscita Slide 15

16 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 = CON1; *i_ctr = al } extern "C" void halt_input(ioaddr i_ctr) ; // { natb al = CON2; *i_ctr = al } extern "C" void outputb(natb a, ioaddr reg) ; // { *reg = a } extern "C" void go_output(ioaddr i_ctr) ; // { natb al = CON3; *i_ctr = al } extern "C" void halt_output(ioaddr i_ctr) ; // { natb al = CON4; *i_ctr = al } G. Frosini – Ingresso e uscita Slide 16

17 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

18 OPERAZIONE DI LETTURA (2)
# utente.s .TEXT .GLOBAL read_n # sottoprogramma di interfaccia read_n: INT $io_tipo_r # gate di tipo trap rit_r: RET # sistema.s .EXTERN c_read_n a_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

19 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.iCTRI); G. Frosini – Ingresso e uscita Slide 19

20 G. Frosini – Ingresso e uscita Slide 20
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

21 G. Frosini – Ingresso e uscita Slide 21
DRIVER DI INGRESSO (2) # sistema.s .TEXT .EXTERN c_driverin_i int_tipoi_i: CALL salva_stato CALL c_driverin_i CALL inviaEOI 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.iCTRI); 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.iRBR, 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

22 G. Frosini – Ingresso e uscita Slide 22
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

23 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.iCTRO); G. Frosini – Ingresso e uscita Slide 23

24 G. Frosini – Ingresso e uscita Slide 24
DRIVER DI USCITA # sistema.s .TEXT .EXTERN c_driverout_i int_tipoo_i: CALL salva_stato CALL c_driverout_i CALL inviaEOI 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.iCTRO); 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.iTBR); p_deso->punt = static_cast<natb*>(p_deso->punt) + 1; G. Frosini – Ingresso e uscita Slide 24

25 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: leggere il contenuto di CTR in un registro di appoggio; porre a 1 il bit bi o il bit bo (lasciando invariati gli altri bit) nel registro di appoggio; scrivere il nuovo contenuto del registro di appoggio in CTR. G. Frosini – Ingresso e uscita Slide 25

26 REGISTRO DI CONTROLLO UNICO (2)
Con le interruzioni (mascherabili) abilitate potrebbe verificarsi la seguente sequenza di eventi: un processo di ingresso P1 compie l’azione 1), poi viene interrotto; va in esecuzione un processo di uscita P2, che compie le azioni 1), 2) e 3), poi viene interrotto; va nuovamente in esecuzione il processo P1, che compie le azioni 2) e 3), rendendo nulla l’azione 3) effettuata da P2. 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

27 G. Frosini – Ingresso e uscita Slide 27
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

28 G. Frosini – Ingresso e uscita Slide 28
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

29 G. Frosini – Ingresso e uscita Slide 29
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

30 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

31 DESCRITORI DI INTERFACCE A BLOCCHI
// sistema.cpp ... struct interfb_reg { ioaddr iBR, iCTR, iCMD, iSCR; ioaddr iBAR; }; 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 desintb: .SPACE BYTE_BIO # TB*sizeof(desb_io) Descrittori: devono essere inizializzati prima di essere utilizzati. G. Frosini – Ingresso e uscita Slide 31

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

33 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 = CONB1; *ctr = al } extern "C" void haltb_inout(ioaddr ctr) ; // { natb al = CONB2; *ctr = al } G. Frosini – Ingresso e uscita Slide 33

34 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

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

36 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 CALL c_writeb_n G. Frosini – Ingresso e uscita Slide 36

37 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.iBAR); outputb(quanti, p_des -> indreg.iSCR); gob_inout(p_des -> indreg.iCTR); outputb(CMD_READ, p_des -> indreg.iCMD); G. Frosini – Ingresso e uscita Slide 37

38 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.iBAR); outputb(quanti, p_des -> indreg.iSCR); gob_inout(p_des -> indreg.iCTR); outputb(CMD_WRITE, p_des -> indreg.iCMD); outputbb(vetto, DIM_BLOCK, p_des -> indreg.iBR); G. Frosini – Ingresso e uscita Slide 38

39 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 c_driverb_i int_tipob_i: CALL salva_stato # coinvolto gate di tipo interrupt CALL c_driverb_i CALL inviaEOI CALL carica_stato IRET G. Frosini – Ingresso e uscita Slide 39

40 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.iCTR); 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.iCMD, lav); // ack richiesta di interruzione if (lav == CMD_READ) inputbb(p_des -> indreg.iBR, 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.iBR); p_des->punt = static_cast<natb*>(p_des->punt) + DIM_BLOCK; G. Frosini – Ingresso e uscita Slide 40

41 DRIVER E PROCESSI ESTERNI (1)
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

42 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

43 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

44 ATTIVAZIONE DEI 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

45 STRUTTURA DI UN HANDLER
viene attivato da un’interruzione esterna e manda in esecuzione un processo esterno. # sistema.s .DATA a_p .SPACE P*4 .TEXT .EXTERN inspronti handler_i: CALL salva_stato # vi sono al più P handler (i va da 0 a P-1) CALL inspronti MOVL $i, %ECX # i ha uno specifico valore per ogni handler MOVL a_p(, %ECX, 4), %EAX # mette nel puntatore esecuzione MOVL %EAX, esecuzione # il contenuto del puntatore a_p[i] associato all’handler CALL carica_stato IRET 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

46 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 sem_ini: INT $tipo_si rit_si: RET .GLOBAL sem_wait sem_wait: INT $tipo_w # sottoprogramma di interfaccia rit_w: RET .GLOBAL sem_signal sem_signal: INT $tipo_s # sottoprogramma di interfaccia rit_s: RET G. Frosini – Ingresso e uscita Slide 46

47 G. Frosini – Ingresso e uscita Slide 47
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

48 STRUTTURA DI UN PROCESSO ESTERNO (1)
Stuttura di un processo esterno: // io.cpp extern "C" void wfi(); void estern(int h) // codice di un processo esterno { ... for(;;) { ... wfi(); } } 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

49 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

50 G. Frosini – Ingresso e uscita Slide 50
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 wfi: INT $tipo_wfi rit_wfi: RET G. Frosini – Ingresso e uscita Slide 50

51 G. Frosini – Ingresso e uscita Slide 51
PRIMITIVA WFI() (2) .TEXT .EXTERN schedulatore a_wfi: # routine INT $tipo_wfi CALL salva_stato // non avviene l’inserimento in lista pronti CALL inviaEOI CALL schedulatore CALL carica_stato IRET Chiamata del sottoprogramma inviaEOI: 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

52 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 %EAX, esecuzione # il contenuto del puntatore a_p[i] associato all’handler CALL 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

53 INTERRUZIONE DI UN 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

54 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 inspronti handleri_j: CALL salva_stato CALL inspronti MOVL $i, %ECX MOVL a_p(, %ECX, 4), %EAX # mette nel puntatore esecuzione MOVL %EAX, esecuzione # il contenuto del puntatore # associato all’handler CALL carica_stato IRET G. Frosini – Ingresso e uscita Slide 54

55 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.iCTRI); inputb(p_desi -> indreg.in_out.iRBR, 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

56 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.iCTRO); a = *static_cast<natb*>(p_deso-> punt); // caricamento outputb(a, p_deso -> indreg.in_out.iTBR); // 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

57 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.iCTR); inputb(p_des -> indreg.iCMD, lav); // ack richiesta di interruzione if (lav == CMD_READ) inputbb(p_des -> indreg.iBR, 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.iBR); 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

58 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

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

60 G. Frosini – Ingresso e uscita Slide 60
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

61 AZIONI DELLA FUNZIONE PCI (1)
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

62 AZIONI DELLA FUNZIONE PCI (2)
Operazioni cicliche effettuate dalla funzione PCI: 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; 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. 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

63 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

64 G. Frosini – Ingresso e uscita Slide 64
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 iBMCMD, iBMSTR, iBMDTPR; natl mutex, sincr; natl prd[2]; natl stato; }; extern dmadescrittore dmades[M]; # io.s .ALIGN # allineamento a 2**12 .DATA .GLOBAL dmades dmades: SPACE DMA_BYTE # M * sizeof(dmadescrittore) # tutti i descrittori devono essere contenuti in una pagina G. Frosini – Ingresso e uscita Slide 64

65 INIZIALIZZAZIONE DEI DESCRITTORI
Inizializzazione di un descrittore DMA: inizializzazione dei campi iBMCMD, iBMSTR, iBMDTPR, 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

66 G. Frosini – Ingresso e uscita Slide 66
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

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

68 G. Frosini – Ingresso e uscita Slide 68
BUFFER DI MEMORIA (1) Primitiva che effettua un’operazione in DMA: prevede che il processo che la invoca, sia P1, si blocchi su un semaforo di sincronizzazione; esecuzione di un altro processo, sia P2. 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 P1 non sia accessibile mentre è in esecuzione P2. 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 P2). 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

69 G. Frosini – Ingresso e uscita Slide 69
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

70 SOTTOPROGRAMMI DI UTILITA’ (1)
# io.s .TEXT .GLOBAL dmago_in # scrittura in BMCMD dmago_in: #... RET .GLOBAL dmago_out # scrittura in BMCMD dmago_out: #... .GLOBAL leggi_stato # lettura di BMSTS leggi_stato: #... 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

71 SOTTOPROGRAMMI DI UTILITA’ (2)
// io.cpp extern "C " void dmago_in(dmadescrittore* p_dmades); // *(p_dmades-> iBMCMD) = LAV0; extern " C " void dmago_out(dmadescrittore* p_dmades); // *(p_dmades-> iBMCMD) = LAV1; extern " C " natl leggi_stato(dmadescrittore* p_dmades); // return *(p_dmades->iBMSTS) ; 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] = 0x | quanti; // EOT posto a 1 } G. Frosini – Ingresso e uscita Slide 71

72 G. Frosini – Ingresso e uscita Slide 72
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

73 G. Frosini – Ingresso e uscita Slide 73
LETTURA IN DMA (2) # utente.s .DATA .ALIGN # allineamento a 2**12 .GLOBAL buf_rr buf_rr: .SPACE 4096 .TEXT .GLOBAL dmaleggi # sottoprogramma di interfaccia dmaleggi: INT $dma_tipo_r # gate di tipo trap rit_dr: RET # io.s .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

74 G. Frosini – Ingresso e uscita Slide 74
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

75 G. Frosini – Ingresso e uscita Slide 75
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

76 G. Frosini – Ingresso e uscita Slide 76
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

77 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

78 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

79 DESCRITTORI DI INTERFACCE A BLOCCHI IN DMA
// io.cpp ... struct interfreg { ioaddr iBAR; }; 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

80 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 # allineamento a 2**12 .GLOBAL buffb_rr buffb_rr: .space 4096 .TEXT .GLOBAL dmaleggib # sottoprogramma di interfaccia dmaleggib: INT $dma_tipob_r # gate di tipo trap rit_dbr: RET G. Frosini – Ingresso e uscita Slide 80

81 LETTURA A BLOCCHI IN DMA (2)
# io.s .EXTERN c_dmaleggib a_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

82 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.iBAR); 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

83 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


Scaricare ppt "G. Frosini – Ingresso e uscita Slide 1"

Presentazioni simili


Annunci Google