だから私は次のレジスタとその使用法が何であるかを知っています:
CS =コードセグメント(IPに使用)
DS =データセグメント(MOVに使用)
ES =宛先セグメント(MOVSなどに使用)
SS =スタックセグメント(SPに使用)
しかし、使用することを目的とした次のレジスタは何ですか?
FS = "ファイルセグメント"?
GS = ???
注:私は特定のオペレーティングシステムについて質問しているのではありません。CPUがそれらを何に使用することを意図していたのかについて質問しています。
だから私は次のレジスタとその使用法が何であるかを知っています:
CS =コードセグメント(IPに使用)
DS =データセグメント(MOVに使用)
ES =宛先セグメント(MOVSなどに使用)
SS =スタックセグメント(SPに使用)
しかし、使用することを目的とした次のレジスタは何ですか?
FS = "ファイルセグメント"?
GS = ???
注:私は特定のオペレーティングシステムについて質問しているのではありません。CPUがそれらを何に使用することを意図していたのかについて質問しています。
それらが意図されていたものと、WindowsおよびLinuxで使用されているものがあります。
セグメントレジスタの背後にある本来の目的は、プログラムが、独立して永続的な仮想ストアの一部となることを目的とした、メモリの多くの異なる(大きな)セグメントにアクセスできるようにすることでした。このアイデアは、ファイルを単にアドレス可能なメモリセグメントとして扱う1966年のMulticsオペレーティングシステムから採用されました。BS「ファイルを開く、レコードを書き込む、ファイルを閉じる」はなく、ダーティページのフラッシュで「この値をその仮想データセグメントに保存する」だけです。
現在の2010年のオペレーティングシステムは大きく後退しているため、「宦官」と呼ばれています。プロセススペースの単一セグメントにのみアドレス指定でき、いわゆる「フラット(IMHO鈍い)アドレススペース」を提供します。x86-32マシンのセグメントレジスタは、実際のセグメントレジスタに引き続き使用できますが、誰も気にしませんでした(前世紀、Intelの元社長であるAndy Groveは、Intelのエンジニア全員がエネルギーを費やした後、この機能を実装するための彼のお金、誰もそれを使用するつもりはなかった。行く、アンディ!)
AMDは64ビットに移行する際に、選択肢としてMulticsを排除してもかまわないと判断し(これは慈善的な解釈です。Multicsについては無知でした)、64ビットモードのセグメントレジスタの一般的な機能を無効にしました。スレッドがスレッドローカルストアにアクセスする必要があり、各スレッドには、スレッドローカルストアへのポインタ...すぐにアクセス可能なスレッド状態のどこか(たとえば、レジスタ内)...が必要でした。WindowsとLinuxはどちらも32ビットバージョンでこの目的のためにFSとGSを使用していたため(説明してくれたNickに感謝)、AMDは64ビットセグメントレジスタ(GSとFS)を基本的にこの目的にのみ使用することにしました(私はあなたがプロセススペースのどこにでもポイントさせることができます。アプリケーションコードがそれらをロードできるかどうかはわかりません)。
各スレッドのメモリマップに、そのスレッドローカルストレージ([セグメント]レジスタポインタは不要)である絶対仮想アドレス(たとえば、0-FFFなど)を持たせることは、アーキテクチャ的にはよりきれいなIMHOでした。私は1970年代に8ビットOSでこれを行いましたが、レジスターの別の大きなスタックを使用するように、非常に便利でした。
そのため、セグメントレジスタは付録のようなものになりました。それらは痕跡の目的を果たします。私たちの集団的損失に。
歴史を知らない人は、それを繰り返す運命にありません。彼らは何か面倒なことをする運命にあります。
レジスタFSとGSはセグメントレジスタです。それらにはプロセッサ定義の目的はありませんが、代わりにそれらを実行しているOSによって目的が与えられます。Windows 64ビットでは、GSレジスタはオペレーティングシステムで定義された構造を指すために使用されます。 FSスレッド固有のメモリにアクセスするためにGSOSカーネルによって一般的に使用されます。Windowsでは、GSレジスタはスレッド固有のメモリを管理するために使用されます。LinuxカーネルはGS、CPU固有のメモリにアクセスするために使用します。
FSは、Windowsプロセスのスレッド情報ブロック(TIB)を指すために使用されます。
典型的な例の1つは、コールバック関数へのポインタをに格納する (SEHFS:[0x00] )です。
GSは通常、スレッドローカルストレージ(TLS)へのポインターとして使用されます。以前に見たことがあるかもしれない1つの例は、スタックカナリア保護(stackguard)です。gccでは、次のように表示される場合があります。
mov eax,gs:0x14
mov DWORD PTR [ebp-0xc],eax
「FS」/「GS」レジスタの目的は何ですか?
デフォルトのデータセグメント(DS)を超えてデータにアクセスするだけです。ESとまったく同じです。
だから私は次のレジスタとその使用法が何であるかを知っています:
[...]
まあ、ほとんどですが、DSは「一部の」データセグメントではなく、デフォルトのデータセグメントです。デフォルトですべての操作が行われる場所(* 1)。これは、すべてのデフォルト変数が配置されている場所です-基本的dataにとbss。これは、x86コードがかなりコンパクトである理由の一部です。最も頻繁にアクセスされるすべての重要なデータ(およびコードとスタック)は、16ビットの短縮距離内にあります。
ESは、DSの64 KiBを超えるすべて(* 2)にアクセスするために使用されます。ワードプロセッサのテキスト、スプレッドシートのセル、グラフィックプログラムの画像データなどのように。よく想定されるのとは異なり、このデータはあまりアクセスされないため、プレフィックスが必要な場合は、長いアドレスフィールドを使用する場合よりも害が少なくなります。
同様に、文字列操作を実行するときにDSとESをロード(およびリロード)する必要があるのは、ほんのわずかな煩わしさです。これは、少なくとも当時の最高の文字処理命令セットの1つによって相殺されます。
本当に痛いのは、ユーザーデータが64 KiBを超え、操作を開始する必要がある場合です。一部の操作は一度に1つのデータ項目に対して単純に実行されますが(think A=A*2)、ほとんどの操作には2つ(A=A*B)または3つのデータ項目(A=B*C)が必要です。これらのアイテムが異なるセグメントにある場合、ESは操作ごとに数回リロードされ、かなりのオーバーヘッドが追加されます。
当初、8ビットの世界(* 3)の小さなプログラムと同様に小さなデータセットでは、それは大したことではありませんでしたが、すぐに大きなパフォーマンスのボトルネックになりました。 (およびコンパイラー)。386 Intelは、さらに2つのセグメントを追加することで最終的に救済を提供しました。そのため、要素がメモリに分散された一連の単項、バイナリ、または三項演算は、ESを常にリロードせずに実行できます。
プログラミング(少なくともアセンブリーでは)とコンパイラーの設計にとって、これはかなりの利益でした。もちろん、もっと多くのことがあったかもしれませんが、3つでボトルネックは基本的になくなったので、やりすぎる必要はありません。
F / Gという文字の名前は、Eの後のアルファベットの続きです。少なくともCPU設計の観点からは、何も関連付けられていません。
* 1-文字列の宛先にESを使用することは例外であり、2つのセグメントレジスタが必要です。それらがなければ、あまり役に立ちません-または常にセグメントプレフィックスが必要です。これは、驚くべき機能の1つである、(反復しない)文字列命令を使用すると、シングルバイトエンコーディングのために極端なパフォーマンスが発生する可能性があります。
* 2-後から考えると、「その他すべてのセグメント」は「追加セグメント」よりもはるかに優れた命名でした。
* 3-8086は、 8800が完成するまでのストップギャップ対策としてのみ意図されており、主に組み込みの世界が8080/85の顧客を維持することを目的としていたことを常に覚えておくことが重要です。
Intelマニュアルによると、64ビットモードでは、これらのレジスタは、一部の線形アドレス計算で追加のベースレジスタとして使用されることを目的としています。私はこれをセクション3.7.4.1(4巻セットの86ページ)から引き出しました。通常、CPUがこのモードの場合、セグメンテーションはこのモードでは使用されないことが多いため、線形アドレスは実効アドレスと同じです。
したがって、このフラットなアドレス空間では、FSとGSは、ローカルデータだけでなく、特定のオペレーティングシステムのデータ構造(pg 2793、セクション3.2.4)のアドレス指定にも役割を果たします。したがって、これらのレジスタは、オペレーティングシステムで使用することを目的としていましたが、これらの特定の設計者は決定。
32ビットモードと64ビットモードの両方でオーバーライドを使用する場合、いくつかの興味深いトリックがありますが、これには特権ソフトウェアが含まれます。
「本来の意図」の観点からは、それは単なる追加のレジスターである以外は言うのが難しいです。CPUがリアルアドレスモードの場合、これはプロセッサが高速8086として実行されているようなものであり、これらのレジスタはプログラムによって明示的にアクセスされる必要があります。真の8086エミュレーションのために、CPUを仮想8086モードで実行し、これらのレジスタは使用されません。
FSおよびGSセグメントレジスタは、MS-DOSなどで64KBのセグメントしかない場合、80386プロセッサの16ビットリアルモードまたは16ビットプロテクトモードで非常に役立ちました。
1985年に80386プロセッサが導入されたとき、MS-DOSで640KBのRAMを搭載したPCコンピュータが一般的でした。RAMは高価であり、PCはほとんどの場合MS-DOSでリアルモードで実行され、その最大量のRAMが搭載されていました。
したがって、FSとGSを使用することで、DSまたはESにロードされた以外のセグメントをアドレス指定する必要がある場合はいつでも、DSまたはESレジスタを変更することなく、プログラムからさらに2つの64KBメモリセグメントを効果的にアドレス指定できます。基本的に、Raffzahnは、ESのような他のセグメントレジスタを常にリロードすることを避けるために、これらのレジスタがメモリ内に分散された要素を操作するときに役立つとすでに回答しています。ただし、これはリアルモードまたは16ビットプロテクトモードの64KBセグメントにのみ関連することを強調したいと思います。
16ビットプロテクトモードは、それ以来見られなかった機能を提供する非常に興味深いモードでした。セグメントの長さは、1〜65536バイトの範囲である可能性があります。各メモリアクセスの範囲チェック(セグメントサイズのチェック)はCPUによって実装され、そのセグメントのセレクタテーブルで指定されたセグメントのサイズを超えてメモリにアクセスすると割り込みが発生しました。これにより、ハードウェアレベルでのバッファオーバーランが防止されました。各メモリブロックに独自のセグメントを割り当てることができます(総数には一定の制限があります)。独自のDOSエクステンダを使用してDOSプロテクトモードインターフェイス(DPMI)と呼ばれる16ビットプロテクトモードでMS-DOSで実行されるプログラムを作成するBorlandPascal7.0のようなコンパイラがありました。
80286プロセッサには16ビットプロテクトモードがありましたが、FS/GSレジスタにはありませんでした。したがって、プログラムは、実際の16ビットモードであっても、これらのレジスタを使用する前に、まず80386で実行されているかどうかを確認する必要がありました。FSとGSの使用例を参照してください。MS-DOSリアルモード用のプログラムを登録します。