web-dev-qa-db-ja.com

x86のIN&OUT命令は何に使用されますか?

「Linuxカーネルの理解」の本を読んでいる間、私はこれらを指示IN&OUTに奨励しました。リファレンスマニュアルを参照しました。

5.1.9 I/O命令

これらの命令は、プロセッサのI/Oポートとレジスタまたはメモリ間でデータを移動します。

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

私はいくつかのものを手に入れませんでした:

  1. 「プロセッサのI/Oポート」。彼らは何ですか?なぜこれらのポートに「文字列」を読み書きしたいのですか?
  2. 私は、これらの指示を使用する必要がある場面を決して奨励しませんでした。これらはいつ必要になりますか?
  3. いくつかの実用的な例を挙げてください。
50
claws

メモリアドレス指定の仕組みを知っていますか?アドレスバス、データバス、およびいくつかの制御ラインがあります。 CPUはメモリの1バイト(または開始バイト)のアドレスをアドレスバスに配置し、次にREAD信号を発生させ、いくつかのRAMチップはそのアドレスのメモリの内容をデータバス上の個々の行(バイトのビットに対応)を上下させるこれは、RAMとROMの両方で機能します。

ただし、I/Oデバイスもあります。シリアルポートとパラレルポート、PCの小さな内蔵スピーカーのドライバー、ディスクコントローラー、サウンドチップなどです。また、これらのデバイスは読み取りおよび書き込みも行われます。また、CPUが正しいデバイスにアクセスし、(通常)特定のデバイス内の正しいデータの場所にアクセスするようにアドレスを指定する必要があります。

ほとんどの「現代の」PCに見られるxxx86シリーズを含む一部のCPUモデルでは、I/Oデバイスはメモリとアドレス空間を共有します。 RAM/ROMとIOデバイスは同じアドレス、データ、および制御ラインに接続されます。たとえば、COM1のシリアルポートは(16進数)03F8からアドレス指定されます。しかし、ほぼ確実にメモリがあります。同じアドレスで。

これは本当に簡単な図です:

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

明らかに、CPUはメモリまたはI/Oデバイスのいずれかと通信する必要があり、両方と通信する必要はありません。この2つを区別するために、「M /#IO」と呼ばれる制御ラインの1つは、CPUがメモリ(line = high)またはI/Oデバイス(line = low)と通信するかどうかをアサートします。

IN命令はI/Oデバイスから読み取り、OUTは書き込みます。 INまたはOUT命令を使用すると、M /#IOはアサートされない(ローに保持される)ため、メモリは応答せず、I/Oチップは応答します。メモリ指向の命令の場合、M /#IOがアサートされるため、CPUはRAMと通信し、IOデバイスは通信を停止します。

特定の条件下では、IOデバイスはデータラインを駆動でき、RAMはそれらを同時に読み取ることができます。逆もまた同様です。DMAと呼ばれます。

従来、シリアルポートとプリンターポート、キーボード、マウス、温度センサーなどはI/Oデバイスでした。ディスクはその中間にありました。データ転送はI/Oコマンドによって開始されますが、ディスクコントローラーは通常、システムメモリにデータを直接デポジットします。

WindowsやLinuxなどの最新のオペレーティングシステムでは、I/Oポートへのアクセスは「通常の」ユーザープログラムから隠されており、ハードウェアを処理するソフトウェア、特権命令、およびドライバーのレイヤーがあります。そのため、今世紀では、ほとんどのプログラマーはこれらの命令を処理しません。

82
Carl Smotricz

次のようなものから始めます。

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

非常に古いテクノロジーチップ/アーキテクチャの手順を学習しています。プロセッサコア以外のすべてがオフチップであったときに戻ります。アドレス行とデータ行を参照してください。RD読み取り行とWR書き込み行とIO/M行がありますか?

アドレス可能なスペースがあり、IO/M IOまたはMemoryによって簡単にデコードされるため、メモリベースとI/Oベースの2種類の命令がありました。

メモリをプロセッサに接続するための74LSxxグルーロジック、大量のワイヤ、および大量のチップがあったことを思い出してください。そして、メモリはまさにそのメモリであり、大きな高価なチップでした。必要な周辺機器が制御レジスタも持っているのに役立つ何かをする場合、メモリはピクセルデータかもしれませんが、水平および垂直スキャンクロック制限を設定する必要がある場合、これらはメモリではなく、個々の74LSxxラッチであるかもしれません/ OマップされたI/Oは両方のグルーロジックに保存され、プログラマの観点からは非常に理にかなっています。また、セグメントレジスタを変更して64Kメモリウィンドウを狙うなども避けました。メモリアドレススペースは、特に数ビットごとに多くのチップとワイヤが必要になるため、アドレスのデコードを数ビットに制限したかったのです。

大小のエンディアンメモリマップI/O対I/OマップI/Oのように、宗教戦争でした。そして、あなたの質問に対してあなたが見ようとしている回答のいくつかは、それを生きた人々に今日まだ存在している強い意見を反映するでしょう。現実には、今日市場に出回っているすべてのチップにはさまざまな目的で複数のバスがあり、アドレスデコーダを使用してリアルタイムクロックをddrメモリバスから外さないでください。命令バスとデータバスが完全に分離されているものもあります。ある意味で、I/Oポートという用語は悪であり、20年から30年は発言されるべきではありませんが、Intelはさまざまなクラスの個別のアドレススペースの概念に対する戦争で勝ちました。戦争が本当に終わる前に引退するか、去るには、私と同年齢の人々が必要です。メモリマップドI/Oという用語でさえ、過去のものです。

これが本当にすべて、つまり特定の命令の使用によって制御されるインテルチップの外側にある単一のアドレスデコードビットです。ビットがオンであった命令のセットを1つ使用し、ビットがオフであった命令のセットを1つ使用します。メモリマップドレジスタではなく命令であるものがたくさんあるxmos xcoreプロセッサの命令セットを見てください。このI/OマップI/Oをまったく新しいレベルに引き上げます。

上記で説明したように、それが使用された場所には、理にかなったものを入れて、ビデオピクセル、ネットワークパケットメモリ(おそらく)、サウンドカードメモリなどのメモリアドレススペースを書き込む余裕があります)など。また、データに関連するアドレス空間の制御レジスタは非常に小さく、たぶん数個のレジスタがデコードされ、I/O空間で使用されました。明白なものは、ストレージがほとんどないシリアルポートとパラレルポートでした。シリアルポートに小さなfifoがあったとしても、それがあったかもしれません。

アドレス空間が不足していたため、メモリはアドレスレジスタとデータレジスタの2つのレジスタの後ろに隠れていることが珍しくなく、今日でも見られます。このメモリはこれら2つのレジスタを介してのみ使用でき、メモリはマップされません。したがって、アドレスレジスタ内のこの隠しメモリにオフセットを書き込み、メモリの内容にアクセスするためにデータレジスタを読み書きします。インテルにはrep命令があり、insb/w outsb/wと組み合わせることができるため、I/Oサイクルを実行するたびに、ハードウェアデコーダー(ニース/フレンドリーなハードウェア関係者があなたと協力している場合)がアドレスを自動インクリメントします。したがって、アドレスレジスタに開始アドレスを書き込んでrep outswを実行すると、プロセッサとメモリバスでフェッチとデコードのクロックサイクルを燃やすことなく、周辺機器との間でデータを非常に高速に移動できます。この種のことは現在、分岐予測に基づいたフェッチを備えた最新のスーパースカラープロセッサのおかげで設計上の欠陥と見なされます。ハードウェアはコードの実行とは無関係の読み取りをいつでも経験できるため、決して自動インクリメントしないでください。ステータスレジスタのビットをアドレス指定またはクリアするか、アドレスへの読み取りの結果として何かを変更します。

386に組み込まれている保護メカニズムと現在では、実際にユーザー空間からI/Oに簡単にアクセスできます。あなたが生計のために何をするか、あなたの会社が何を生産するかなどに依存します。ユーザースペース(WindowsやLinuxなどのアプリケーションプログラム)またはカーネル/ドライバースペースからの命令ファミリーを使用することができます。選択。また、仮想マシンを活用してI/O命令を使用してドライバーと通信するなどの楽しいこともできますが、WindowsとLinuxの両方の世界の人々を怒らせるでしょう。他のポスターは、ドライバーを作成している場合を除き、これらの手順を使用する必要はほとんどないという点で正しいです。また、I/OマップI/Oを使用しているデバイスのドライバーを作成することはないでしょう。これらのレガシーデバイス用のドライバはすでに作成されています。最新のデザインはほとんど間違いなくI/Oを備えていますが、それはすべてメモリマッピングされ(プログラマの観点から)、I/O命令ではなくメモリ命令を使用します。これがDOSである場合、投票機、ガソリンポンプ、キャッシュレジスタ、またはDOSベースの機器の長いリストをどこに構築するかに応じて、他の側は間違いなく死んでいません。実際、PCまたはPCベースの周辺機器またはマザーボードを構築する場所で作業している場合、DOSベースのツールは、BIOSアップデートなどのテストおよび配布に依然として広く使用されています。 Linuxドライバを書くために現在のDOSテストプログラムからコードを取得しなければならない状況にまだ出くわします。 NFLでサッカーの試合を投げたりキャッチしたりできる人が全員いないように、この種のことを伴うソフトウェア作業を行う割合は非常に少ない。ですから、あなたが見つけたこれらの指示は、歴史の教訓以上のものではない可能性が高いと言うことは、まだ安全です。

22
old_timer

実用的な例を挙げます。

まず、次の方法を学びます。

  • ここで説明したように、最小限のブートローダーOSを作成し、QEMUと実際のハードウェアで実行します。 https://stackoverflow.com/a/32483545/895245
  • いくつかのBIOS呼び出しを行って、迅速でダーティなIOを実行する

次に:

  1. PS/2コントローラー :キーボードで最後に入力した文字のスキャンコードIDをalに取得します:

    in $0x60, %al
    

    最小限の例

  2. リアルタイムクロック(RTC) :秒の定義でウォールタイムを取得します:

    .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
    

    最小限の例

  3. プログラマブルインターバルタイマー(PIT)0x1234 / 1193181秒ごとに1つの割り込み番号8を生成します。

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

    最小限の例

    A Linuxカーネル4.2の使用 。他にもあります。

テスト済み:QEMU 2.0.0 Ubuntu 14.04、および実際のハードウェアLenovo ThinkPad T400。

ポート番号を見つける方法: x86 I/Oポート割り当ての仕様はありますか?

https://github.com/torvalds/linux/blob/v4.2/Arch/x86/kernel/setup.c#L646 には、Linuxカーネルで使用される多くのポートのリストがあります。

その他のアーキテクチャ

すべてのアーキテクチャにそのようなIO専用命令があるわけではありません。

ARMでは、IOは、マジックハードウェアで定義されたメモリアドレスに書き込むだけで実行されます。

私はこれが https://stackoverflow.com/a/3221839/895245 が「メモリマップドI/O対I/OマップドI/O」によって意味するものだと思います。

プログラマーの観点からは、ARMの方法が好きです。なぜなら、IO命令は、動作するためにマジックアドレスが既に必要であり、64ビットアドレッシング。

具体的なARMの例については https://stackoverflow.com/a/40063032/895245 を参照してください。

ハードウェアレベルでは、ほとんどのマイクロプロセッサにはI/O機能がほとんど、またはまったく組み込まれていません。いくつかのプロセッサには、特別な命令を使用してオン/オフできる1つ以上のピンや、特別な命令を使用してテストできる1つ以上のピンがあります分岐命令ですが、そのような機能はまれです。代わりに、I/Oは通常、メモリアドレスの範囲へのアクセスが何らかの効果を引き起こすようにシステムを配線するか、メモリのロード/ストア操作と同様に動作する「in」および「out」命令を含めることによって処理されます。 「これはメモリ操作ではなくI/O操作です。」と出力されます。 16ビットプロセッサの時代には、特殊な入出力命令を使用することでいくつかの真の利点がありました。今日では、このような利点は、I/Oに自分のアドレス空間の大きなチャンクを単純に割り当てることができ、まだ十分なメモリが残っているため、ほとんど意味がありません。

I/O命令を不適切に実行することにより、プログラムがシステムに大きな混乱をもたらす可能性があるため(たとえば、そのような命令は任意のディスクアクセスを実行できます)、すべての最新のオペレーティングシステムは、ユーザーレベルのコードでそのような命令を使用することを禁止しています。一部のシステムでは、このような命令を仮想化できます。たとえば、ユーザーコードがI/Oポート0x3D4および0x3D5に書き込もうとすると、オペレーティングシステムはそれをビデオコントロールコントロールレジスタを設定して点滅カーソルを移動する試みと解釈する場合があります。ユーザープログラムがOUT命令を実行するたびに、オペレーティングシステムが引き継ぎ、ユーザープログラムが何をしようとしているかを確認し、適切に動作します。

ほとんどの場合、オペレーティングシステムがINまたはOUT命令を適切なものに変換したとしても、オペレーティングシステムに適切なアクションを直接要求する方が効率的です。

4
supercat

オペレーティングシステムを作成していない場合は、これらの指示を使用することはありません。

x86ベースのマシンには、2つの独立したアドレススペースがあります-使い慣れたメモリアドレススペースと、I/Oアドレススペースです。 I/Oポートアドレスは16ビット幅のみで、シリアルまたはパラレルポート、ディスクコントローラーなどのような、I/Oデバイスの一部である低レベルのレジスタやその他の低レベルのウィジェットを参照します。

これらはデバイスドライバーとオペレーティングシステムでのみ使用されるため、実用的な例はありません。

3
John Saunders

それよりも少し巧妙なことがあります。 「余分なアドレスバス/チップセレクトピン」を使用して、同じワイヤに64kbの個別のアドレス空間を多重化するだけではありません。 Intel 8086および8088とそのクローンは、データバスとアドレスバスも多重化します。 CPUのすべての非常に珍しいもの。データシートには「最小/最大」構成要素と、「通常」動作するために接続する必要があるすべてのラッチレジスタがいっぱいです。一方、アドレスデコードの負荷とゲートおよび「または」ゲートを節約し、64kbは「すべての人に十分なI/Oポート」である必要があります。

また、すべての「ドライバー開発者のみ」の人々のために、注意してください:PC以外のハードウェアでIntel互換チップを使用している人々のほかに(彼らはそもそもIBM PCでの使用を意図したものではありませんでした-IBMは彼らが安価ですでに市場に出回っていた)、Intelは同じ命令セット(Intel Quark)を備えたマイクロコントローラも販売しており、同じ命令セットを備えた他のサプライヤによる「システムオンチップ」もたくさんあります。私は、「ユーザー空間」「カーネル」と「ドライバー」を別々に32kbに詰め込むことはできないと思います:)。ほとんどの場合、このような複雑な「オペレーティングシステム」は最適でも望ましいものでもありません。いくつかのUDPパケットをRAMで形成し、それらをいくつかのリングバッファーに入れて、いくつかのリレーをクリッククリックするのに、30MBのカーネルと10秒のロード時間は必要ありません。基本的に最良の選択です。 PICマイクロコントローラーだけでは十分ではない場合でも、産業用PC全体は必要ないため、ポートI/O命令は、大規模なオペレーティングシステムの「ドライバー開発者」だけでなく、多く使用されます。

Ioポートを介して一部の外部コントローラーに接続されたCPU。古いx86 pcでは、I/Oポートを使用してフロッピードライブで動作します。どのコマンドがデバイスコントローラーを受け入れるかがわかっている場合は、ポートを介してプログラムできます。

現代の世界では、ports命令は使用しません。あなたがドライバー開発者である(またはドライバーになる)場合は例外です。

i/Oポートに関する詳細情報があります http://webster.cs.ucr.edu/AoA/DOS/ch03/CH03-6.html#HEADING6-1

1
Evgen Bodunov