Sartomiki.net

  • Aumenta dimensione caratteri
  • Dimensione caratteri predefinita
  • Diminuisci dimensione caratteri

Sistemi di I/O

E-mail Stampa PDF
Valutazione attuale: / 0
ScarsoOttimo 

Generalità
I compiti principali di un calcolatore sono la gestione delle operazioni di Input/Output e l'elaborazione. Molto spesso, però, le operazioni di calcolo avvengono su dati reperiti dai dispositivi di I/O, rendendo quindi il sistema di I/O determinante per le prestazioni dell'intero sistema.
Esistono dispositivi di I/O molto diversi tra loro sia per velocità, sia per funzionalità e questo rende il sottosistema di I/O molto complesso e difficile da realizzare.
Negli ultimi anni si è notata una certa uniformazione degli standard di comunicazione a livello fisico/logico per le interfacce di I/O, ma anche una crescente diversificazione dei dispositivi. Per gestire queste tendenze contrastanti, l'architettura di I/O tende ad uniformarsi, mentre la struttura del SO fornisce moduli implementabili direttamente dai costruttori dei dispositivi (driver). In questo modo un'interfaccia uniforme permette la comunicazione di dispositivi molto diversi tra loro.

Architetture e dispositivi di I/O
Nonostante la diversità dei dispositivi di I/O, l'architettura di collegamento con il calcolatore è abbastanza semplice: un dispositivo comunica con il calcolatore mediante segnali, che passano attraverso un cavo e, attraverso una porta, essi arrivano al calcolatore. Se il cavo è condiviso tra più dispositivi esso viene detto BUS. Ogni BUS, oltre che dal filo, è definito anche da un protocollo di comunicazione, che definisce quali siano le regole che permettono a più dispositivi di utilizzarlo senza creare conflitti. E' possibile collegare tra loro più BUS, attraverso altri dispositivi: in questo caso si parla di daisy chain.
Esistono diversi tipi di BUS, sia per velocità che per protocolli. Un esempio è dato dal BUS PCI, utilizzato nella maggior parte dei computer per connettere tra loro i dispositivi veloci (memoria video, controllore BUS SCSI per i dischi), al quale è anche collegato, mediante un'interfaccia, un BUS di espansione, che permette l'accesso al BUS PCI anche ai dispositivi più lenti (mouse, tastiera, porta seriale, …).
Un controllore è un insieme di componenti elettronici che permette il funzionamento di un protocollo di comunicazione. Un controllore SCSI, ad esempio, è spesso realizzato mediante un adattatore, che attraverso un'unità di elaborazione e l'esecuzione di microcopie, permette di elaborare i messaggi del complesso protocollo SCSI.
La CPU e i dispositivi non comunicano direttamente, in quanto passano i messaggi direttamente al controllore, il quale poi li gestisce. La comunicazione tra dispositivi/CPU e controllore avviene mediante l'uso di registri, dove vengono salvate le informazioni riguardanti il tipo di comunicazione e i dati da trasferire.
E' possibile accedere ai dispositivi di I/O mediante chiamate di sistema apposite (isolated I/O) oppure mediante chiamate di sistema standard, come read e write (memory mapped), a seconda dell'implementazione. Alcuni sistemi utilizzano entrambe le tecniche.
Le porte di I/O sono generalmente costituite da 4 registri di comunicazione:
-status, che contiene lo stato della porta (trasferimento completato, …)
-data-in, che contiene le informazioni ricevute
-data-out, che contiene le informazioni da spedire
-control, che contiene i dati per la configurazione della porta. E' ad esempio contenuto un bit per l'impostazione del metodo di comunicazione (half duplex, full duplex), oppure la lunghezza delle parole, il controllo di parità, la velocità di trasferimento…
La dimensione dei registri va da 1B a 4B. Essi possono essere concatenati secondo una politica FIFO, per gestire più dati.

Protocolli di comunicazione
Esistono diversi protocolli fisici di comunicazione tra CPU e un controllore di BUS. Essi sono abbastanza complessi, ma esistono principalmente 3 politiche possibili:
-polling. In questo caso il controllore specifica lo stato del BUS ad esso associato, mediante un bit (busy). Ogni volta che la CPU necessita di effettuare un'operazione di I/O su uno specifico BUS legge il valore di busy, finchè esso non risulta libero. A questo punto pone a 1 il bit di write e scrive il byte da trasferire nel registro data-out. La CPU pone a 1 il bit di command-ready, nel momento in cui ha terminato queste due operazioni. Quando il controllore si accorge che la CPU ha bisogno di trasferire dei dati, mette a 1 il bit busy, legge il tipo di trasferimento (bit di write), i dati presenti in data-out e li trasferisce. Nel momento in cui è terminato il trasferimento, la CPU pone a 0 il bit di command-ready, mentre il controllore pone a 0 il registro di error (nel caso in cui non ci siano stati errori di comunicazione). Il metodo di polling è molto oneroso per la CPU, in quanto deve attendere attivamente che il BUS sia libero, prima di poter nuovamente eseguire altre operazioni.
-interrupt. In questo caso la CPU è collegata mediante una linea di richiesta di interrupt al controllore. Ad ogni ciclo, la CPU controlla se la linea è attiva, e, in questo caso, salva lo stato corrente e chiama la procedura di gestione dell'interrupt (interrupt handler routine), chiamata a sua volta dal controllore. Il controllo a questo punto passa al gestore delle interruzioni, che evade il compito. Negli OS moderni sono necessari meccanismi di gestione dell'interrupt più complessi: è per questo che è stato introdotto un controllore degli interrupt che ha diverse funzioni:
--gestire due linee di richieste di interrupt (una per gli interrupt mascherabili, che possono interrompere solo alcune procedure, un'altra per gli interrupt non mascherabili), per garantire che le fasi critiche di elaborazione non vengano interrotte.
--gestire una interrupt vector table, che ha lo scopo di racchiudere tutti gli indirizzi alle procedure di interrupt sollevabili dai dispositivi. In questo modo il controllore deve inviare alla CPU una richiesta di interrupt contenente lo scostamento all'interno dell'interrupt vector table relativo alla procedura. Grazie a questo metodo è anche possibile accorpare tra loro richieste di interrupt per dispositivi diversi.
--gestire livelli di priorità delle interruzioni.
--gestire le eccezioni (come la divisione per 0, l'accesso agli indirizzi di memoria inesistenti, accessi in memoria virtuale…)
--gestire chiamate di sistema (trap), in quanto molte di esse chiamano routine di libreria standard.
--gestire il controllo di flusso all'interno del kernel
-DMA. La gestione a interrupt è molto funzionale, ma può risultare onerosa nel caso in cui ci siano continue letture del bit di stato (trasferimenti di file di grosse dimensioni). E' per questo motivo che parte di questo lavoro è spesso affidato al controllore di accesso diretto alla memoria (DMA). La CPU, quando deve trasferire grosse parti di memoria da una parte ad un'altra, con bassa priorità, affida il lavoro al DMA, inviandogli una richiesta contenente l'indirizzo e la dimensione dei blocchi da trasferire. Il DMA agisce direttamente sul BUS di memoria, mentre la CPU può continuare l'esecuzione del codice. La procedura di negoziazione tra il DMA e il controllore del dispositivo avviene per mezzo di due fili diretti (DMA-request e DMA-ACK). La comunicazione avviene per mezzo di una segnalazione da parte del dispositivo mediante DMA-req, a questo punto il DMA prende il controllo del BUS e mette sulla linea di comunicazione l'indirizzo richiesto e segnala l'inizio della comunicazione per mezzo del DMA-ACK. Quando il dispositivo comprende l'indirizzo, invia i dati e toglie il DMA-req. Quando l'intero trasferimento finisce il DMA controller blocca l'accesso alla memoria principale (sottrazione di cicli) e salva i dati. E' anche possibile che il DMA utilizzi indirizzi virtuali e non fisici per la comunicazione, in questo caso si parla di DVMA.

Tipologie di dispositivo
Nonostante la molteplicità di dispositivi, esistono diverse tecniche per gestirli in modo uniforme. E' possibile non considerare le differenze specifiche di ogni dispositivo, identificandone diverse tipologie. A ogni gruppo si accede per mezzo di un unico insieme di istruzioni, detto interfaccia. I driver, invece, implementano queste funzioni in modo differente per ogni singolo dispositivo. In questo modo il kernel può impartire le proprie funzioni in modo più o meno uniforme. Il problema principale è che ogni sistema operativo identifica funzionalità diverse per ogni gruppo, e costringe i produttori a implementare interfacce differenti per ogni SO.
I dispositivi di I/O si possono dividere in base a diversi parametri:
-trasferimento a flusso di caratteri o a blocchi, a seconda se viene trasferito un byte alla volta o un blocco di byte tutto insieme
-accesso sequenziale o diretto, a seconda se il dispositivo trasmette i dati in un ordine prestabilito o variabile.
-sincroni o asincroni, a seconda se i dati vengono trasferiti in un tempo prevedibile o no.
-condivisibili o riservati, a seconda se il dispositivo può essere usato contemporaneamente da più processi o no.
-velocità
-funzionalità (lettura/scrittura, sola lettura, sola scrittura).
Normalmente i SO forniscono chiamate specifiche anche per qualche dispositivo aggiuntivo, come i timer. E' anche possibile che i SO permettano il passaggio diretto tra applicazione e driver (back door).
Le interfacce più comuni sono:
-dispositivi con trasferimento a blocchi, che sintetizza tutte le funzionalità necessarie per la gestione di un unità a disco. E' possibile anche permettere il raw I/O, per gestire queste unità come semplici sequenze di blocchi. E' permesso, in alcuni casi, l'I/O diretto, che permette di caricare in memoria principale parti di disco, in modo da poterlo gestire come raw disco ad alta velocità.
-dispositivi con trasferimento a caratteri, che permette il funzionamento di dispositivi come la tastiera, mouse, modem, … . L'interfaccia base permette di implementare le funzioni base get e put, ma è possibile costruire servizi aggiuntivi, come la lettura per riga, la correzione ed altri.
-dispositivi di rete, che fornisce servizi specifici per gestire i lenti trasferimenti di rete. E' possibile che essi siano forniti mediante un'interfaccia basata su socket, che permette di creare un socket, collegarlo ad un punto della rete e controllare lo scambio di pacchetti. L'interfaccia dei socket prevede anche una funzione di nome select, che permette di gestire in parallelo più socket. Esistono altre interfacce di rete, come quelle di UNIX (pipe half duplex, FIFO, STREAMS, code, …)
-orologi e timer, che permette un uso efficiente di questi dispositivi. Essi forniscono informazioni sull'ora attuale, sul tempo trascorso e impostare sveglie.

Chiamate di I/O
E' importante decidere se le chiamate di sistema di I/O siano bloccanti o no. Nel caso di chiamata bloccante, nel momento in cui un processo fa una richiesta di I/O, esso viene interrotto e messo nella coda dei processi in attesa; nel momento in cui arriverà la risposta, ritornerà nella coda dei processi pronti ad essere eseguiti. Molti programmi, però, necessitano di continuare l'esecuzione anche durante le operazioni di I/O. E' tecnica comune utilizzare più thread per attendere un'I/O, e intanto continuare l'esecuzione dell'altro thread. Solaris, a partire da questa tecnica, ha implementato una libreria per gestire le chiamate non bloccanti.
Una possibile alternativa alle chiamate non bloccanti sono le chiamate asincrone, per le quali nel momento di una richiesta di I/O l'esecuzione del processo continua; nel momento in cui viene terminata l'esecuzione del comando di I/O, viene inviato un segnale all'applicazione stessa, mediante un segnale o una variabile.

Sottosistema di I/O per il kernel
Il kernel fornisce molti servizi accessori per garantire buone prestazione delle operazioni di I/O:
-scheduling di I/O, che permette di stabilire un ordine efficiente di gestione delle chiamate di I/O. Per garantire buone prestazioni viene gestita una coda per ogni processo, e viene scelto in base a diversi principi quale richiesta soddisfare. Lo scheduler può prediligere le richieste di I/O dei processi a priorità maggiore, tentare di essere equo, scegliere in base a quanto l'operazione di I/O sia onerosa in termini di tempo…
-gestione dei buffer, che permette di gestire dispositivi di diversa velocità (doppia bufferizzazione), diversa dimensione dei blocchi e realizzare la semantica delle copie (garantisce che la versione dei dati scritta nel disco sia conforme a quella contenuta nel buffer al momento della chiamata di sistema).
-gestione delle cache, che permette di gestire copie di dati in memoria principale contenuti su dispositivi esterni, in modo da diminuire notevolmente il tempo di accesso.
-code esclusive, che permette di gestire dispositivi che necessitano dati sequenziali. Ad esempio è possibile memorizzare in ordine corretto i dati da inviare ad una stampante, senza che essi vengano mescolati. In alcuni OS questa funzione è svolta da un demone o da un thread del kernel; è comunque sempre possibile aggiungere elementi alle code o rimuoverli a seconda delle esigenze.
-sistema gestione degli errori, che permette di risolvere problemi contingenti, derivate da chiamate di I/O errate. Questa funzione è svolta mediante il valore di una variabile associata (errno in UNIX) ad ogni chiamata di sistema. Molto spesso i dispositivi generano molti codici di errore, che però non vengono utilizzati dall'OS
-sistema di protezione, che permette di diminuire il numero di errori. Questa funzione è implementata impedendo l'I/O a tutte le applicazioni utente, che per effettuarle devono invocare chiamate di sistema specifiche, per chiedere all'OS di svolgere una data operazione. Il sistema verifica la richiesta e nel caso sia valida esegue l'operazione e ridà il controllo al processo chiamante. Spesso è necessario che i processi utenti utilizzino direttamente le periferiche (schede audio, schede video, …); in questo caso è possibile che l'OS permetta di dare il controllo esclusivo ad una applicazione di una parte della risorsa richiesta, mediante lock.
-strutture dati, che permettono all'OS di mantenere informazioni sullo stato dei diversi dispositivi di I/O. UNIX, ad esempio, gestisce l'accesso a diversi oggetti con chiamate di sistema uguali, ma diverse nell'implementazione. Queste strutture di gestione, offrono implementazioni di una stessa interfaccia. Windows, invece, applica modelli orientati agli oggetti ancora più espliciti, mediante l'uso di messaggi che si modificano a seconda della richiesta.


blog comments powered by Disqus
 

http://sartomiki.net/modules/mod_fuofb/assets/it/find-us-on-facebook-1.png

Follow me

Amici

Chi è online

 4 visitatori online

Siti amici

Banner

Notizie flash

Nella versione 0.9 è stata modificata completamente la grafica... Ci sono ancora alcuni piccoli bug, che andranno presto risolti! Per critiche, suggeririmenti o semplici commenti non esitate a Contattarmi!

PUBBLICITA'