図の各領域はセグメントですか?
これらは、「セグメント」という言葉のほぼまったく異なる2つの使用法です。
- x86セグメンテーション/セグメントレジスタ:最新のx86 OSは、32ビットモードですべてのセグメントが同じbase = 0およびlimit = maxであるフラットメモリモデルを使用します。 。(FSまたはGSを除き、64ビットモードでもスレッドローカルストレージに使用されます。)
- リンカー/プログラムローダーセクション/セグメント。(ELFファイル形式のセクションとセグメントの違いは何ですか)
用法は、共通の起源を持っている:あなたがいる場合された(特にページ仮想メモリなし)セグメント化されたメモリ・モデルを使用して、あなたはデータを持っている可能性があり、BSSアドレスはDSセグメントベース、SSベースにスタック比べ、およびへのコードの相対に対して相対的CSベースアドレス。
そのため、セグメントベースに関連する16ビットまたは32ビットのオフセットを変更せずに、複数の異なるプログラムを異なる線形アドレスにロードしたり、起動後に移動することさえできます。
ただし、ポインタがどのセグメントに関連するかを知る必要があるため、「遠いポインタ」などがあります。(実際の16ビットx86プログラムは、データとしてコードにアクセスする必要がないことが多いので、どこかで64kコードセグメントを使用でき、DS = SSで別の64kブロックを使用できます。または、すべてのセグメントベースが等しい小さなコードモデル)。
x86セグメンテーションとページングの相互作用
32/64ビットモードでのアドレスマッピングは次のとおりです。
- segment:offset(オフセットを保持するレジスタによって暗示されるか、命令プレフィックスでオーバーライドされるセグメントベース)
- 32ビットまたは64ビットの線形仮想アドレス= base + offset。(Linuxが使用するフラットメモリモデルでは、ポインター/オフセット=線形アドレスも。FSまたはGSに関連するTLSにアクセスする場合を除きます。)
ページテーブル(TLBによってキャッシュ)は、32(レガシーモード)、36(レガシーPAE)、または52ビット(x86-64)の物理アドレスにリニアにマッピングします。(/programming/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the)。
この手順はオプションです。制御レジスタのビットを設定して、起動時にページングを有効にする必要があります。ページングがない場合、線形アドレスは物理アドレスです。
すべてがマップされるフラット(線形)アドレス空間にはオフセット自体と同じビット数しかないため、セグメンテーションでは単一プロセス(またはスレッド)で32または64ビットを超える仮想アドレス空間を使用できないことに注意してください。(16ビットx86の場合はそうではありませんでした。セグメンテーションは、実際にはほとんどが16ビットのレジスタとオフセットで64kを超えるメモリを使用するのに役立ちました。)
CPUは、セグメントベースを含むGDT(またはLDT)からロードされたセグメント記述子をキャッシュします。ポインタを間接参照すると、そのレジスタがどのレジスタにあるかに応じて、デフォルトでセグメントとしてDSまたはSSが使用されます。レジスタ値(ポインター)は、セグメントベースからのオフセットとして扱われます。
セグメントのベースは通常ゼロであるため、CPUはこれを特別な場合に行います。あなたがいる場合や、別の観点から、行う非ゼロセグメントベースを持っているベースアドレスを追加するバイパスの「特別」(ノーマル)の場合は適用されないため、負荷が余分な待ち時間を有します。
Linuxがx86セグメントレジスタを設定する方法:
CS / DS / ES / SSのベースと制限は、32および64ビットモードですべて0 / -1です。これは、すべてのポインターが同じアドレス空間を指すため、フラットメモリモデルと呼ばれます。
(AMD CPUアーキテクトは、メインストリームOSがそれを使用していないため、64ビットモードのフラットメモリモデルを強制することでセグメンテーションを廃止しました。ただし、PAEまたはx86- 64ページの表形式。)
TLS(スレッドローカルストレージ):FSおよびGSは、ロングモードでbase = 0に固定されていません。(これらは386で新しく追加されたもので、rep
ESを使用する-string命令でさえも、暗黙的に使用されていません。)x86-64 Linuxは、各スレッドのFSベースアドレスをTLSブロックのアドレスに設定します。
たとえばmov eax, [fs: 16]
、32ビット値を16バイトからこのスレッドのTLSブロックにロードします。
CSセグメント記述子は、CPUのモードを選択します(16/32/64ビット保護モード/ロングモード)。Linuxは、すべての64ビットユーザー空間プロセスに単一のGDTエントリを使用し、すべての32ビットユーザー空間プロセスに別のGDTエントリを使用します。(CPUが正しく機能するためには、DS / ESも有効なエントリに設定する必要があり、SSも同様です)。また、特権レベル(カーネル(リング0)とユーザー(リング3))を選択するため、64ビットのユーザー空間に戻った場合でも、カーネルは、通常の使用の代わりに、iret
またはsysret
ジャンプまたはリット命令。
x86-64では、syscall
エントリポイントはswapgs
GSをユーザー空間のGSからカーネルのGSにフリップするために使用し、このスレッドを使用してこのスレッドのカーネルスタックを見つけます。(スレッドローカルストレージの特殊なケース)。syscall
命令は、カーネルスタック点までスタックポインタを変更しません。カーネルがエントリポイント1に到達しても、まだユーザースタックを指し示しています。
DS / ES / SSは、CPUが保護モード/ロングモードで動作するために有効なセグメント記述子に設定する必要がありますが、これらのディスクリプタのベース/制限はロングモードでは無視されます。
そのため、基本的にx86セグメンテーションはTLSに使用され、ハードウェアが必要とする必須のx86 osdevに使用されます。
脚注1:おもしろい歴史:AMD64シリコンがリリースされる数年前からカーネル開発者とAMDアーキテクトの間でメッセージのメーリングリストアーカイブがあり、設計が微調整されてsyscall
使用可能になりました。詳細については、この回答のリンクを参照してください。