108 votes

Quelles sont les instructions IN & OUT utilisées en x86?

J'ai rencontré ces instructions IN & OUT en lisant le livre "Understanding Linux Kernel". J'ai consulté le manuel de référence.

5.1.9 Instructions E/S

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

IN    Lire depuis un port
OUT   Écrire vers un port
INS/INSB  Chaîne d'entrée depuis un port/Chaîne d'octets d'entrée depuis un port 
INS/INSW  Chaîne d'entrée depuis un port/Chaîne de mots d'entrée depuis un port 
INS/INSD  Chaîne d'entrée depuis un port/Chaîne de double-mots d'entrée depuis un port
OUTS/OUTSB    Chaîne de sortie vers un port/Chaîne d'octets de sortie vers un port 
OUTS/OUTSW    Chaîne de sortie vers un port/Chaîne de mots de sortie vers un port 
OUTS/OUTSD    Chaîne de sortie vers un port/Chaîne de double-mots de sortie vers un port

Je n'ai pas compris quelques choses :

  1. Les "ports E/S du processeur". Qu'est-ce que c'est ? Pourquoi voudrait-on lire et écrire des "chaînes" vers et depuis ces ports ?
  2. Je n'ai jamais rencontré de scénario où j'aurais besoin d'utiliser ces instructions. Quand aurais-je besoin de les utiliser ?
  3. Donnez quelques exemples pratiques.

165voto

Carl Smotricz Points 36400

Vous savez comment fonctionne l'adressage mémoire? Il y a un bus d'adressage, un bus de données, et quelques lignes de contrôle. Le CPU met l'adresse d'un octet (ou d'un octet de départ) de la mémoire sur le bus d'adressage, puis lève le signal de lecture, et une puce de RAM doit de préférence renvoyer le contenu de la mémoire à cette adresse en élevant ou en abaissant des lignes individuelles (correspondant aux bits dans le(s) octet(s)) sur le bus de données. Cela fonctionne à la fois pour la RAM et la ROM.

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

Pour certains modèles de CPU, y compris la série xxx86 trouvée 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 périphériques d'IO sont connectés aux mêmes lignes d'adresse, de données et 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 schéma vraiment simple:

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

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

L'instruction IN lit à partir d'un périphérique d'E/S, OUT écrit. Lorsque vous utilisez les instructions IN ou OUT, le M/#IO n'est pas activé (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 activé de sorte que le CPU parle à la RAM, et les périphériques d'E/S restent en dehors de la communication.

À certaines conditions, les périphériques d'E/S peuvent piloter les lignes de données et la RAM peut les lire en même temps. Et vice versa. C'est ce qu'on appelle la DMA.

Traditionnellement, les ports série et parallèles, ainsi que le clavier, la souris, les capteurs de température, etc. étaient des périphériques d'E/S. Les disques étaient un peu entre les deux ; les transferts de données étaient initiés par des commandes d'E/S mais le contrôleur de disque déposerait généralement 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 y a des couches de logiciel, d'instructions privilégiées et de pilotes pour gérer le matériel. Ainsi, en ce siècle, la plupart des programmeurs ne travaillent pas avec ces instructions.

29voto

dwelch Points 27195

Commencez par quelque chose comme ceci :

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

Vous apprenez les instructions pour une puce/architecture très ancienne. À l'époque où tout sauf le cœur du processeur était hors de la puce. Voyez 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 l'E/S car il y avait des espaces adressables, facilement décodés par l'E/S ou la mémoire.

Rappelez-vous que vous aviez de la logique de collage 74LSxx, beaucoup de fils et beaucoup de puces pour connecter une mémoire au processeur. Et la mémoire était juste ça, de la mémoire, de grandes et coûteuses puces. Si vous aviez un périphérique qui avait besoin de faire quelque chose d'utile, vous aviez également des registres de contrôle, la mémoire pouvait être des données pixel, mais quelque part vous deviez définir les limites des horloges de balayage horizontales et verticales, celles-ci pourraient être des verrous individuels 74LSxx, PAS de mémoires, avoir une E/S mappée en E/S sauvegardée à la fois sur la logique de collage et cela avait tout simplement beaucoup de sens du point de vue d'un programmeur, cela évitait également de changer vos registres de segment pour diriger votre fenêtre mémoire 64K, etc. L'espace d'adressage mémoire était une ressource sacrée, surtout lorsque vous vouliez limiter votre décodage d'adresse à quelques bits, car chaque bit vous coûtait un certain nombre de puces et de fils.

Comme pour les oppositions entre le big et le little endian et l'E/S mappée en mémoire contre l'E/S mappée en E/S, c'était une guerre religieuse. Et certaines des réponses que vous allez voir à votre question reflèteront les fortes opinions qui existent encore aujourd'hui chez les personnes qui l'ont vécue. La réalité est que chaque puce sur le marché aujourd'hui a plusieurs bus pour diverses choses, vous ne pouvez pas connecter votre horloge en temps réel sur le bus mémoire DDR avec un décodeur d'adresse. Certains ont même encore des bus d'instructions et de données complètement séparés. En un sens, Intel a remporté la bataille pour le concept de spaces adresses séparés pour différentes classes de choses même si le terme port d'E/S est maléfique et mauvais et ne doit pas être prononcé avant encore disons 20 à 30 ans. Il faut que des gens de mon âge qui l'ont vécu soient à la retraite ou partis avant que la guerre ne soit vraiment terminée. Même le terme mémoire mappée en E/S est une chose du passé.

C'était vraiment tout ce que c'était, un simple 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, le bit était activé, utilisez un autre ensemble d'instructions, le bit était désactivé. Si vous voulez voir quelque chose d'intéressant, regardez l'ensemble 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 porte la mappée en E/S à un tout nouveau niveau.

Où cela était utilisé est comme je l'ai décrit ci-dessus, vous mettrez des choses qui ont du sens et pour lesquelles vous pouviez vous permettre de gaspiller de l'espace d'adresse mémoire comme des pixels vidéo, de la mémoire pour paquets réseau (peut-être), de la mémoire pour une carte son (enfin pas ça non plus mais vous auriez pu), etc. Et les registres de contrôle, l'espace d'adresse par rapport aux données était très petit, peut-être seulement quelques registres, étaient décodés et utilisés dans l'espace d'E/S. Les plus évidents sont/étaient les ports série et parallèle qui n'avaient presque pas de stockage, vous auriez pu avoir un petit fifo sur le port série, si quelque chose.

Comme l'espace d'adresse était rare, il n'était pas rare et est toujours visible aujourd'hui, de cacher de la mémoire derrière deux registres, un registre d'adresse et un registre de données, cette mémoire n'est disponible qu'à travers ces deux registres, elle n'est pas mappée en mémoire. donc vous écrivez le décalage dans cette mémoire cachée dans le registre d'adresse et vous lisez ou écrivez dans 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 allait (si vous aviez des gens sympas/amicaux travaillant avec vous) augmenter automatiquement l'adresse chaque fois que vous faisiez un cycle d'E/S. Ainsi, vous pouviez écrire l'adresse de départ dans le registre d'adresse et faire un rep outsw et sans brûler de cycles d'horloge de lecture et de décodage dans le processeur et sur le bus mémoire, vous pouviez déplacer des 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 fetchs basés sur des prédictions de branchements, votre matériel peut expérimenter des lectures à tout moment qui n'ont rien à voir avec l'exécution du code, en conséquence, vous NE devriez JAMAIS incrémenter automatiquement une adresse ou effacer les bits dans un registre de statut ou modifier quoi que ce soit à la suite d'une lecture à une adresse. (Note de l'éditeur : en fait, assurez-vous simplement que vos registres E/S avec des effets secondaires pour la lecture sont dans des régions/pages mémoires non cacheables. La lecture spéculative de mémoire non cacheable n'est pas autorisée dans l'ISA x86. Et ne peut jamais arriver pour les accès à l'espace d'E/S. Mais in/out sont très lents et partiellement sérialisants, et l'espace d'adresse mémoire physique n'est plus rare, donc la mémoire de périphérique est normalement simplement mappée en mémoire pour un accès efficace avec des transactions PCIe de taille complète.)

Les mécanismes de protection intégrés dans le 386 et jusqu'à aujourd'hui rendent en fait très facile l'accès à l'E/S depuis l'espace utilisateur. En fonction de votre métier, de ce que votre entreprise produit, etc. Vous pouvez certainement utiliser la famille d'instructions in et out depuis l'espace utilisateur (programmes d'application sous Windows et Linux, etc.) ou depuis l'espace kernel/pilote, c'est à vous de choisir. Vous pouvez aussi faire des choses amusantes comme profiter de la machine virtuelle et utiliser des instructions E/S pour communiquer avec les pilotes, mais cela pourrait probablement froisser les gens dans les mondes Windows et Linux, ce pilote/application n'irait pas très loin. Les autres contributeurs ont raison de dire que vous n'allez probablement jamais avoir besoin d'utiliser ces instructions à moins que vous n'écriviez des pilotes, et vous n'allez probablement jamais écrire de pilotes pour des appareils utilisant l'E/S mappée en E/S parce que vous savez... les pilotes pour ces périphériques anciens ont déjà été écrits. Les conceptions modernes ont certainement de l'E/S mais c'est tout mappé en mémoire (du point de vue des programmeurs) et utilisent des instructions mémoire et non des instructions E/S. Maintenant, l'autre côté de la chose c'est que MS-DOS n'est certainement pas mort, en fonction de l'endroit où vous travaillez vous pouvez être en train de construire des machines de vote, des pompes à essence, des caisses enregistreuses ou une longue liste d'équipements basés sur DOS. En fait, si vous travaillez quelque part qui construit des PC ou des périphériques PC ou des cartes mères, les outils basés sur DOS sont toujours largement utilisés pour les tests et la distribution de mises à jour de BIOS et autres choses similaires. Je me retrouve encore dans des situations où je dois prendre du code d'un programme de test dos actuel pour écrire un pilote sous linux. Tout comme tout le monde qui sait lancer ou attraper une balle de football ne joue pas dans la NFL, un pourcentage très faible de personnes font un travail logiciel qui implique ce genre de choses. Il est donc toujours juste de dire que ces instructions que vous avez trouvées ne seront probablement pas plus pour vous qu'une leçon d'histoire.

23voto

Ciro Santilli Points 3341

Donnez quelques exemples pratiques.

Apprenez d'abord comment :

  • créer un système d'exploitation de chargeur minimal et l'exécuter sur QEMU et un matériel réel comme je l'ai expliqué ici : https://stackoverflow.com/a/32483545/895245
  • faire quelques appels BIOS pour effectuer rapidement des E/S basiques

Ensuite :

  1. contrôleur PS/2 : obtenir l'ID du code de touche du dernier caractère tapé sur le clavier dans al :

    in $0x60, %al

    Exemple minimal

  2. Horloge temps réel (RTC) : obtenir l'heure actuelle avec la définition des secondes :

    .equ RTCaddress, 0x70
    .equ RTCdata, 0x71
    
    /* al contient les secondes. */
    mov $0, %al
    out %al, $RTCaddress
    in $RTCdata, %al
    
    /* al contient les minutes. */
    mov $0x02, %al
    out %al, $RTCaddress
    in $RTCdata, %al
    
    /* al contient l'heure. */
    mov $0x04, %al
    out %al, $RTCaddress

    Exemple minimal

  3. Programmable Interval Timer (PIT) : générer une interruption numéro 8 toutes les 0x1234 / 1193181 secondes :

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

    Exemple minimal

    Un exemple d'utilisation du noyau Linux 4.2. Il y en a d'autres.

Testé sur : QEMU 2.0.0 Ubuntu 14.04 et un Lenovo ThinkPad T400 matériel réel.

Comment trouver les numéros de port : Y a-t-il une spécification de l'attribution des ports d'E/S x86 ?

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

Autres architectures

Toutes les architectures n'ont pas de telles instructions d'E/S dédiées.

En ARM par exemple, les E/S se font simplement en écrivant à des adresses mémoire matérielles magiques définies.

Je pense que c'est ce que https://stackoverflow.com/a/3221839/895245 veut dire par "E/S cartographiées en mémoire par rapport aux E/S cartographiées en E/S".

D'un point de vue du programmeur, je préfère la méthode ARM, puisque les instructions E/S ont déjà besoin d'adresses magiques pour fonctionner, et nous avons d'immenses espaces d'adresses inutilisées en adressage 64 bits.

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

6voto

supercat Points 25534

Au niveau matériel, la plupart des microprocesseurs ont peu ou pas de capacité d'E/S intégrée. Quelques processeurs ont un ou plusieurs broches qui peuvent être allumées et éteintes à l'aide d'instructions spéciales, et/ou un ou plusieurs broches qui peuvent être testées à l'aide d'instructions de branchement spéciales, mais de telles fonctionnalités sont rares. Au lieu de cela, l'E/S est généralement gérée 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/stocakge de mémoire sauf qu'un signal spécial est émis indiquant "C'est une opération E/S au lieu d'une opération mémoire." À l'époque des processeurs 16 bits, il y avait vraiment des avantages à avoir des instructions in/out spécialisées. De nos jours, de tels avantages sont largement discutables puisque l'on pourrait simplement allouer une grande partie de notre espace d'adressage à l'E/S et avoir encore beaucoup de mémoire restante.

Comme un programme pourrait causer d'importants dégâts sur un système en exécutant de manière inappropriée des instructions E/S (par exemple, de telles instructions pourraient effectuer des accès disque arbitraires), tous les systèmes d'exploitation modernes interdisent l'utilisation de telles instructions dans le code au niveau utilisateur. Certains systèmes peuvent autoriser la virtualisation de telles instructions; si du code utilisateur tente d'écrire dans les ports I/O 0x3D4 et 0x3D5, par exemple, un système d'exploitation pourrait interpréter cela comme une tentative de régler certains registres 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 prendrait le relais, verrait ce que le programme utilisateur essayait de faire, et agirait de manière appropriée.

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

4voto

Il y a un peu plus de subtilité que cela. Ce n'est pas juste la multiplication d'un espace d'adresse séparé de 64 ko sur les mêmes fils avec un 'bus d'adresse supplémentaire/une broche de sélection de puce'. Les Intel 8086, 8088 et leurs clones multiplexent également le bus de données et le bus d'adresse ; des choses très peu communes dans les CPU. Les fiches techniques regorgent de configurations 'minimum/maximum' et de tous les registres de verrouillage que vous devez connecter pour le faire se comporter 'normalement'. D'un autre côté, cela économise un tas de portes 'et' et de portes 'ou' dans le décodage d'adresse et 64ko devraient être 'suffisants en termes de ports d'E/S pour tout le monde' :P.

Aussi, pour tous ceux qui sont uniquement des 'développeurs de pilotes', prenez note : en plus des gens utilisant des puces compatibles Intel dans d'autres matériels que des PC (elles n'étaient jamais vraiment destinées à être utilisées dans l'IBM PC au départ - 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 existe de nombreux 'systèmes sur une puce' d'autres fournisseurs avec le même jeu d'instructions également. Je ne pense pas que vous réussirez à tout faire tenir dans 32 ko avec des espaces d'utilisateurs séparés, un 'noyau' et des 'pilotes' :). Pour la plupart des choses, de tels 'systèmes d'exploitation' complexes ne sont ni optimaux ni souhaités. Former quelques paquets UDP dans la RAM, les placer dans un tampon circulaire et faire cliqueter quelques relais ne nécessite pas un noyau de 30 mo et un temps de chargement de 10 secondes, vous savez. C'est essentiellement le meilleur choix au cas où un microcontrôleur PIC ne serait juste pas suffisant mais que vous ne vouliez pas un PC industriel complet. Ainsi, les instructions d'E/S portuaire sont beaucoup utilisées et pas seulement par les 'développeurs de pilotes' pour les plus grands systèmes d'exploitation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X