メモリは非常に大きな配列とみなすことができることがわかっています。メモリ内の要素を見つけたい場合は、配列の添字によって指定されます。メモリについても同じことが当てはまりますが、前提があります。配列は何で構成されているか 順序付けされたバイトのセットで構成されています。この順序付けされたバイト配列では、各バイトは一意のアドレスを持ちます。このアドレスはメモリ アドレスとも呼ばれます。
メモリには多数のオブジェクトが保存されています。各オブジェクトは、char オブジェクト、byte オブジェクト、int オブジェクトなどの異なるバイトで構成されており、すべてメモリ内のさまざまな場所に分割されています。メモリ内でこれらのオブジェクトのアドレスを見つけるための CPU の操作は、メモリ アドレッシングと呼ばれます。メモリ バス幅は、アドレス 0 から開始してメモリ アドレスの何ビットをアドレス指定できるかを決定します。 80X86 は 32 ビットなので、バス幅も 32 ビットなので、メモリ アドレスは合計 2^32 個あり、合計 4GB のメモリ アドレスを保存できます。 int、long、double などの複数バイトのデータ型は、連続したメモリ アドレスを通じて抽出できます。
オブジェクトはアドレス指定できますが、これらのオブジェクトが格納されるバイト順序は異なり、ビッグ エンディアンとリトル エンディアンという 2 つの格納方法があります。
たとえば、アドレス 0x100 に int 型のオブジェクトがあり、その 16 進値は 0x01234567 です。図を描くと、2 つの格納順序の違いが理解できると思います。
これは非常に簡単で、0x01234567 の int データ型は 01 23 45 67 バイトに分割でき、01 が上位ビット、67 が下位ビットになります。リトルエンディアンとビッグエンディアンのストレージ順序、つまり、リトルエンディアンが最初にローエンドであり、ビッグエンディアンが最初にハイエンドであることを説明できます。ビッグエンディアンとリトルエンディアンの違いは格納順序のみであり、オブジェクトの桁数や数値とは関係ありません。ほとんどの Intel マシンはリトル エンディアン モードを使用するため、80X86 もリトル エンディアン ストレージですが、ほとんどの IBM および Oracle マシンはビッグ エンディアン ストレージを使用します。
メモリは比較的大きすぎるため、コンピュータはメモリ内のすべてのデータを一度に直接アドレス指定することができないため、メモリは一般にセグメント化されます。これには、「なぜメモリをセグメント化する必要があるのか?」という疑問が伴います。以上、一般的な紹介をさせていただきました。
なぜメモリをセグメント化する必要があるのでしょうか?
https://www.php.cn/link/d005ce7aeef46bd18515f783fb8e87fa
セグメンテーション メカニズムを使用して、メモリ空間は線形領域に分割され、各線形領域はpass セグメントのベース アドレスとセグメント内のオフセットを使用して、セグメントを見つけます。セグメントのベースアドレス部は16ビットのセグメントセレクタで指定され、そのうち14ビットで2の14乗、つまり16384個のセグメントを選択でき、セグメント内のオフセットアドレス部は32ビットの値で指定されるため、セグメント内のアドレスは 0 ~ 4G で、セグメントの最大長は 4 GB で、これは前述の 4 GB のメモリ アドレスに対応します。 16 ビットのセグメントと 32 ビットのセグメント内のオフセットで構成される 48 ビットのアドレスまたはロング ポインタは論理アドレスと呼ばれ、論理アドレスは仮想アドレスです。
X86 アーキテクチャには、セグメント ベース アドレスの格納に使用される 6 つの特殊レジスタ (CS、DS、ES、SS、FS、GS) があります。 CS はコード セグメントのアドレス指定に使用され、SS はスタック セグメントのアドレス指定に使用され、その他のレジスタはデータ セグメントのアドレス指定に使用されます。いつでも CS によってアドレス指定されるセグメントは、現在のコード セグメントと呼ばれます。現在のコード セグメントで実行される次の命令のオフセット アドレスは、すでに EIP レジスタに存在します。このとき、セグメントベースアドレス:オフセットアドレスは、CS:EIPと表すことができる。
セグメント レジスタ SS によってアドレス指定されるセグメントは、現在のスタック セグメントと呼ばれます。スタックの先頭は ESP レジスタによって与えられます。いつでも、SS:ESP はスタックの先頭を指し、そこに他の 4 つは汎用データ セグメント レジスタであり、デフォルトで命令内にデータ セグメントがない場合、DS によって与えられます。
通常、完全なメモリ管理システムは、アクセス保護とアドレス変換という 2 つのコンポーネントで構成されます。アクセス保護は、あるアプリケーションが別のプログラムで使用されているメモリ アドレスにアクセスすることを防ぎます。アドレス変換は、さまざまなアプリケーションに動的アドレス割り当て方法を提供します。アクセス保護とアドレス変換は相互に補完します。
アドレス変換は通常メモリ ブロックを基本単位として使用します。ここでブロックとは何かについて説明します。ご存知のとおり、Linux ではすべてがファイルであり、ファイルはブロックで構成されます。ブロックファイル システムのコンポーネント単位を記述するために使用され、データ処理の基本単位でもあります。一般的なブロックには、512B、1KB、4KB などのさまざまなサイズがあります。ブロックは基本単位ですが、本質的にはセクタで構成されます。
アドレス変換を実装するには、セグメンテーション メカニズムとページング メカニズムという 2 つの方法があります。 x86 でのメモリ管理の実装は、セグメンテーションとページングのメカニズムを組み合わせています。次の図は、セグメンテーションとページングの後に物理アドレスに変換された仮想アドレスのマッピング図です。
この図では、説明する必要があります:
まず、この図には 3 つのアドレスとその 3 つのアドレスの変換プロセスが含まれています。一般に、論理アドレスは、セグメント ベース アドレス変換後にリニア アドレスになります。リニア アドレスは、保護モードのセグメント ベースです。アドレスセグメント内のオフセットなので、この図は保護モードでのアドレス変換図です。リニア アドレスは、ページング メカニズムを有効にする必要がある場合、ページング メカニズムの後に物理アドレスに変換されます。ページング メカニズムが有効でない場合、リニア アドレス = 物理アドレスになります。
論理アドレスについてもう一度説明する必要があります。論理アドレスにはセグメント セレクターとセグメント内オフセットが含まれます。セグメント セレクターの概念は、私が初めて触れたときは比較的曖昧でした。簡単に言うと、以下のセグメント ベース アドレスは、セグメント ベース アドレスが 16 ビットで、セグメント内のオフセットが 32 ビットであることは誰もが知っています。
多くの書籍や記事でセグメント セレクターについて言及されています。実際、セグメント セレクターはセグメント セレクターです。これは完全に翻訳の問題です。英語では、これらはすべてセレクターです。
セグメントディスクリプタについては後述しますが、セグメントディスクリプタとセグメントセレクタは別のものですが、セグメントセレクタは16ビットのセグメントディスクリプタです。
この図に書いていないことをお話しますと、論理アドレスはリニア アドレスに変換でき、リニア アドレスは物理アドレスに変換できることは誰もが知っています。原因は変換されましたか?実は、ここで使用されている変換方法はMMU(メモリ管理ユニット)であり、リニアアドレスから物理アドレスへの変換にはページングユニットのハードウェア回路を使用しています。この記事の焦点は、特定の変換プロセスについて説明することではなく、セグメンテーションとページングの 2 つのメカニズムに焦点を当てることです。
セグメンテーションとページングの 2 つのメカニズムについて詳しく説明します。
まず、私が書いた「なぜメモリをセグメント化する必要があるのか」の説明を読むことをお勧めします。
https://www.php.cn/link/d005ce7aeef46bd18515f783fb8e87fa
複数のプログラムは同じメモリ空間で実行され、互いに干渉しません。セグメンテーションは、コード、データ、スタックの領域を分離するメカニズムを提供します。 CPU で複数のプログラムまたはタスクが実行されている場合、各プログラムには独自のセグメントのセット (プログラム コード、データ、スタックを含む) を割り当てることができ、CPU はセグメント間の境界を強化することでアプリケーションの相互干渉を防ぎます。
システムで使用されるすべてのセグメントは、CPU のリニア アドレス空間に含まれます。指定されたセグメント内のバイトを見つけるには、プログラムは変換を行うための論理アドレスを提供する必要があります。論理アドレスには、セグメント セレクタとセグメント内のオフセットが含まれます。各セグメントにはセグメント記述子があります。セグメント記述子は、セグメントのサイズ、セグメントのアクセス権と特権レベル、セグメント タイプ、および最初のバイトを示すために使用されますセグメントのオンラインの性的アドレス空間内の位置 (セグメント ベース アドレス)。論理アドレスのオフセット部分は、セグメント内の特定のバイトの位置を特定するためにセグメント ベース アドレスに追加されるため、セグメント ベース アドレス オフセットは CPU の線形アドレス空間内のアドレスを形成します。
リニア アドレス空間は物理アドレス空間と同じ構造ですが、収容できるセグメントは大きく異なります。仮想アドレス、つまり論理アドレス空間には最大 16 K のセグメントを含めることができ、各セグメントは収容 サイズは 4 GB であるため、仮想アドレスは合計 64 TB (2^46) のセグメントを見つけることができ、リニア アドレスと物理アドレス空間は 4GB (2^32) です。したがって、ページングが無効になっている場合は、リニア アドレス空間が物理アドレス空間になります。
この図は、論理アドレス→リニアアドレス→物理アドレスのマッピング図です。GDT テーブルと LDT テーブルは、それぞれアドレス空間の半分を占めます。は 8192 各セグメントの最大長は 4G です GDT テーブルからクエリするか LDT テーブルからクエリするか、どのテーブルからクエリするかはセグメント セレクタの TI 属性によって異なります セグメント セレクタの構造は次のとおりです セグメント セレクターは 3 つの部分に分かれています。この記事では依然としてメモリ管理を好み、特定の詳細にはあまりこだわっていないため、ここではセグメント記述子の詳細な説明はありません。
GDTR では、セグメント セレクターとオフセットから構成される論理アドレスをセグメント ディスクリプターに合成し、直接保存できます。セグメント セレクターとセグメント内オフセットは、MMU を通過した後に線形アドレスに変換できます。
上で説明したように、リニア アドレスは論理アドレスから変換されます。ページング メカニズムが無効な場合、リニア アドレスは物理アドレスです。ページング メカニズムが有効な場合、リニア アドレスは物理アドレスになります。 、リニアアドレスと論理アドレス、アドレス空間の数は依然として異なります。一般に、プログラムはマルチタスクであり、通常、マルチタスクによって定義される線形アドレス空間は、物理メモリ容量よりもはるかに大きくなります。アドレス変換マップは、リニア アドレスと物理アドレスの両方のサイズが 4G であることを示しています。これは、リニア アドレスが仮想ストレージ テクノロジによって仮想化されているためです。
仮想ストレージはメモリ管理技術です。この技術を使用すると、メモリ空間が実際の物理メモリ容量よりもはるかに大きいかのように錯覚することができます。その本質は、メモリ、つまりメモリを仮想化することです。たったの 4G かもしれませんが、メモリは 64G あると思われるので、非常に多くのアプリケーションを開くことができます。
ページング メカニズムは実際には仮想化の実装であり、仮想化環境では、大量の線形アドレス空間が小さな物理メモリ (RAM または ROM) にマッピングされます。ページングが発生すると、各セグメントはページ (通常は 4K) に分割され、これらのページは物理メモリまたはディスクに保存されます。オペレーティング システムは、ページ ディレクトリとページ テーブルを使用してこれらのページを管理します。プログラムがリニア アドレス空間内のアドレス位置にアクセスしようとすると、CPU はページ ディレクトリとページ テーブルを使用してリニア アドレスを物理アドレスに変換し、物理メモリに格納します。
現在アクセスされているページが物理メモリにない場合、CPU は割り込みを実行します。一般的なエラーはページ例外です。その後、オペレーティング システムはページをハード ディスクから物理メモリに読み取り、その後割り込みポイントからプログラムの実行を継続します。オペレーティング システムはページのスワップインとスワップアウトを頻繁に行うことが多く、これもパフォーマンスのボトルネックになります。
セグメンテーションでは、各セグメントの長さは固定されず、最大長は 4G ですが、ページングでは、各ページのサイズは固定です。物理メモリであってもディスク上であっても、物理メモリの管理には固定サイズのページを使用する方が適していますが、複雑なシステムの論理パーティションの処理には可変サイズのブロックを使用するセグメント化メカニズムの方が適しています。
セグメンテーションとページングは 2 つの異なるアドレス変換メカニズムですが、これらはアドレス変換プロセス全体で独立して処理され、各プロセスは独立しています。どちらのメカニズムも中間テーブルを使用してエントリ マッピングを保存しますが、この中間テーブルの構造は異なります。セグメントテーブルはリニアアドレス空間に存在し、ページテーブルは物理アドレス空間に格納されます。
80x86 には 2 つの保護機構があり、その 1 つはタスクごとに異なる仮想アドレス空間を割り当てることでタスク間の完全な分離を実現します。これは、各タスクに論理アドレスから物理アドレスへの異なる変換を与えることによって実現されます。各アプリケーションは、独自の仮想空間内のデータと命令にのみアクセスでき、独自のマッピングを通じてのみ物理アドレスを取得できます。2 番目のメカニズムは、タスクの保護です。オペレーティング システムのメモリ セグメントと一部の特殊レジスタがアプリケーションからアクセスされないように保護します。これら 2 つのタスクについて、以下で詳しく説明します。
各タスクは、独自の仮想アドレス空間に個別に配置され、ハードウェアを介して物理アドレスにマッピングされます。異なる仮想アドレスは、異なる物理アドレスに変換されます。の場合、A の仮想アドレスは存在せず、B の物理アドレスの範囲にマッピングされます。これにより、すべてのタスクが分離され、異なるタスクは互いに干渉しなくなります。
各タスクには独自のマッピング テーブル、セグメント テーブル、ページ テーブルがあり、CPU が異なるアプリケーションまたはタスク間で切り替わると、これらのテーブルも切り替わります。
仮想アドレスはオペレーティング システムの抽象化です。つまり、仮想アドレスはオペレーティング システムによって完全に抽象化され、アプリケーションとタスクをより適切に管理できるようになります。各タスクは論理アドレスを仮想アドレスにマップできます。これは、各タスクがオペレーティング システムにアクセスでき、オペレーティング システムをすべてのタスクで共有できることも意味します。すべてのタスクが同じ仮想アドレス空間を持つ仮想アドレス空間のこの部分はグローバル アドレス空間と呼ばれ、Linux はグローバル アドレス空間を使用します。
グローバルアドレス空間内の各タスクは、それぞれ固有の仮想アドレス空間を持ち、この仮想アドレス空間をローカルアドレス空間(ローカルアドレス空間)と呼びます。
異なるタスク間のオペレーティング システムの保護を水平保護に例えると、メモリ セグメントとレジスタの保護は垂直保護とみなすことができます。タスク内のさまざまなセグメントへのアクセスを制限するために、オペレーティング システムは 4 つの特権レベルを設定して各タスクを保護します。
優先度は 4 段階に分かれており、0 が最も高く、3 が最も低くなります。一般に、最も機密性の高いデータには最高の優先度が与えられ、タスクの最も信頼できる部分によってのみアクセスできます。機密性の低いデータには低い優先度が与えられます。カーネル オペレーティング システムのアクセスは一般にレベル 0、アプリケーション データは一般的にはレベル3。各メモリ セグメントは特権レベルに関連付けられています。
CPU は CS を介してセグメントから命令とデータを取得して実行することがわかります。セグメントから取得した命令とデータには特権レベルがあり、通常は現在の特権レベル (現在の特権レベル) でアクセスされます。現在アクティブなコードです。特権レベルです。アプリケーションがセグメントにアクセスしようとすると、この権限レベルと比較され、このセグメントよりも低い権限レベルのみにアクセスできます。
以上がLinux 保護モードでのメモリ管理の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。