webentwicklung-frage-antwort-db.com.de

Wofür werden IN & OUT-Anweisungen in x86 verwendet?

Ich habe diese Anweisungen unter IN & OUT gefunden, als ich das Buch "Linux-Kernel verstehen" gelesen habe. Ich habe Referenzhandbuch nachgeschlagen.

5.1.9 E/A-Anweisungen

Diese Anweisungen verschieben Daten zwischen den E/A-Anschlüssen des Prozessors und einem Register oder Speicher.

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

Ich habe nicht wenige Dinge bekommen:

  1. msgstr "I/O - Ports des Prozessors". Was sind Sie? Warum sollten wir "Strings" von diesen Ports lesen und schreiben wollen?
  2. Ich bin nie auf ein Szenario gestoßen, in dem ich diese Anweisungen verwenden muss. Wann würde ich diese brauchen?
  3. Nennen Sie einige praktische Beispiele.
50
claws

Sie wissen, wie die Speicheradressierung funktioniert? Es gibt einen Adressbus, einen Datenbus und einige Steuerleitungen. Die CPU schreibt die Adresse eines Bytes (oder eines Anfangsbytes) des Speichers in den Adressbus, hebt dann das READ-Signal an und irgendein RAM - Chip gibt hoffentlich den Inhalt des Speichers an dieser Adresse durch Anheben oder Absenken zurück einzelne Leitungen (entsprechend den Bits in den Bytes) auf dem Datenbus. Dies funktioniert sowohl für RAM als auch für ROM.

Aber es gibt auch E/A-Geräte: Serielle und parallele Anschlüsse, der Treiber für den winzigen internen Lautsprecher eines PCs, Festplattencontroller, Soundchips usw. Und diese Geräte werden auch gelesen und beschrieben. Sie müssen auch adressiert werden, damit die CPU auf das richtige Gerät und (normalerweise) den richtigen Datenstandort innerhalb eines bestimmten Geräts zugreift.

Bei einigen CPU-Modellen einschließlich der xxx86-Serie, wie sie in den meisten "modernen" PCs zu finden sind, teilen sich E/A-Geräte den Adressraum mit dem Speicher. Sowohl RAM/ROM- als auch IO Geräte sind an die gleichen Adress-, Daten- und Steuerleitungen angeschlossen. Beispielsweise wird die serielle Schnittstelle für COM1 ab (hex) 03F8 adressiert. Aber es gibt mit ziemlicher Sicherheit Speicher an derselben Adresse.

Hier ist ein wirklich einfaches Diagramm:

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

Es ist klar, dass die CPU entweder mit dem Speicher oder dem E/A-Gerät kommunizieren muss, niemals mit beiden. Um zwischen den beiden zu unterscheiden, gibt eine der Steuerleitungen mit der Bezeichnung "M/# IO" an, ob die CPU mit dem Speicher (Leitung = hoch) oder einem E/A-Gerät (Leitung = niedrig) sprechen möchte.

Der IN-Befehl liest von einem E/A-Gerät, der OUT-Befehl schreibt. Wenn Sie die IN- oder OUT-Anweisungen verwenden, wird das M/# IO nicht aktiviert (niedrig gehalten), sodass der Speicher nicht reagiert und der E/A-Chip dies tut. Bei speicherorientierten Befehlen wird M/# IO aktiviert, sodass die CPU mit dem RAM kommuniziert und IO Geräte von der Kommunikation ausgeschlossen sind.

Unter bestimmten Umständen können die IO Geräte die Datenleitungen ansteuern und die RAM können sie gleichzeitig lesen. Und umgekehrt. Es heißt DMA.

Herkömmlicherweise waren serielle und Druckeranschlüsse sowie Tastatur, Maus, Temperatursensoren usw. E/A-Geräte. Scheiben waren irgendwie dazwischen; Datenübertragungen würden durch E/A-Befehle initiiert, aber der Plattencontroller würde normalerweise seine Daten direkt im Systemspeicher ablegen.

In modernen Betriebssystemen wie Windows oder Linux ist der Zugriff auf E/A-Ports vor "normalen" Benutzerprogrammen verborgen, und es gibt Schichten von Software, privilegierten Anweisungen und Treibern für den Umgang mit der Hardware. In diesem Jahrhundert beschäftigen sich die meisten Programmierer nicht mit diesen Anweisungen.

82
Carl Smotricz

Beginnen Sie mit so etwas:

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

Sie lernen Anweisungen für einen sehr alten Technologie-Chip/eine Architektur. Damals, als alles außer dem Prozessorkern vom Chip war. Siehe die Adressleitungen und die Datenleitungen und es gibt eine RD-Leseleitung und eine WR-Schreibleitung und eine IO/M-Leitung?

Es gab zwei Arten von speicherbasierten Befehlen und E/A-basierten Befehlen, da adressierbare Bereiche vorhanden waren, die vom E/A IO oder Speicher leicht decodiert werden konnten.

Denken Sie daran, Sie hatten 74LSxx-Klebelogik, viele Drähte und viele Chips, um einen Speicher mit dem Prozessor zu verbinden. Und Erinnerung war nur diese Erinnerung, große, teure Chips. Wenn Sie ein Peripheriegerät hatten, das irgendetwas Nützliches tun musste, hatten Sie auch Steuerregister, der Speicher könnte aus Pixeldaten bestehen, aber irgendwo mussten Sie die horizontalen und vertikalen Abtasttaktgrenzen einstellen, dies könnten einzelne 74LSxx-Latches sein, NICHT Speicher mit I/O zugeordnete E/A, die auf beiden Klebelogiken gespeichert sind und aus Sicht des Programmierers sehr sinnvoll sind. Außerdem wurde vermieden, die Segmentregister zu ändern, um das 64-KB-Speicherfenster usw. auszurichten. Der Speicheradressraum war eine heilige Ressource, besonders wenn Sie wollte Ihre Adressdecodierung auf ein paar Bits beschränken, da Sie alle paar Bits eine Reihe von Chips und Drähten kosten.

Wie Big- und Little-Endian-Speicher war I/O gegen I/O ein religiöser Krieg. Und einige der Antworten, die Sie auf Ihre Frage erhalten, werden die starken Meinungen widerspiegeln, die es heute noch in den Menschen gibt, die es gelebt haben. Die Realität ist, dass jeder Chip auf dem Markt heutzutage mehrere Busse für verschiedene Dinge hat. Sie hängen Ihre Echtzeituhr nicht mit einem Adressdecoder an den DDR-Speicherbus. Einige haben sogar noch völlig getrennte Instruktions- und Datenbusse. In gewisser Weise hat Intel den Krieg für das Konzept der getrennten Adressräume für verschiedene Klassen von Dingen gewonnen, obwohl der Begriff E/A-Port böse und schlecht ist und noch 20 bis 30 Jahre nicht geäußert werden sollte. Sie brauchen Leute in meinem Alter, die es gelebt haben, um in Rente zu gehen oder weg zu sein, bevor der Krieg wirklich vorbei ist. Auch der Begriff "Memory Mapped I/O" gehört der Vergangenheit an.

Das ist wirklich alles, was es jemals gab, ein einzelnes Adressdecodierungsbit auf der Außenseite des Intel-Chips, das durch die Verwendung spezifischer Anweisungen gesteuert wurde. Verwenden Sie einen Befehlssatz, bei dem das Bit aktiviert war. Verwenden Sie einen Befehlssatz, bei dem das Bit deaktiviert war. Möchten Sie etwas Interessantes sehen, schauen Sie sich den Befehlssatz für die xmos xcore-Prozessoren an. Sie haben viele Dinge, die Befehle sind, anstatt Speicherregister zuzuordnen. Es bringt dieses E/A-Zuordnungs-E/A-Ding auf eine ganz neue Ebene.

Wo es verwendet wurde, ist, wie ich oben beschrieben habe, würden Sie Dinge setzen, die Sinn machten, und Sie könnten es sich leisten, Speicheradressraum für wie Videopixel, Netzwerkpaketspeicher (vielleicht), Soundkartenspeicher zu brennen (auch nicht das, aber Sie könnten es haben) ) usw. Und die Steuerregister, der Adressraum im Verhältnis zu den Daten, waren sehr klein, vielleicht nur ein paar Register, wurden decodiert und im E/A-Raum verwendet. Die offensichtlichen sind/waren serielle und parallele Ports, die wenig oder gar keinen Speicher hatten. Möglicherweise hatten Sie ein kleines FIFO an der seriellen Schnittstelle, wenn überhaupt.

Da der Adressraum knapp war, war es nicht ungewöhnlich und es ist immer noch zu sehen, dass Speicher hinter zwei Registern, einem Adressregister und einem Datenregister, verborgen ist. Dieser Speicher ist nur über diese beiden Register verfügbar, er ist nicht speicherabgebildet. Sie schreiben also den Offset in diesen verborgenen Speicher im Adressregister und lesen oder schreiben das Datenregister, um auf den Inhalt des Speichers zuzugreifen. Nun, da Intel die rep-Anweisung hatte und Sie sie mit insb/w outsb/w kombinieren konnten, würde der Hardware-Decoder (wenn Sie nette/freundliche Hardware-Leute hatten) die Adresse automatisch inkrementieren, wenn Sie einen I/O-Zyklus machten. Sie könnten also die Startadresse in das Adressregister schreiben und eine Wiederholung durchführen, und ohne brennende Abruf- und Dekodierungs-Taktzyklen im Prozessor und auf dem Speicherbus könnten Sie Daten ziemlich schnell in das Peripheriegerät hinein- oder herausbewegen. Dank der modernen Superskalarprozessoren mit Abrufen auf der Basis von Verzweigungsvorhersagen wird diese Art von Dingen jetzt als Konstruktionsfehler angesehen. Ihre Hardware kann jederzeit Lesevorgänge ausführen, die nichts mit der Ausführung von Code zu tun haben. Aus diesem Grund sollten Sie NIEMALS eine automatische Inkrementierung vornehmen Adressieren oder Löschen von Bits in einem Statusregister oder Ändern von Elementen als Ergebnis des Lesens einer Adresse.

Die Schutzmechanismen, die in den 386 und höher eingebaut sind, machen den Zugriff auf E/A vom Benutzerbereich aus sehr einfach. Abhängig davon, was Sie beruflich machen, was Ihr Unternehmen produziert usw. Sie können auf jeden Fall die In- und Out-Anweisungsfamilie aus dem Benutzerbereich (Anwendungsprogramme unter Windows und Linux usw.) oder dem Kernel-/Treiberbereich verwenden Wahl. Sie können auch lustige Dinge tun, wie die virtuelle Maschine nutzen und I/O-Anweisungen verwenden, um mit Treibern zu sprechen, aber das würde wahrscheinlich die Leute sowohl in der Windows- als auch in der Linux-Welt verärgern, dass Treiber/App es nicht sehr weit bringen würden. Die anderen Plakate sind insofern korrekt, als Sie diese Anweisungen wahrscheinlich nie verwenden müssen, es sei denn, Sie schreiben Treiber, und Sie werden wahrscheinlich niemals Treiber für Geräte schreiben, die E/A-zugeordnete E/A verwenden, weil Sie wissen, dass Treiber für diese älteren Geräte wurden bereits geschrieben. Moderne Designs haben auf jeden Fall E/A, aber aus Sicht des Programmierers ist der gesamte Speicher zugeordnet und verwendet Speicherbefehle, keine E/A-Befehle. Jetzt ist die andere Seite, wenn dies DOS ist, definitiv nicht tot, je nachdem, wo Sie Abstimmungsmaschinen oder Gaspumpen oder Registrierkassen oder eine lange Liste von DOS-basierten Geräten bauen. Wenn Sie an einem Ort arbeiten, an dem PCs oder PC-basierte Peripheriegeräte oder Motherboards gebaut werden, werden DOS-basierte Tools immer noch häufig zum Testen und Verteilen von BIOS-Updates und ähnlichen Dingen verwendet. Ich stoße immer noch auf Situationen, in denen ich Code aus einem aktuellen DOS-Testprogramm nehmen muss, um einen Linux-Treiber zu schreiben. Genau wie nicht jeder, der in der NFL Fußballspiele werfen oder fangen kann, machen prozentual sehr wenige Software-Arbeiten, die solche Dinge beinhalten. Sie können also mit Sicherheit sagen, dass diese Anweisungen, die Sie gefunden haben, wahrscheinlich nicht mehr für Sie sein werden als eine Geschichtsstunde.

22
old_timer

Nennen Sie einige praktische Beispiele.

Zuerst lernen Sie, wie man:

  • erstellen Sie ein minimales Bootloader-Betriebssystem und führen Sie es auf QEMU und realer Hardware aus, wie hier erläutert: https://stackoverflow.com/a/32483545/895245
  • machen Sie einige BIOS-Aufrufe, um ein schnelles und unsauberes IO durchzuführen

Dann:

  1. PS/2-Controller : Ermittelt die Scancode-ID des letzten auf der Tastatur eingegebenen Zeichens zu al:

    in $0x60, %al
    

    Minimalbeispiel

  2. Real Time Clock (RTC) : Wandzeit mit Sekundenangabe ermitteln:

    .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
    

    Minimalbeispiel

  3. Programmierbarer Intervall-Timer (PIT) : Erzeugt alle 0x1234 / 1193181 Sekunden eine Interrupt-Nummer 8:

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

    Minimalbeispiel

    A Verwendung des Linux-Kernels 4.2 . Da sind andere.

Getestet auf: QEMU 2.0.0 Ubuntu 14.04 und realer Hardware Lenovo ThinkPad T400.

So finden Sie die Portnummern: Gibt es eine Spezifikation für die x86-E/A-Portzuweisung?

https://github.com/torvalds/linux/blob/v4.2/Arch/x86/kernel/setup.c#L646 enthält eine Liste mit vielen vom Linux-Kernel verwendeten Ports.

Andere Architekturen

Nicht alle Architekturen verfügen über solche IO dedizierten Anweisungen.

In ARM wird zum Beispiel IO einfach durch Schreiben auf von der Hardware festgelegte Speicheradressen erstellt.

Ich denke, das ist was https://stackoverflow.com/a/3221839/895245 bedeutet durch "Speicher zugeordnete E/A vs E/A zugeordnete E/A".

Aus Sicht des Programmierers bevorzuge ich den ARM - Weg, da für die Ausführung von IO - Befehlen bereits magische Adressen erforderlich sind und bei der 64-Bit-Adressierung enorme ungenutzte Adressräume vorhanden sind.

Siehe https://stackoverflow.com/a/40063032/895245 für ein konkretes ARM Beispiel.

Auf Hardwareebene sind in den meisten Mikroprozessoren nur wenige oder gar keine E/A-Funktionen integriert. Einige Prozessoren verfügen über einen oder mehrere Pins, die mithilfe spezieller Anweisungen ein- und ausgeschaltet werden können, und/oder einen oder mehrere Pins, die mithilfe spezieller Anweisungen getestet werden können Verzweigungsanweisungen, aber solche Funktionen sind selten. Stattdessen werden E/A-Vorgänge normalerweise durch Verkabelung des Systems ausgeführt, sodass der Zugriff auf einen Bereich von Speicheradressen einen gewissen Effekt auslöst, oder durch Einfügen von "In" - und "Out" -Anweisungen, die sich wie Speicherlade-/Speicheroperationen verhalten, mit Ausnahme eines speziellen Signals wird ausgegeben und sagt: "Dies ist eine E/A-Operation anstelle einer Speicheroperation." In den Tagen von 16-Bit-Prozessoren gab es einige echte Vorteile, wenn man sich auf Ein-/Ausgabeanweisungen spezialisierte. Heutzutage sind solche Vorteile weitgehend umstritten, da man einfach einen großen Teil seines Adressraums der E/A zuweisen und immer noch viel Speicherplatz zur Verfügung hat.

Da ein Programm ein erhebliches Chaos auf einem System anrichten kann, wenn E/A-Anweisungen nicht ordnungsgemäß ausgeführt werden (z. B. können solche Anweisungen willkürliche Plattenzugriffe ausführen), verbieten alle modernen Betriebssysteme die Verwendung solcher Anweisungen in Code auf Benutzerebene. Bei einigen Systemen können solche Anweisungen möglicherweise virtualisiert werden. Wenn der Benutzercode beispielsweise versucht, auf die E/A-Ports 0x3D4 und 0x3D5 zu schreiben, wird dies möglicherweise von einem Betriebssystem als Versuch interpretiert, einige Steuerregister für die Videosteuerung so einzurichten, dass der blinkende Cursor bewegt wird. Jedes Mal, wenn das Benutzerprogramm den OUT-Befehl ausführte, übernahm das Betriebssystem, sah, was das Benutzerprogramm versuchte, und handelte angemessen.

In den allermeisten Fällen wäre es effizienter, die entsprechende Aktion direkt vom Betriebssystem anzufordern, selbst wenn das Betriebssystem einen IN- oder OUT-Befehl in etwas Passendes übersetzen würde.

4
supercat

Wenn Sie kein Betriebssystem schreiben, werden Sie diese Anweisungen niemals verwenden.

x86-basierte Computer verfügen über zwei unabhängige Adressräume - den Ihnen bekannten Speicheradressraum und dann den E/A-Adressraum. Die E/A-Portadressen sind nur 16 Bit breit und verweisen auf Low-Level-Register und andere Low-Level-Widgets, die Teil eines E/A-Geräts sind, z. B. einen seriellen oder parallelen Port, einen Festplattencontroller usw.

Es gibt keine praktischen Beispiele, da diese nur von Gerätetreibern und Betriebssystemen verwendet werden.

3
John Saunders

Es ist ein bisschen mehr Trick als das. Es wird nicht nur ein separater Adressraum von 64 KB auf die gleichen Drähte mit einem zusätzlichen Adressbus/Chip-Auswahl-Pin gemultiplext. Intel 8086 und 8088 und ihre Klone multiplexen auch den Datenbus und den Adressbus. alles sehr ungewöhnliche Sachen in CPUs. Die Datenblätter sind voll mit 'Minimum/Maximum'-Konfigurationselementen und allen Latch-Registern, die Sie benötigen, um sich' normal 'zu verhalten. Auf der anderen Seite spart es eine Menge und Gates und 'oder' Gates in der Adressdecodierung und 64kb sollten 'genug I/O-Ports für alle sein': P.

Beachten Sie außerdem Folgendes für alle, die nur Treiberentwickler sind: Abgesehen davon, dass Intel-kompatible Chips nicht nur für PCs verwendet werden (sie waren ursprünglich nie für den Einsatz in IBM-PCs vorgesehen - IBM hat sie nur deshalb verwendet, weil sie es sind waren billig und bereits auf dem Markt), verkauft Intel auch Mikrocontroller mit dem gleichen Befehlssatz (Intel Quark) und es gibt viele "Systeme auf einem Chip" von anderen Anbietern mit dem gleichen Befehlssatz. Ich glaube nicht, dass Sie es schaffen werden, irgendetwas mit getrenntem 'User Space', 'Kernel' und 'Treibern' auf 32 KB zu stopfen :). Für die meisten Dinge sind solche komplexen "Betriebssysteme" weder optimal noch erwünscht. Das Bilden einiger UDP-Pakete in RAM und das anschließende Speichern in einem Ringpuffer sowie das Ausführen einiger Relays per Klick erfordert keinen 30-MB-Kernel und eine Ladezeit von 10 Sekunden. Dies ist im Grunde die beste Wahl Für den Fall, dass ein PIC-Mikrocontroller nicht gerade ausreicht, Sie aber keinen ganzen Industrie-PC benötigen, werden die E/A-Anweisungen für die Ports häufig verwendet und nicht nur von den Treibern für größere Betriebssysteme.

CPU über IO-Ports mit einigen externen Controllern verbunden. Auf alten x86-PCs arbeite ich mit einem Diskettenlaufwerk, das E/A-Ports verwendet. Wenn Sie wissen, welche Befehle den Gerätecontroller akzeptieren, können Sie ihn über seine Ports programmieren.

In der modernen Welt werden Sie niemals Anweisungen für Ports verwenden. Ausnahme, wenn Sie Treiberentwickler sind (oder sein werden).

es gibt detailliertere Informationen zu E/A-Ports http://webster.cs.ucr.edu/AoA/DOS/ch03/CH03-6.html#HEADING6-1

1
Evgen Bodunov