6

メモリ管理がどのように低レベルになるかを理解しようとしており、いくつか質問があります。

1) Kip R. Irvine によるアセンブリ言語に関する本では、リアル モードでは、プログラムの開始時に、最初の 3 つのセグメント レジスタにコード、データ、およびスタック セグメントのベース アドレスがロードされると述べています。これは私には少しあいまいです。これらの値は手動で指定されていますか、それとも値をレジスタに書き込む命令をアセンブラが生成していますか? 自動的に行われる場合、これらのセグメントのサイズをどのように見つけますか?

2) 私は、Linux がフラット線形モデルを使用していることを知っています。つまり、非常に限られた方法でセグメンテーションを使用しています。また、Daniel P. Bovet と Marco Cesati による「Understanding the Linux Kernel」によると、GDT にはユーザー データ、ユーザー コード、カーネル データ、およびカーネル コードの 4 つの主要なセグメントがあります。4 つのセグメントはすべて、サイズとベース アドレスが同じです。タイプとアクセス権だけが異なる場合、なぜ 4 つが必要なのか理解できません (それらはすべて同じ線形アドレスを生成しますよね?)。それらの 1 つだけを使用して、その記述子をすべてのセグメント レジスタに書き込んでみませんか?

3) セグメンテーションを使用しないオペレーティング システムは、どのようにプログラムを論理セグメントに分割しますか? たとえば、セグメント記述子のないコードとスタックを区別する方法などです。ページングを使用してそのようなことを処理できると読みましたが、その方法がわかりません。

4

3 に答える 3

4
  1. リアルモード用にプログラミングする人はもういないので、本当に古い本を何冊か読んだに違いありません;-)リアルモードでは、メモリアクセスの物理アドレスを で取得できますphyical address = segment register * 0x10 + offset。オフセットは、汎用レジスタの 1 つ内の値です。これらのレジスタは 16 ビット幅であるため、セグメントの長さは 64kb になり、属性がないという理由だけで、そのサイズについてできることは何もありません! 乗算により、1MB のメモリが使用可能になりますが、セグメント レジスタアドレス レジスタ* 0x10に何を入れるかによって、重複する組み合わせがあります。real-mode用のコードをコンパイルしていませんが、セグメントレジスタをセットアップするのは OS 次第だと思いますELFバイナリをロードするときにローダーがいくつかのページを割り当てるのと同じように、バイナリのロード中に。ただし、ベアメタル カーネル コードをコンパイルしたので、これらのレジスタを自分でセットアップする必要がありました。

  2. アーキテクチャの制約により、フラット モデルでは 4 つのセグメントが必須です。プロテクト モードでは、セグメント レジスタにはセグメント ベース アドレスが含まれなくなりますが、基本的に GDT へのオフセットであるセグメント セレクタが含まれます。セグメント セレクターの値に応じて、CPU は特定のレベルの特権になります。これが CPL (現在の特権レベル) です。セグメント セレクターは、DPL (記述子特権レベル) を持つセグメント記述子を指します。これは、セグメント レジスターがこのセレクターで満たされている場合、最終的には CPL になります (少なくともコード セグメント セレクターには当てはまります)。したがって、少なくとも 1 組が必要です。カーネルをユーザーランドから区別するためのセグメントセレクター。さらに、セグメントはコード セグメントまたはデータ セグメントのいずれかであるため、最終的にGDT には4 つのセグメント記述子があります。

  3. 下位互換性のためにセグメンテーションがまだ存在しているという理由だけで、セグメンテーションを使用する本格的な OS の例はありません。フラット モデル アプローチを使用することは、それを取り除くための手段にすぎません。とにかく、その通りです。ページングは​​はるかに効率的で用途が広く、ほぼすべてのアーキテクチャ (少なくとも概念) で利用できます。ここでページングの内部について説明することはできませんが、知っておく必要があるすべての情報は優れた Intel man: Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3A: System Programming Guide, Part 1 にあります。

于 2012-06-07T21:15:40.243 に答える
3

質問 3 に対する Benoit の回答を拡張すると...

コード、定数データ、変更可能なデータ、スタックなどの論理部分へのプログラムの分割は、さまざまな時点でさまざまなエージェントによって行われます。

まず、コンパイラ (およびリンカー) は、この区分が指定された実行可能ファイルを作成します。いくつかの実行可能ファイル形式 (PE、ELF など) を見ると、それらがある種のセクションやセグメント、または任意の名前をサポートしていることがわかります。ファイル内のアドレスとサイズと場所に加えて、これらのセクションには、これらのセクションの目的を OS に伝える属性があります。ファイル内のスペース)、スタックに関する情報、依存関係のリスト (DLL など) などがあります。

次に、OS がプログラムの実行を開始すると、ファイルを解析して、プログラムに必要なメモリ量、各セクションのどこに、どのようなメモリ保護が必要かを確認します。後者は通常、ページ テーブルを介して行われます。コード ページは実行可能で読み取り専用としてマークされ、定数データ ページは実行不可で読み取り専用としてマークされ、他のデータ ページ (スタックのデータ ページを含む) は実行不可で読み取り/書き込みとしてマークされます。これが本来あるべき姿です。

多くの場合、プログラムは読み書き可能であると同時に、動的に生成されたコード用の実行可能領域、または単に既存のコードを変更できるようにする必要があります。組み合わされた RWX アクセスは、実行可能ファイルで指定するか、実行時に要求することができます。

動的スタック拡張用のガード ページなど、他の特別なページが存在する場合があり、それらはスタック ページの隣に配置されます。たとえば、プログラムは 64KB のスタックに十分な数のページが割り当てられた状態で開始し、プログラムがそのポイントを超えてアクセスしようとすると、OS はそれらのガード ページへのアクセスをインターセプトし、スタックに追加のページを (サポートされている最大サイズまで) 割り当て、ガードページをさらに移動します。これらのページは、実行可能ファイルで指定する必要はなく、OS が独自に処理できます。このファイルでは、スタック サイズと、場合によっては場所のみを指定する必要があります。

コード メモリとデータ メモリを区別するため、またはメモリ アクセス権を強制するためのハードウェアまたはコードが OS にない場合、分割は非常に形式的です。16 ビットのリアルモード DOS プログラム (COM および EXE) には、特別な方法でマークされたコード、データ、およびスタック セグメントがありませんでした。COM プログラムは 1 つの共通の 64KB セグメントにすべてを持ち、IP=0x100 と SP=0xFFxx で始まり、コードとデータの順序は内部で任意であり、事実上自由に絡み合うことができました。DOS EXE ファイルは、CS:IP および SS:SP の開始位置のみを指定し、それ以降はコード、データ、およびスタック セグメントを DOS と区別できませんでした。ファイルをロードし、再配置を実行し (EXE のみ)、PSP (コマンド ライン パラメータとその他の制御情報を含むプログラム セグメント プレフィックス) をセットアップし、SS:SP と CS:IP をロードするだけで済みました。

于 2012-06-15T08:22:59.040 に答える
-1

Wikipedia is your friend in this case. http://en.wikipedia.org/wiki/Memory_segmentation and http://en.wikipedia.org/wiki/X86_memory_segmentation should be good starting points.

I'm sure there are others here who can personally provide in-depth explanations, though.

于 2012-06-05T20:32:54.350 に答える