Per cosa vengono utilizzate le istruzioni IN & OUT in x86?

Li ho encoutered alle istruzioni IN & OUT durante la lettura del libro “Understanding Linux Kernel”. Ho consultato il manuale di riferimento.

5.1.9 Istruzioni I / O

Queste istruzioni spostano i dati tra le porte I / O del processore e un registro o una memoria.

IN Read from a port OUT Write to a port INS/INSB Input string from port/Input byte string from port INS/INSW Input string from port/Input word string from port INS/INSD Input string from port/Input doubleword string from port OUTS/OUTSB Output string to port/Output byte string to port OUTS/OUTSW Output string to port/Output word string to port OUTS/OUTSD Output string to port/Output doubleword string to port 

Non ho avuto poche cose:

  1. “porte I / O del processore”. Quali sono? Perché dovremmo leggere e scrivere “stringhe” da e verso queste porte?
  2. Non ho mai scoperto uno scenerio dove ho bisogno di usare queste istruzioni. Quando avrei bisogno di questi?
  3. Dare alcuni esempi pratici.

Sai come funziona l’indirizzamento della memoria? C’è un bus indirizzo, un bus dati e alcune linee di controllo. La CPU inserisce l’indirizzo di un byte (o un byte di inizio) della memoria sul bus di indirizzo, quindi solleva il segnale READ e qualche chip RAM restituisce il contenuto della memoria a quell’indirizzo alzando o abbassando le singole righe (corrispondenti ai bit nei byte) sul bus dati. Funziona sia per RAM che per ROM.

Ma poi ci sono anche dispositivi I / O: porte seriali e parallele, il driver per il minuscolo altoparlante interno di un PC, i controller del disco, i chip audio e così via. E anche questi dispositivi vengono letti e scritti da. Devono inoltre essere indirizzati in modo che la CPU acceda al dispositivo corretto e (di solito) alla corretta posizione dei dati all’interno di un determinato dispositivo.

Per alcuni modelli di CPU, inclusa la serie xxx86, presente nella maggior parte dei PC “moderni”, i dispositivi I / O condividono lo spazio degli indirizzi con la memoria. Entrambi i dispositivi RAM / ROM e IO sono collegati allo stesso indirizzo, ai dati e alle linee di controllo. Ad esempio, la porta seriale per COM1 viene indirizzata a partire da (hex) 03F8. Ma c’è quasi certamente memoria allo stesso indirizzo.

Ecco un diagramma molto semplice:

[ https://qph.ec.quoracdn.net/main-qimg-e510d81162f562d8f671d5900da84d68-c?convert_to_webp=true ]

Chiaramente la CPU ha bisogno di parlare con la memoria o il dispositivo I / O, mai entrambi. Per distinguere tra i due, una delle linee di controllo chiamata “M / # IO” asserisce se la CPU vuole parlare alla memoria (riga = alta) o un dispositivo I / O (linea = bassa).

L’istruzione IN viene letta da un dispositivo I / O, scrive OUT. Quando si usano le istruzioni IN o OUT, il M / # IO non viene asserito (tenuto premuto), quindi la memoria non risponde e il chip I / O lo fa. Per le istruzioni orientate alla memoria, M / # IO viene asserito in modo che la CPU parli alla RAM e gli IO Device rimangano fuori dalla comunicazione.

In determinate condizioni i dispositivi IO possono pilotare le linee dati e la RAM può leggerle contemporaneamente. E viceversa. Si chiama DMA.

Tradizionalmente, le porte seriali e della stampante, oltre a tastiera, mouse, sensori di temperatura e così via erano dispositivi I / O. I dischi erano una specie di mezzo; i trasferimenti di dati verrebbero avviati dai comandi di I / O, ma il controller del disco di solito li depositerebbe direttamente nella memoria di sistema.

Nei moderni sistemi operativi come Windows o Linux, l’accesso alle porte I / O è nascosto da “normali” programmi utente, e ci sono strati di software, istruzioni privilegiate e driver per gestire l’hardware. Quindi, in questo secolo, la maggior parte dei programmatori non si occupa di quelle istruzioni.

Inizia con qualcosa di simile a questo:

http://www.cpu-world.com/info/Pinouts/8088.html

Stai imparando le istruzioni per un chip / architettura tecnologicamente molto vecchio. Indietro quando tutto tranne il core del processore era off-chip. Vedi le linee di indirizzo e le linee di dati e c’è una riga di lettura RD e riga di scrittura WR e linea IO / M?

Esistevano due tipi di istruzioni basate su memoria e I / O perché c’erano spazi indirizzabili, facilmente decodificati dall’IO / M IO o dalla memoria.

Ricorda che hai avuto la logica della colla 74LSxx, molti fili e molti chip per colbind una memoria al processore. E la memoria era solo quel ricordo, grandi chip costosi. Se avessi una periferica necessaria a fare qualcosa di utile, avevi anche i registri di controllo, la memoria potrebbe essere i dati dei pixel, ma da qualche parte dovevi impostare i limiti di scansione orizzontale e verticale, questi potrebbero essere singoli latch 74LSxx, NON ricordi, avere io / O mappato I / O salvato su entrambe le logiche di colla e ha molto senso dal punto di vista del programmatore, evitando anche di cambiare i registri dei segmenti per mirare alla finestra della memoria 64K, ecc. Lo spazio degli indirizzi di memoria era una risorsa sacra, specialmente quando volevo limitare la decodifica dell’indirizzo a pochi bit perché ogni bit costava un certo numero di chip e fili.

Come I / O mappato I / O mappato I / O mappato in memoria I / O big e little endian era una guerra religiosa. E alcune delle risposte che vedrai alla tua domanda rifletteranno le opinioni forti che sono ancora in circolazione oggi nelle persone che l’hanno vissuta. La realtà è che ogni chip sul mercato oggi ha più busess per varie cose, non si blocca il tuo orologio in tempo reale dal bus di memoria ddr con un decodificatore di indirizzi. Alcuni hanno persino bus di dati e istruzioni completamente separati. In un certo senso, Intel ha vinto la guerra per il concetto di spazi di indirizzi separati per diverse classi di cose anche se il termine porta I / O è malvagio e cattivo e non dovrebbe essere pronunciato per dire 20-30 anni in più. Hai bisogno della gente della mia età che l’abbia vissuta per essere ritirata o scomparsa prima che la guerra sia davvero finita. Anche il termine I / O mappato in memoria è una cosa del passato.

Questo è davvero tutto ciò che è sempre stato, un singolo indirizzo decodifica un bit all’esterno del chip Intel, controllato dall’uso di istruzioni specifiche. Usa un set di istruzioni in cui il bit era in uso una serie di istruzioni che il bit era spento. Vuoi vedere qualcosa di interessante da un’occhiata al set di istruzioni per i processori xmos xcore hanno un sacco di cose che sono istruzioni invece di registri mappati in memoria, ci vuole questo I / O mappato I / O cosa ad un livello completamente nuovo.

Dove è stato usato è come ho descritto sopra, avresti messo le cose che avevano senso e potresti permetterti di masterizzare lo spazio degli indirizzi di memoria come pixel video, memoria dei pacchetti di rete (forse), memoria delle tabs audio (beh, non è così, ma potresti avere ), ecc. E i registri di controllo, lo spazio di indirizzamento relativo ai dati era molto piccolo, forse solo pochi registri, sono stati decodificati e utilizzati nello spazio I / O. le ovvie sono / erano le porte seriali e le porte parallele che avevano poca o nessuna memoria, avresti potuto avere un piccolo fifo sulla porta seriale se non altro.

Poiché lo spazio degli indirizzi era scarso, non era raro e si vede ancora oggi che la memoria nasconde dietro due registri un registro di indirizzi e un registro dati, questa memoria è disponibile solo attraverso questi due registri, non è mappata in memoria. quindi scrivi l’offset in questa memoria nascosta nel registro degli indirizzi e leggi o scrivi il registro dei dati per accedere al contenuto della memoria. Ora perché intel aveva le istruzioni rep e si poteva combinarlo con insb / w outsb / w il decodificatore hardware sarebbe (se tu avessi gente simpatica / amichevole che lavora con te) autoincrementare l’indirizzo ogni volta che hai fatto un ciclo I / O. Quindi è ansible scrivere l’indirizzo di partenza nel registro degli indirizzi e fare un rep outsw e senza masterizzare i cicli di fetch e decode clock nel processore e sul bus di memoria si potrebbe spostare i dati abbastanza velocemente dentro o fuori dalla periferica. Questo genere di cose è ora considerato un difetto di progettazione grazie ai moderni processori super scalari con recuperi basati sulla previsione delle filiali, l’hardware può leggere in qualsiasi momento senza avere nulla a che fare con l’esecuzione del codice, pertanto non è MAI necessario incrementare automaticamente un indirizzare o cancellare i bit in un registro di stato o modificare qualcosa come risultato di una lettura in un indirizzo.

I meccanismi di protezione incorporati nel 386 e al presente rendono davvero molto facile accedere agli I / O dallo spazio utente. A seconda di cosa fai per vivere, cosa produce la tua azienda, ecc. Puoi sicuramente usare la famiglia di istruzioni dallo spazio utente (programmi applicativi in ​​windows e linux, ecc.) O spazio kernel / driver, è il tuo scelta. Puoi anche fare cose divertenti come approfittare della macchina virtuale e usare le istruzioni I / O per parlare con i driver, ma questo probabilmente farebbe incazzare la gente sia nei mondi windows che linux, che driver / app non lo farebbero molto lontano. Gli altri poster sono corretti in quanto probabilmente non avrai mai bisogno di usare queste istruzioni a meno che tu non stia scrivendo i driver, e probabilmente non scriverai mai driver per dispositivi che usano I / O mappato I / O perché sai … il i driver per questi dispositivi legacy sono già stati scritti. I progetti moderni hanno sicuramente I / O ma sono tutti mappati in memoria (dal punto di vista dei programmatori) e utilizzano istruzioni di memoria e non istruzioni I / O. Ora il lato opposto se questo è DOS non è sicuramente morto, a seconda di dove potresti build macchine per il voto o pompe di benzina o registratori di cassa o una lunga lista di apparecchiature basate su DOS. Infatti, se lavori da qualche parte per la costruzione di periferiche o tabs madri PC o PC, gli strumenti basati su DOS sono ancora ampiamente utilizzati per testare e distribuire aggiornamenti del BIOS e altre cose simili. Mi imbatto ancora in situazioni in cui devo prendere il codice da un programma di test DOS corrente per scrivere un driver linux. Proprio come non tutti quelli che possono lanciare o prendere una partita di football nella NFL, la percentuale di lavoro del software che coinvolge questo genere di cose è pochissima. Quindi è ancora sicuro dire che queste istruzioni che hai trovato probabilmente non ti saranno più utili di una lezione di storia.

Dare alcuni esempi pratici.

Prima impara come:

Poi:

  1. Controller PS / 2 : ottieni l’ID scancode dell’ultimo carattere digitato sulla tastiera su al :

     in $0x60, %al 

    Esempio minimo

  2. Real Time Clock (RTC) : ottieni il tempo di wall con la definizione di secondi:

     .equ RTCaddress, 0x70 .equ RTCdata, 0x71 /* al contains seconds. */ mov $0, %al out %al, $RTCaddress in $RTCdata, %al /* al contains minutes. */ mov $0x02, %al out %al, $RTCaddress in $RTCdata, %al /* al contains hour. */ mov $0x04, %al out %al, $RTCaddress 

    Esempio minimo

  3. Programmable Interval Timer (PIT) : genera un numero di interrupt 8 ogni 0x1234 / 1193181 secondi:

     mov $0b00110100, %al outb %al, $0x43 mov $0xFF, %al out %al, $0x34 out %al, $0x12 

    Esempio minimo

    Un utilizzo del kernel 4.2 di Linux . Ce ne sono altri

Testato su: QEMU 2.0.0 Ubuntu 14.04 e hardware reale Lenovo ThinkPad T400.

Come trovare i numeri di porta: esiste una specifica dell’assegnazione della porta I / O x86?

https://github.com/torvalds/linux/blob/v4.2/arch/x86/kernel/setup.c#L646 ha un elenco di molte porte utilizzate dal kernel di Linux.

Altre architetture

Non tutte le architetture hanno istruzioni IO dedicate.

Ad esempio, in ARM, l’IO viene fatto semplicemente scrivendo agli indirizzi di memoria definiti dall’hardware magico.

Penso che questo sia ciò che https://stackoverflow.com/a/3221839/895245 significa per “I / O mappato in memoria vs I / O mappato I / O”.

Dal punto di vista del programmatore, preferisco il modo ARM, dal momento che le istruzioni IO hanno già bisogno di indirizzi magici per funzionare, e abbiamo enormi spazi di indirizzi inutilizzati in indirizzamento a 64 bit.

Vedere https://stackoverflow.com/a/40063032/895245 per un esempio ARM concreto.

Se non stai scrivendo un sistema operativo, non utilizzerai mai queste istruzioni.

Le macchine basate su x86 hanno due spazi di indirizzi indipendenti: lo spazio degli indirizzi di memoria che si ha familiarità con lo spazio degli indirizzi I / O. Gli indirizzi delle porte I / O sono solo 16 bit di larghezza e i registri di basso livello di riferimento e altri widget di basso livello che fanno parte di un dispositivo I / O, qualcosa come una porta seriale o parallela, controller del disco, ecc.

Non ci sono esempi pratici perché questi sono usati solo dai driver di dispositivo e dai sistemi operativi.

A livello di hardware, la maggior parte dei microprocessori ha poca o nessuna capacità di I / O integrata. Alcuni processori hanno uno o più pin che possono essere accesi e spenti usando istruzioni speciali e / o uno o più pin che possono essere testati usando speciali istruzioni di ramo, ma tali caratteristiche sono rare. Invece, l’I / O viene solitamente gestito collegando il sistema in modo che l’accesso a un intervallo di indirizzi di memoria possa innescare qualche effetto, o includendo istruzioni “in” e “out” che si comportano come operazioni di caricamento / memorizzazione della memoria eccetto che un segnale speciale viene visualizzato dicendo “Questa è un’operazione di I / O invece di un’operazione di memoria”. Ai tempi dei processori a 16 bit, c’erano dei veri vantaggi nell’avere istruzioni specialistiche in / out. Oggigiorno tali vantaggi sono in gran parte discutibili, dal momento che si può semplicemente allocare una grande porzione del proprio spazio di indirizzamento all’I / O e ne rimane ancora abbastanza per la memoria.

Dal momento che un programma potrebbe causare gravi danni a un sistema eseguendo in modo inappropriato le istruzioni di I / O (ad es. Tali istruzioni potrebbero eseguire accessi al disco arbitrari), tutti i sistemi operativi moderni vietano l’uso di tali istruzioni nel codice a livello utente. Alcuni sistemi possono consentire la virtualizzazione di tali istruzioni; se il codice utente tenta di scrivere su porte I / O 0x3D4 e 0x3D5, ad esempio, un sistema operativo potrebbe interpretarlo come un tentativo di impostare alcuni registri di controllo del controllo video per spostare il cursore lampeggiante. Ogni volta che il programma utente eseguiva l’istruzione OUT, il sistema operativo prendeva il sopravvento, verificava cosa stava cercando di fare il programma utente e agiva in modo appropriato.

Nella stragrande maggioranza dei casi, anche se il sistema operativo traducesse un’istruzione IN o OUT in qualcosa di adatto, sarebbe più efficiente richiedere direttamente l’azione appropriata dal sistema operativo.

C’è un po ‘più di inganno a questo. Non si limita a multiplexare uno spazio di indirizzamento separato di 64kb sugli stessi fili con un “extra address bus / chip select pin”. Intel 8086 e 8088 e i loro cloni multiplex anche il bus dati e il bus indirizzo; tutte cose molto insolite nelle CPU. I fogli di dati sono pieni di elementi di configurazione ‘minimo / massimo’ e tutti i registri di latch che è necessario associare ad esso per farlo comportarsi ‘normalmente’. D’altra parte, salva un carico di e gate e ‘o’ gates nella decodifica degli indirizzi e 64kb dovrebbe essere ‘abbastanza porte i / o per tutti’: P.

Inoltre, per tutte quelle persone che si occupano solo di sviluppatori di driver, prendi nota: oltre a persone che utilizzano chip compatibili con Intel in altri componenti hardware rispetto ai PC (non erano mai stati concepiti per l’uso nel PC IBM in primo luogo), IBM li ha semplicemente presi perché erano economici e già sul mercato), Intel vende anche microcontrollori con lo stesso set di istruzioni (Intel Quark) e ci sono un sacco di “sistemi su un chip” di altri fornitori con lo stesso set di istruzioni. Non penso che riuscirai a stipare qualcosa con “spazio utente”, “kernel” e “driver” separati in 32kb :). Per la maggior parte delle cose, questi “sistemi operativi” così complessi non sono né ottimali né desiderati. Formando alcuni pacchetti UDP nella RAM e inserendoli in un buffer circolare e facendo alcuni relè, fare clic su un clic non richiede un kernel 30mb e un tempo di caricamento di 10 secondi, lo sai. È fondamentalmente la scelta migliore nel caso in cui un microcontrollore PIC non sia sufficiente ma non si desidera un intero PC industriale. Quindi le istruzioni I / O sulla porta vengono utilizzate molto e non solo dagli “sviluppatori di driver” per i sistemi operativi più grandi.

CPU collegata ad alcuni controller esterni tramite porte io. sul vecchio x86 pc lavoro con unità floppy usando le porte I / O. se sai quali comandi accettano il controller del dispositivo, puoi programmarlo attraverso le sue porte.

Nel mondo moderno non userai mai le istruzioni per le porte. Eccezione se sei (o lo sarai) sviluppatore di driver.

vi sono informazioni più dettagliate sulle porte I / O http://webster.cs.ucr.edu/AoA/DOS/ch03/CH03-6.html#HEADING6-1