web-dev-qa-db-fra.com

À quoi servent les instructions IN & OUT dans x86?

J'ai rencontré ces instructions dans les instructions IN OUT en lisant le livre "Comprendre le noyau Linux". J'ai recherché le manuel de référence.

5.1.9 Instructions d'E/S

Ces instructions déplacent les données entre les ports d'E/S du processeur et un registre ou une mémoire.

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

Je n'ai pas obtenu peu de choses:

  1. "ports d'E/S du processeur". Que sont-ils? Pourquoi voudrions-nous lire des "chaînes" d'écriture depuis ces ports?
  2. Je n'ai jamais rencontré un scénario où j'ai besoin d'utiliser ces instructions. Quand en aurais-je besoin?
  3. Donnez quelques exemples pratiques.
50
claws

Vous savez comment fonctionne l'adressage mémoire? Il y a un bus d'adresses, un bus de données et certaines lignes de contrôle. Le CPU place l'adresse d'un octet (ou d'un octet de début) de mémoire sur le bus d'adresse, puis lève le signal READ, et une puce RAM retourne, espérons-le, le contenu de la mémoire à cette adresse par augmenter ou diminuer les lignes individuelles (correspondant aux bits dans le (s) octet (s)) sur le bus de données. Cela fonctionne à la fois pour RAM et ROM).

Mais il y a aussi des périphériques d'E/S: ports série et parallèle, le pilote pour le petit haut-parleur interne d'un PC, des contrôleurs de disque, des puces audio, etc. Et ces appareils sont également lus et écrits. Ils doivent également être adressés afin que le CPU accède au bon périphérique et (généralement) à l'emplacement correct des données au sein d'un périphérique donné.

Pour certains modèles de CPU, y compris la série xxx86 que l'on trouve dans la plupart des PC "modernes", les périphériques d'E/S partagent l'espace d'adressage avec la mémoire. La RAM/ROM et les appareils IO sont connectés à la même adresse, aux mêmes données et aux mêmes lignes de contrôle. Par exemple, le port série pour COM1 est adressé à partir de (hex) 03F8. Mais il y a presque certainement de la mémoire à la même adresse.

Voici un diagramme très simple:

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

De toute évidence, le processeur doit parler à la mémoire ou au périphérique d'E/S, jamais aux deux. Pour faire la distinction entre les deux, l'une des lignes de commande appelée "M/# IO" indique si la CPU veut parler à la mémoire (ligne = haut) ou à un périphérique d'E/S (ligne = bas).

L'instruction IN lit à partir d'un périphérique d'E/S, écrit OUT. Lorsque vous utilisez les instructions IN ou OUT, le M/# IO n'est pas affirmé (maintenu bas), donc la mémoire ne répond pas et la puce d'E/S le fait. Pour les instructions orientées mémoire, M/# IO est affirmé afin que le CPU parle à la RAM et que les périphériques IO restent en dehors de la communication).

Dans certaines conditions, les périphériques IO peuvent piloter les lignes de données et les RAM peuvent les lire en même temps. Et vice versa. Cela s'appelle DMA.

Traditionnellement, les ports série et imprimante, ainsi que le clavier, la souris, les capteurs de température, etc. étaient des périphériques d'E/S. Les disques étaient en quelque sorte entre les deux; les transferts de données seraient initiés par des commandes d'E/S mais le contrôleur de disque déposait généralement directement ses données dans la mémoire système.

Dans les systèmes d'exploitation modernes comme Windows ou Linux, l'accès aux ports d'E/S est caché aux programmes utilisateur "normaux", et il existe des couches de logiciels, des instructions privilégiées et des pilotes pour gérer le matériel. Donc, au cours de ce siècle, la plupart des programmeurs ne traitent pas ces instructions.

82
Carl Smotricz

Commencez avec quelque chose comme ça:

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

Vous apprenez des instructions pour une architecture/puce technologique très ancienne. Retour quand tout sauf le cœur du processeur était hors puce. Voir les lignes d'adresse et les lignes de données et il y a une ligne de lecture RD et une ligne d'écriture WR et une ligne IO/M?

Il y avait deux types d'instructions basées sur la mémoire et basées sur les E/S car il y avait des espaces adressables, facilement décodés par l'IO/M IO ou Memory.

N'oubliez pas que vous aviez une logique de colle 74LSxx, beaucoup de fils et beaucoup de puces pour connecter une mémoire au processeur. Et la mémoire n'était que cette mémoire, de grosses puces chères. Si vous aviez un périphérique qui devait faire quelque chose d'utile, vous aviez également des registres de contrôle, la mémoire pouvait être des données de pixels, mais quelque part vous deviez définir les limites des horloges de balayage horizontal et vertical, il pouvait s'agir de verrous 74LSxx individuels, PAS de mémoires, ayant I Les E/S mappées/O enregistrées sur la logique de collage et tout simplement très logiques du point de vue du programmeur, elles évitaient également de modifier vos registres de segments pour viser votre fenêtre de mémoire de 64 Ko, etc. L'espace d'adressage de la mémoire était une ressource sacrée, surtout lorsque vous voulait limiter le décodage de votre adresse à quelques bits car chaque bit vous coûtait un certain nombre de puces et de fils.

Comme la grande et la petite mémoire endienne, les entrées/sorties mappées vs les entrées/sorties mappées étaient une guerre de religion. Et certaines des réponses que vous allez voir à votre question vont refléter les opinions fortes qui existent encore aujourd'hui chez les gens qui l'ont vécu. La réalité est que chaque puce sur le marché aujourd'hui a plusieurs bus pour différentes choses, vous ne suspendez pas votre horloge en temps réel au bus mémoire ddr avec un décodeur d'adresse. Certains ont même des bus d'instructions et de données complètement séparés. Dans un sens, Intel a remporté la guerre pour le concept d'espaces d'adressage séparés pour différentes classes de choses, même si le terme port d'E/S est mauvais et mauvais et ne devrait pas être prononcé pendant 20 à 30 ans. Vous avez besoin de gens de mon âge qui l'ont vécu pour être retraités ou partis avant la fin de la guerre. Même le terme d'E/S mappées en mémoire appartient au passé.

C'est vraiment tout ce qu'il a jamais été, un seul bit de décodage d'adresse à l'extérieur de la puce Intel qui était contrôlé par l'utilisation d'instructions spécifiques. Utilisez un ensemble d'instructions sur lequel le bit était activé. Utilisez un ensemble d'instructions sur lequel le bit était désactivé. Vous voulez voir quelque chose d'intéressant, regardez le jeu d'instructions pour les processeurs xmos xcore, ils ont beaucoup de choses qui sont des instructions au lieu de registres mappés en mémoire, cela prend cette chose d'E/S mappées d'E/S à un tout nouveau niveau.

Là où il a été utilisé, comme je l'ai décrit ci-dessus, vous mettriez des choses qui avaient du sens et vous pourriez vous permettre de graver l'espace d'adressage de la mémoire pour des pixels vidéo, la mémoire de paquets réseau (peut-être), la mémoire de la carte son (enfin pas ça non plus mais vous pourriez avoir ), etc. Et les registres de contrôle, l'espace d'adressage par rapport aux données était très petit, peut-être seulement quelques registres, ont été décodés et utilisés dans l'espace d'E/S. les plus évidents sont/étaient des ports série et des ports parallèles qui avaient peu ou pas de stockage, vous pourriez avoir eu un petit fifo sur le port série si quoi que ce soit.

Parce que l'espace d'adressage était rare, il n'était pas rare et on voit encore aujourd'hui que la mémoire est cachée derrière deux registres, un registre d'adresses et un registre de données, cette mémoire n'est disponible que via ces deux registres, elle n'est pas mappée en mémoire. vous écrivez donc l'offset dans cette mémoire cachée dans le registre d'adresses et vous lisez ou écrivez le registre de données pour accéder au contenu de la mémoire. Maintenant, parce qu'Intel avait l'instruction rep et que vous pouviez la combiner avec insb/w outsb/w, le décodeur matériel (si vous aviez des gens du matériel Nice/friendly travaillant avec vous) auto-incrémentait l'adresse chaque fois que vous faisiez un cycle d'E/S. Ainsi, vous pouvez écrire l'adresse de départ dans le registre d'adresses et effectuer une répétition et sans graver les cycles d'horloge de récupération et de décodage dans le processeur et sur le bus mémoire, vous pouvez déplacer les données assez rapidement dans ou hors du périphérique. Ce genre de chose est maintenant considéré comme un défaut de conception grâce aux processeurs super scalaires modernes avec des récupérations basées sur la prédiction de branche, votre matériel peut subir des lectures à tout moment qui n'ont rien à voir avec l'exécution de code, par conséquent, vous ne devez JAMAIS incrémenter automatiquement un adresse ou effacer les bits dans un registre d'état ou modifier quoi que ce soit à la suite d'une lecture à une adresse.

Les mécanismes de protection intégrés au 386 et jusqu'à aujourd'hui facilitent en fait l'accès aux E/S depuis l'espace utilisateur. Selon ce que vous faites dans la vie, ce que produit votre entreprise, etc. Vous pouvez très certainement utiliser la famille d'instructions in et out depuis l'espace utilisateur (programmes d'application sous Windows et Linux, etc.) ou l'espace noyau/pilote, c'est votre choix. Vous pouvez également faire des choses amusantes comme profiter de la machine virtuelle et utiliser des instructions d'E/S pour parler aux pilotes, mais cela ferait probablement chier les gens à la fois dans les mondes Windows et Linux, ce pilote/application ne ferait pas très loin. Les autres affiches sont correctes car vous n'aurez probablement jamais besoin d'utiliser ces instructions à moins que vous n'écriviez des pilotes, et vous n'écrirez probablement jamais de pilotes pour des périphériques utilisant des E/S mappées d'E/S parce que vous savez ... les pilotes pour ces périphériques hérités ont déjà été écrits. Les conceptions modernes ont très certainement des E/S mais elles sont toutes mappées en mémoire (du point de vue des programmeurs) et utilisent des instructions de mémoire et non des instructions d'E/S. Maintenant, l'autre côté s'il s'agit de DOS n'est certainement pas mort, selon l'endroit où vous construisez des machines à voter ou des pompes à essence ou des caisses enregistreuses ou une longue liste d'équipements basés sur DOS. En fait, si vous travaillez dans un endroit qui construit des PC ou des périphériques ou des cartes mères basés sur PC, les outils basés sur DOS sont encore largement utilisés pour tester et distribuer les mises à jour du BIOS et d'autres choses similaires. Je rencontre toujours des situations où je dois prendre le code d'un programme de test DOS actuel pour écrire un pilote Linux. Tout comme tout le monde qui ne peut pas lancer ou attraper un ballon de football joue dans la NFL, très peu de gens font des logiciels qui impliquent ce genre de choses. Il est donc toujours prudent de dire que ces instructions que vous avez trouvées ne seront probablement pas plus pour vous qu'une leçon d'histoire.

22
old_timer

Donnez quelques exemples pratiques.

Apprenez d'abord à:

Ensuite:

  1. manette PS/2 : obtenir l'ID de scancode du dernier caractère tapé sur le clavier à al:

    in $0x60, %al
    

    exemple minimal

  2. Real Time Clock (RTC) : obtenez l'heure du mur avec la définition des secondes:

    .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
    

    exemple minimal

  3. Programmable Interval Timer (PIT) : générer un numéro d'interruption 8 chaque 0x1234 / 1193181 secondes:

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

    exemple minimal

    A tilisation du noyau Linux 4.2 . Il y en a d'autres.

Testé sur: QEMU 2.0.0 Ubuntu 14.04 et le vrai matériel Lenovo ThinkPad T400.

Comment trouver les numéros de port: Existe-t-il une spécification d'attribution de port d'E/S x86?

https://github.com/torvalds/linux/blob/v4.2/Arch/x86/kernel/setup.c#L646 a une liste de nombreux ports utilisés par le noyau Linux.

Autres architectures

Toutes les architectures n'ont pas de telles instructions dédiées IO.

Dans ARM par exemple, IO se fait simplement en écrivant sur des adresses mémoire définies par le matériel magique).

Je pense que c'est ce que https://stackoverflow.com/a/3221839/895245 signifie par "E/S mappées en mémoire vs E/S mappées E/S".

Du point de vue du programmeur, je préfère la méthode ARM, car les instructions IO ont déjà besoin d'adresses magiques pour fonctionner, et nous avons d'énormes espaces d'adressage inutilisés dans 64 adressage de bits.

Voir https://stackoverflow.com/a/40063032/895245 pour un exemple concret ARM.

Au niveau matériel, la plupart des microprocesseurs ont peu ou pas de capacité d'E/S intégrée. Quelques processeurs ont une ou plusieurs broches qui peuvent être activées et désactivées à l'aide d'instructions spéciales et/ou une ou plusieurs broches qui peuvent être testées à l'aide de instructions de branchement, mais ces fonctionnalités sont rares. Au lieu de cela, les E/S sont généralement gérées en câblant le système de sorte que les accès à une plage d'adresses mémoire déclenchent un certain effet, ou en incluant des instructions "in" et "out" qui se comportent comme des opérations de chargement/stockage de mémoire, sauf qu'un signal spécial est une sortie indiquant "Il s'agit d'une opération d'E/S au lieu d'une opération de mémoire." À l'époque des processeurs 16 bits, il y avait des avantages réels à avoir des instructions d'entrée/sortie spécialisées. De nos jours, ces avantages sont en grande partie sans objet, car on pourrait simplement allouer une grande partie de l'espace d'adressage aux E/S et avoir encore beaucoup de mémoire.

Puisqu'un programme pourrait faire des ravages considérables sur un système en exécutant de manière inappropriée des instructions d'E/S (par exemple, de telles instructions pourraient effectuer des accès de disque arbitraires), tous les systèmes d'exploitation modernes interdisent l'utilisation de ces instructions dans le code au niveau de l'utilisateur. Certains systèmes peuvent permettre la virtualisation de telles instructions; si le code utilisateur essaie d'écrire sur les ports d'E/S 0x3D4 et 0x3D5, par exemple, un système d'exploitation peut interpréter cela comme une tentative de définition de certains registres de contrôle de contrôle vidéo pour déplacer le curseur clignotant. Chaque fois que le programme utilisateur exécutait l'instruction OUT, le système d'exploitation prenait le relais, voyait ce que le programme utilisateur essayait de faire et agissait de manière appropriée.

Dans la grande majorité des cas, même si le système d'exploitation traduisait une instruction IN ou OUT en quelque chose de convenable, il serait plus efficace de demander directement l'action appropriée au système d'exploitation.

4
supercat

Si vous n'écrivez pas de système d'exploitation, vous n'utiliserez jamais ces instructions.

les machines x86 ont deux espaces d'adressage indépendants: l'espace d'adressage mémoire que vous connaissez, puis l'espace d'adressage d'E/S. Les adresses des ports d'E/S n'ont qu'une largeur de 16 bits et font référence à des registres de bas niveau et à d'autres widgets de bas niveau qui font partie d'un périphérique d'E/S - quelque chose comme un port série ou parallèle, un contrôleur de disque, etc.

Il n'y a pas d'exemples pratiques car ils ne sont utilisés que par les pilotes de périphériques et les systèmes d'exploitation.

3
John Saunders

Il y a un peu plus de ruse que ça. Il ne se contente pas de multiplexer un espace d'adressage séparé de 64 Ko sur les mêmes fils avec une "broche de sélection de bus/puce d'adresse supplémentaire". Intel 8086 et 8088 et leurs clones multiplexent également le bus de données et le bus d'adresses; tous les trucs très rares dans les processeurs. Les fiches techniques sont pleines de trucs de configuration "minimum/maximum" et tous les registres de verrouillage dont vous avez besoin pour vous y connecter pour qu'il se comporte "normalement". D'un autre côté, il enregistre une charge de portes et "et" dans le décodage des adresses et 64 Ko devraient être "suffisamment de ports d'E/S pour tout le monde": P.

En outre, pour toutes ces personnes `` développeuses de pilotes uniquement '', prenez note: outre les personnes utilisant des puces compatibles Intel dans un autre matériel que des PC (elles n'ont jamais vraiment été conçues pour être utilisées dans le PC IBM - IBM les a simplement prises parce qu'elles étaient bon marché et déjà sur le marché), Intel vend également des microcontrôleurs avec le même jeu d'instructions (Intel Quark) et il y a beaucoup de `` systèmes sur puce '' par d'autres fournisseurs avec le même jeu d'instructions. Je ne pense pas que vous parviendrez à entasser quoi que ce soit avec un "espace utilisateur" séparé "noyau" et "pilotes" dans 32 Ko :). Pour la plupart des choses, ces "systèmes d'exploitation" complexes ne sont ni optimaux ni souhaités. La formation de certains paquets UDP en RAM puis les mettre dans un tampon en anneau et faire en sorte que certains relais cliquent) ne nécessite pas de noyau de 30 Mo et 10 secondes de chargement, vous savez. C'est fondamentalement le meilleur choix dans le cas où un microcontrôleur PIC ne suffit pas, mais que vous ne voulez pas un PC industriel complet. Les instructions d'E/S du port sont donc souvent utilisées et pas seulement par les "développeurs de pilotes" pour les systèmes d'exploitation plus grands.

CPU connecté à certains contrôleurs externes via des ports io. sur un vieux PC x86, je travaille avec un lecteur de disquette utilisant des ports d'E/S si vous savez quelles commandes acceptent le contrôleur de périphérique, vous pouvez le programmer via ses ports.

Dans le monde moderne, vous n'utiliserez jamais les instructions des ports. Exception si vous êtes (ou serez) développeur de pilotes.

il y a des informations plus détaillées sur les ports d'E/S http://webster.cs.ucr.edu/AoA/DOS/ch03/CH03-6.html#HEADING6-1

1
Evgen Bodunov