web-dev-qa-db-ja.com

x86ページングは​​どのように機能しますか?

この質問は、主題に関する無料の良い情報の空白を埋めるためのものです。

良い答えは、1つの大きなSOの答えまたは少なくともいくつかの答えに収まると信じています。

主な目標は、完全な初心者に十分な情報を提供して、マニュアルを自分で取り、ページングに関連する基本的なOSの概念を理解できるようにすることです。

推奨されるガイドライン:

  • 答えは初心者に優しいものでなければなりません:
    • 具体的ですが、おそらく単純化された例が非常に重要です
    • 示されている概念のアプリケーションは大歓迎です
  • 有用なリソースを引用するのは良いことです
  • oSがページング機能をどのように使用するかについての小さな余談を歓迎します
  • PAEとPSEの説明は大歓迎です
  • x86_64への小さな余談は大歓迎です

関連する質問と、それらがtheyではないと思う理由:

Nice TOCおよびその他のコンテンツを含むこの回答のバージョン

報告されたエラーを修正します。大規模な修正を行うか、欠落している側面を追加する場合は、適切な担当者を得るために自分で答えを作成してください。マイナーな編集は直接マージできます。

サンプルコード

最小限の例: https://github.com/cirosantilli/x86-bare-metal-examples/blob/5c672f73884a487414b3e21bd9e579c67cd77621/paging.S

プログラミングの他のすべてと同様に、これを本当に理解する唯一の方法は、最小限の例を試すことです。

これを「難しい」主題にしているのは、独自の小さなOSを作成する必要があるため、最小限の例が大きいということです。

Intelマニュアル

例を念頭に置いて理解することは不可能ですが、できるだけ早くマニュアルに慣れるようにしてください。

インテルでは、ページングについて Intel Manual Volume 3 System Programming Guide-325384-056US September 2015 第4章「ページング」で説明しています。

特に興味深いのは、図4-4「32ビットページングを使用したCR3およびページング構造のエントリのフォーマット」です。これは、主要なデータ構造を示しています。

MMU

ページングは​​、CPUの メモリ管理ユニット (MMU)部分によって行われます。他の多くの例と同様(例 x87コプロセッサー[〜#〜] apic [〜#〜] )、これは初期の頃は別々のチップでした。後でCPUに統合されました。しかし、この用語はまだ使用されています。

一般的な事実

論理アドレスは、「通常の」ユーザーランドコードで使用されるメモリアドレスです(例:mov eax, [rsi]rsiの内容)。

最初のセグメンテーションはそれらを線形アドレスに変換し、次にページングは​​線形アドレスを物理アドレスに変換します。

(logical) ------------------> (linear) ------------> (physical)
             segmentation                 paging

ほとんどの場合、物理アドレスは実際のRAMハードウェアメモリセルのインデックス付けと考えることができますが、これは次の理由により100%真実ではありません。

ページングは​​保護モードでのみ使用できます。保護モードでのページングの使用はオプションです。 cr0レジスタのPGビットが設定されている場合、ページングがオンになります。

ページングとセグメンテーション

ページングとセグメンテーションの大きな違いは次のとおりです。

  • ページングは​​RAMをページと呼ばれる同じサイズのチャンクに分割します
  • セグメンテーションは、メモリを任意のサイズのチャンクに分割します

これはページングの主な利点です。同じサイズのチャンクにより管理が容易になるためです。

ページングは​​非常に一般的になっているため、セグメンテーションのサポートは、IA32をエミュレートする互換モードでのみ存在する新しいソフトウェアのメイン操作モードである64ビットモードのx86-64で削除されました。

応用

ページングは​​、最新のOSにプロセス仮想アドレススペースを実装するために使用されます。仮想アドレスを使用すると、OSは次の方法で単一のRAMに2つ以上の同時プロセスを適合させることができます。

  • 両方のプログラムが他のプログラムについて何も知る必要がない
  • 両方のプログラムのメモリは、必要に応じて増減できます
  • プログラム間の切り替えは非常に高速です
  • あるプログラムが別のプロセスのメモリにアクセスすることはできません

歴史的にページングは​​セグメンテーション後に行われ、可変長セグメントの代わりにページの固定サイズのメモリチャンクを管理する方が簡単であるため、Linuxなどの最新のOSで仮想メモリを実装するために大幅に置き換えられました。

ハードウェア実装

保護モードでのセグメンテーション(セグメントレジスタの変更がGDTまたはLDTからのロードをトリガーする)と同様に、ページングハードウェアはメモリ内のデータ構造を使用してジョブ(ページテーブル、ページディレクトリなど)を実行します。

これらのデータ構造の形式は固定されていますハードウェアによるですが、RAMのデータ構造を正しくセットアップおよび管理し、ハードウェアに場所を伝えるのはOS次第ですそれらを見つけるために(cr3経由)。

他のいくつかのアーキテクチャでは、ページングをほぼ完全にソフトウェアの手に委ねているため、TLBミスはOSが提供する機能を実行してページテーブルを移動し、TLBに新しいマッピングを挿入します。これにより、ページテーブルの形式はOSによって選択されたままになりますが、 ハードウェアが他の命令の順不同の実行とページウォークをオーバーラップできる可能性は低く、x86のように です。

例:単純化された単一レベルのページングスキーム

これは、仮想メモリ空​​間を実装するために、x86アーキテクチャのsimplifiedバージョンでページングがどのように動作するかの例です。

ページテーブル

OSは、次のページテーブルを提供できます。

OSによってプロセス1に与えられたページテーブル:

RAM location        physical address   present
-----------------   -----------------  --------
PT1 + 0       * L   0x00001            1
PT1 + 1       * L   0x00000            1
PT1 + 2       * L   0x00003            1
PT1 + 3       * L                      0
...                                    ...
PT1 + 0xFFFFF * L   0x00005            1

OSによってプロセス2に与えられたページテーブル:

RAM location       physical address   present
-----------------  -----------------  --------
PT2 + 0       * L  0x0000A            1
PT2 + 1       * L  0x0000B            1
PT2 + 2       * L                     0
PT2 + 3       * L  0x00003            1
...                ...                ...
PT2 + 0xFFFFF * L  0x00004            1

どこで:

  • PT1およびPT2:RAM上のテーブル1および2の初期位置。

    サンプル値:0x000000000x12345678など.

    これらの値を決定するのはOSです。

  • L:ページテーブルエントリの長さ。

  • present:ページがメモリに存在することを示します。

ページテーブルはRAMにあります。たとえば、次の場所にあります。

--------------> 0xFFFFFFFF


--------------> PT1 + 0xFFFFF * L
Page Table 1
--------------> PT1


--------------> PT2 + 0xFFFFF * L
Page Table 2
--------------> PT2

--------------> 0x0

両方のページテーブルのRAMの初期位置は任意であり、OSによって制御されます。それらが重ならないことを保証するのはOS次第です!

各プロセスはページテーブルに直接アクセスできませんが、OSにリクエストを行ってページテーブルを変更させることができます。たとえば、より大きなスタックまたはヒープセグメントを要求します。

ページは4KB(12ビット)のチャンクであり、アドレスは32ビットであるため、各ページを識別するのに必要なのは20ビット(20 + 12 = 32、したがって16進表記で5文字)だけです。この値はハードウェアによって修正されます。

ページテーブルエントリ

ページテーブルは...ページテーブルエントリのテーブルです!

テーブルエントリの正確な形式は固定されていますハードウェアによる

この単純化された例では、ページテーブルエントリには2つのフィールドのみが含まれています。

bits   function
-----  -----------------------------------------
20     physical address of the start of the page
1      present flag

したがって、この例では、ハードウェア設計者はL = 21を選択できます。

ほとんどの実際のページテーブルエントリには他のフィールドがあります。

メモリはビットではなくバイトでアドレス指定できるため、21バイトに揃えることは実用的ではありません。したがって、この場合は21ビットのみが必要ですが、ハードウェア設計者はおそらくL = 32を選択してアクセスを高速化し、残りのビットを後で使用するために予約します。 x86上のLの実際の値は32ビットです。

シングルレベルスキームでのアドレス変換

OSによってページテーブルが設定されると、線形アドレスと物理アドレス間のアドレス変換が行われますby the hardware

OSはプロセス1をアクティブにしたい場合、cr3PT1に設定します。これは、プロセス1のテーブルの始まりです。

プロセス1が線形アドレス0x00000001にアクセスしたい場合、ページングハードウェア回路はOSに対して以下を自動的に実行します。

  • 線形アドレスを2つの部分に分割します。

    | page (20 bits) | offset (12 bits) |
    

    したがって、この場合は次のようになります。

    • ページ= 0x00000
    • オフセット= 0x001
  • cr3が指すため、ページテーブル1を調べます。

  • エントリ0x00000がページ部分であるため、これを見てください。

    ハードウェアは、このエントリがRAMアドレスPT1 + 0 * L = PT1にあることを知っています。

  • 存在するため、アクセスは有効です

  • ページテーブルでは、ページ番号0x00000の場所は0x00001 * 4K = 0x00001000にあります。

  • 最終的な物理アドレスを見つけるには、オフセットを追加するだけです。

      00001 000
    + 00000 001
      -----------
      00001 001
    

    00001はテーブルで検索されたページの物理アドレスであり、001はオフセットであるためです。

    名前が示すように、オフセットは常にページの物理アドレスに常に追加されます。

  • その後、ハードウェアはその物理的な場所でメモリを取得します。

同様に、プロセス1で次の翻訳が行われます。

linear     physical
---------  ---------
00000 002  00001 002
00000 003  00001 003
00000 FFF  00001 FFF
00001 000  00000 000
00001 001  00000 001
00001 FFF  00000 FFF
00002 000  00002 000
FFFFF 000  00005 000

たとえば、アドレス00001000にアクセスする場合、ページ部分は00001であり、ハードウェアはそのページテーブルエントリがRAMアドレスにあることを知っています:PT1 + 1 * L1ページ部分のため)、それはそれがそれを探す場所です。

OSがプロセス2に切り替えたい場合、cr3が2ページ目を指すようにするだけです。それは簡単です!

これで、プロセス2で次の翻訳が行われます。

linear     physical
---------  ---------
00000 002  00001 002
00000 003  00001 003
00000 FFF  00001 FFF
00001 000  00000 000
00001 001  00000 001
00001 FFF  00000 FFF
00003 000  00003 000
FFFFF 000  00004 000

同じ線形アドレスは、異なるプロセスの異なる物理アドレスに変換されますcr3内の値のみに依存します。

このようにして、すべてのプログラムは、正確な物理アドレスを気にせずに、0で始まりFFFFFFFFで終わるデータを期待できます。

ページ違反

プロセス1が存在しないページ内のアドレスにアクセスしようとした場合はどうなりますか?

ハードウェアは、ページ違反例外を介してソフトウェアに通知します。

通常、例外ハンドラを登録して、何を行う必要があるかを決定するのは、通常OSの責任です。

テーブルにないページにアクセスすると、プログラミングエラーになる可能性があります。

int is[1];
is[2] = 1;

しかし、たとえばLinuxで次のような場合に受け入れられる場合があります。

  • プログラムはスタックを増やしたいと考えています。

    与えられた範囲内の特定のバイトにアクセスしようとし、OSが満足している場合は、そのページをプロセスのアドレス空間に追加します。

  • ページはディスクにスワップされました。

    OSは、ページをRAMに戻すために、プロセスの背後で作業を行う必要があります。

    現在のフラグがクリアされている場合、ページテーブルエントリの他のエントリはOSが必要なものに完全に残されるため、OSは、これがページテーブルエントリの残りの内容に基づいたケースであることを発見できます。

    たとえばLinuxでは、存在する場合= 0:

    • ページテーブルエントリのすべてのフィールドが0の場合、無効なアドレス。

    • そうでない場合、ページはディスクにスワップされ、それらのフィールドの実際の値はディスク上のページの位置をエンコードします。

いずれの場合でも、OSは、ページフォールトを生成したアドレスを把握して、問題に対処する必要があります。これが、ページフォールトが発生するたびにNice IA32開発者がcr2の値をそのアドレスに設定する理由です。例外ハンドラは、cr2を調べるだけでアドレスを取得できます。

簡素化

この例を理解しやすくする現実の単純化:

  • すべての実際のページング回路は、スペースを節約するためにマルチレベルページングを使用しますが、これは単純なシングルレベルスキームを示しています。

  • ページテーブルには、20ビットのアドレスと1ビットの存在フラグの2つのフィールドのみが含まれていました。

    実ページのテーブルには合計12のフィールドが含まれているため、他の機能は省略されています。

例:マルチレベルページングスキーム

単一レベルのページング方式の問題は、4G/4K = 1Mエントリperプロセスというように、RAMを大量に消費することです。各エントリの長さが4バイトの場合、4M プロセスごとになります。これはデスクトップコンピューターにとっても大きすぎます:ps -A | wc -lは、現在244プロセスを実行していると言うので約1GBのRAMを使用してください!

このため、x86開発者は、RAM使用量を削減するマルチレベルスキームを使用することにしました。

このシステムの欠点は、アクセス時間がわずかに長くなることです。

PAEのない32ビットプロセッサに使用される単純な3レベルのページングスキームでは、32アドレスビットは次のように分割されます。

| directory (10 bits) | table (10 bits) | offset (12 bits) |

各プロセスには1つのページディレクトリのみが関連付けられている必要があるため、少なくとも2^10 = 1Kページディレクトリエントリが含まれます。これは、単一レベルのスキームで必要な最小1Mよりもはるかに優れています。

ページテーブルは、OSの必要に応じてのみ割り当てられます。各ページテーブルには、2^10 = 1Kページディレクトリエントリがあります

ページディレクトリには...ページディレクトリエントリが含まれています!ページディレクトリエントリは、テーブルの物理アドレスではなく、ページテーブルのRAMアドレスを指すを除いて、ページテーブルエントリと同じです。これらのアドレスは20ビット幅しかないため、ページテーブルは4KBページの先頭にある必要があります。

cr3は、ページテーブルではなく、現在のプロセスのページディレクトリのRAM上の場所を指すようになりました。

ページテーブルのエントリは、単一レベルのスキームからまったく変化しません。

ページテーブルは、次の理由でシングルレベルスキームから変更されます。

  • 各プロセスには、ページディレクトリエントリごとに1つ、最大1Kのページテーブルがあります。
  • 各ページテーブルには、1Mエントリではなく正確に1Kエントリが含まれます。

最初の2つのレベルで12 | 8 | 12などではなく10ビットを使用する理由は、各ページテーブルエントリの長さが4バイトだからです。次に、ページディレクトリとページテーブルの2 ^ 10エントリが4Kbページにうまく収まります。これは、その目的でページの割り当てと割り当て解除をより速く簡単に行えることを意味します。

マルチレベルスキームでのアドレス変換

OSによってプロセス1に与えられたページディレクトリ:

RAM location     physical address   present
---------------  -----------------  --------
PD1 + 0     * L  0x10000            1
PD1 + 1     * L                     0
PD1 + 2     * L  0x80000            1
PD1 + 3     * L                     0
...                                 ...
PD1 + 0x3FF * L                     0

PT1 = 0x100000000x10000 * 4K)のOSによってプロセス1に与えられたページテーブル:

RAM location      physical address   present
---------------   -----------------  --------
PT1 + 0     * L   0x00001            1
PT1 + 1     * L                      0
PT1 + 2     * L   0x0000D            1
...                                  ...
PT1 + 0x3FF * L   0x00005            1

PT2 = 0x800000000x80000 * 4K)のOSによってプロセス1に与えられたページテーブル:

RAM location      physical address   present
---------------   -----------------  --------
PT2 + 0     * L   0x0000A            1
PT2 + 1     * L   0x0000C            1
PT2 + 2     * L                      0
...                                  ...
PT2 + 0x3FF * L   0x00003            1

ここで:

  • PD1:RAM上のプロセス1のページディレクトリの初期位置。
  • PT1およびPT2:RAM上のプロセス1のページテーブル1およびページテーブル2の初期位置。

したがって、この例では、ページディレクトリとページテーブルをRAMに保存できます。

----------------> 0xFFFFFFFF


----------------> PT2 + 0x3FF * L
Page Table 1
----------------> PT2

----------------> PD1 + 0x3FF * L
Page Directory 1
----------------> PD1


----------------> PT1 + 0x3FF * L
Page Table 2
----------------> PT1

----------------> 0x0

線形アドレス0x00801004を段階的に変換しましょう。

cr3 = PD1、つまり、今説明したページディレクトリを指していると仮定します。

バイナリでは、線形アドレスは次のとおりです。

0    0    8    0    1    0    0    4
0000 0000 1000 0000 0001 0000 0000 0100

10 | 10 | 12としてグループ化すると、以下が得られます。

0000000010 0000000001 000000000100
0x2        0x1        0x4

与えるもの:

  • ページディレクトリエントリ= 0x2
  • ページテーブルエントリ= 0x1
  • オフセット= 0x4

そのため、ハードウェアはページディレクトリのエントリ2を探します。

ページディレクトリテーブルは、ページテーブルが0x80000 * 4K = 0x80000000にあることを示しています。これは、プロセスの最初のRAMアクセスです。

ページテーブルエントリは0x1であるため、ハードウェアは0x80000000のページテーブルのエントリ1を見て、物理ページがアドレス0x0000C * 4K = 0x0000C000にあることを通知します。これは、プロセスの2番目のRAMアクセスです。

最後に、ページングハードウェアはオフセットを追加し、最終アドレスは0x0000C004です。

変換されたアドレスの他の例は次のとおりです。

linear    10 10 12 split   physical
--------  ---------------  ----------
00000001  000 000 001      00001001
00001001  000 001 001      page fault
003FF001  000 3FF 001      00005001
00400000  001 000 000      page fault
00800001  002 000 001      0000A001
00801008  002 001 008      0000C008
00802008  002 002 008      page fault
00B00001  003 000 000      page fault

ページフォールトは、ページディレクトリエントリまたはページテーブルエントリが存在しない場合に発生します。

OSが別のプロセスを同時に実行したい場合、2番目のプロセスに個別のページディレクトリを与え、そのディレクトリを個別のページテーブルにリンクします。

64ビットアーキテクチャ

64ビットは、現在のRAMサイズに対して依然として大きすぎるアドレスであるため、ほとんどのアーキテクチャで使用されるビットは少なくなります。

x86_64は48ビット(256 TiB)を使用し、レガシーモードのPAEはすでに52ビットアドレス(4 PiB)を許可しています。

これらの48ビットのうち12ビットはすでにオフセット用に予約されており、36ビットが残ります。

2レベルのアプローチを採用する場合、最適な分割は2つの18ビットレベルになります。

しかし、それは、ページディレクトリに2^18 = 256Kエントリがあることを意味し、RAMが多すぎることになります。32ビットアーキテクチャの単一レベルのページングに近いです。

したがって、64ビットアーキテクチャでは、さらに3つまたは4つのページレベルが作成されます。

x86_64は9 | 9 | 9 | 12スキームで4つのレベルを使用するため、上位レベルは2^9上位レベルのエントリのみを使用します。

PAE

物理アドレス拡張。

32ビットでは、4GB RAMのみをアドレス指定できます。

これが大型サーバーの制限になり始めたため、IntelはPAEメカニズムをPentium Proに導入しました。

問題を緩和するために、Intelは4つの新しいアドレス行を追加し、64GBに対応できるようにしました。

PAEがオンの場合、ページテーブル構造も変更されます。変更される正確な方法は、PSEがオンかオフかによって異なります。

PAEは、cr4PAEビットを介してオンとオフを切り替えます。

アドレス可能なメモリの合計が64GBであっても、個々のプロセスは最大4GBまでしか使用できません。ただし、OSは異なる4GBチャンクに異なるプロセスを配置できます。

PSE

ページサイズ拡張。

ページの長さを4Kではなく4M(またはPAEがオンの場合は2M)にすることができます。

PSEは、cr4PAEビットを介してオンおよびオフになります。

PAEおよびPSEページテーブルスキーム

PAEとPSEのいずれかがアクティブな場合、異なるページングレベルスキームが使用されます。

  • pAEおよびPSEなし:10 | 10 | 12

  • pAEおよびPSEなし:10 | 22

    22ビットは4Mbをアドレス指定するため、22は4Mbページ内のオフセットです。

  • PAEおよびPSEなし:2 | 9 | 9 | 12

    10ではなく9を2回使用する設計上の理由は、エントリが32ビットに収まらず、すべて20ビットのアドレスビットと12個の意味のあるまたは予約済みのフラグビットで埋められたためです。

    理由は、ページテーブルのアドレスを表すのに20ビットでは不十分だからです。プロセッサに4本の余分なワイヤが追加されたため、24ビットが必要になりました。

    そのため、設計者はエントリサイズを64ビットに増やし、単一ページテーブルに収まるようにすることを決定しました。エントリ数を2 ^ 10ではなく2 ^ 9に減らす必要があります。

    開始2は、ページディレクトリポインターテーブル(PDPT)と呼ばれる新しいページレベルです。これはpointsでディレクトリをページングし、32ビットの線形アドレスを入力するためです。 PDPTも64ビット幅です。

    cr3はPDPTを指すようになりました。PDPTは最初の4 GBのメモリ上にあり、効率を上げるために32ビットの倍数で整列する必要があります。これは、cr3に20ではなく27の有効ビットがあることを意味します。32の倍数の2 ^ 5 *最初の4GBの2 ^ 32を完了するための2 ^ 27。

  • PAEおよびPSE:2 | 9 | 21

    デザイナーは、1つのページに収まるように9ビット幅のフィールドを保持することにしました。

    これにより23ビットが残ります。 PSEのないPAEケースで物事を均一に保つためにPDPTに2を残すと、オフセットに21が残ります。つまり、ページは4Mではなく2M幅になります。

TLB

Translation Lookahead Buffer(TLB)は、ページングアドレスのキャッシュです。

キャッシュであるため、結合レベルなど、CPUキャッシュの設計上の問題の多くを共有します。

このセクションでは、4つの単一アドレスエントリを使用した、単純化された完全連想TLBについて説明します。他のキャッシュと同様に、実際のTLBは通常完全に連想的ではないことに注意してください。

基本操作

線形アドレスと物理アドレス間の変換が行われた後、TLBに保存されます。たとえば、4エントリのTLBは次の状態で開始されます。

  valid   linear   physical
  ------  -------  ---------
> 0       00000    00000
  0       00000    00000
  0       00000    00000
  0       00000    00000

>は、置き換えられる現在のエントリを示します。

ページの線形アドレス00003が物理アドレス00005に変換された後、TLBは次のようになります。

  valid   linear   physical
  ------  -------  ---------
  1       00003    00005
> 0       00000    00000
  0       00000    00000
  0       00000    00000

00007から00009への2回目の翻訳の後、次のようになります。

  valid   linear   physical
  ------  -------  ---------
  1       00003    00005
  1       00007    00009
> 0       00000    00000
  0       00000    00000

00003を再度変換する必要がある場合、ハードウェアは最初にTLBを検索し、単一のRAMアクセス00003 --> 00005でアドレスを見つけます。

もちろん、00000はキーとして00000を含む有効なエントリがないため、TLBにはありません。

交換ポリシー

TLBがいっぱいになると、古いアドレスは上書きされます。 CPUキャッシュの場合と同様に、置換ポリシーは潜在的に複雑な操作ですが、単純で合理的なヒューリスティックは、最も最近使用されていないエントリ(LRU)を削除することです。

LRUを使用して、状態から開始:

  valid   linear   physical
  ------  -------  ---------
> 1       00003    00005
  1       00007    00009
  1       00009    00001
  1       0000B    00003

0000D -> 0000Aを追加すると、次のようになります。

  valid   linear   physical
  ------  -------  ---------
  1       0000D    0000A
> 1       00007    00009
  1       00009    00001
  1       0000B    00003

カム

TLBを使用すると、最初の変換に1回のアクセスが必要になるため、変換が高速になりますTLBレベルごと。これは、単純な32ビットスキームでは2、64ビットアーキテクチャでは3または4を意味します。

TLBは通常、Content-Addressable Memory(CAM)と呼ばれるRAMの高価なタイプとして実装されます。 CAMは、ハードウェア、つまりキー(線形アドレス)を指定して値を取得する構造体に、関連付けマップを実装します。

マッピングはRAMアドレスにも実装できますが、CAMマッピングに必要なエントリはRAMマッピングよりもはるかに少ない場合があります。

たとえば、次のマップ:

  • キーと値の両方が20ビット(単純なページングスキームの場合)
  • 毎回最大4つの値を保存する必要があります

4つのエントリを持つTLBに保存できます。

linear   physical
-------  ---------
00000    00001
00001    00010
00010    00011
FFFFF    00000

ただし、これをRAMで実装するには、2 ^ 20個のアドレスが必要です

linear   physical
-------  ---------
00000    00001
00001    00010
00010    00011
... (from 00011 to FFFFE)
FFFFF    00000

tLBを使用するよりもさらに高価になります。

エントリの無効化

cr3が変更されると、新しいプロセスの新しいページテーブルが使用されるため、すべてのTLBエントリが無効になります。したがって、古いエントリが意味を持つことはほとんどありません。

X86は、単一のTLBエントリを明示的に無効にするinvlpg命令も提供します。他のアーキテクチャでは、特定の範囲のすべてのエントリを無効にするなど、無効なTLBエントリにさらに多くの命令を提供します。

一部のx86 CPUは、x86仕様の要件を超えており、それが保証する以上の一貫性を提供します TLBにまだキャッシュされていない場合、ページテーブルエントリの変更と使用の間 。どうやらWindows 9xは正確性のためにこれに依存していましたが、最新のAMD CPUは一貫したページウォークを提供していません。 Intel CPUは、そうするために誤った推測を検出する必要がありますが。これを利用することはおそらく得策ではないので、おそらく悪い考えであり、デバッグが難しい微妙なタイミングに敏感な問題を引き起こす大きなリスクがあります。

Linuxカーネルの使用

Linuxカーネルは、x86のページング機能を広範囲に使用して、小さなデータの断片化を伴う高速プロセス切り替えを可能にします。

v4.2で、Arch/x86/の下を確認します。

  • include/asm/pgtable*
  • include/asm/page*
  • mm/pgtable*
  • mm/page*

ページを表すために定義された構造体はないようです。マクロのみ:include/asm/page_types.hは特に興味深いものです。抜粋:

#define _PAGE_BIT_PRESENT   0   /* is present */
#define _PAGE_BIT_RW        1   /* writeable */
#define _PAGE_BIT_USER      2   /* userspace addressable */
#define _PAGE_BIT_PWT       3   /* page write through */

Arch/x86/include/uapi/asm/processor-flags.hCR0を定義し、特にPGビット位置を定義します:

#define X86_CR0_PG_BIT      31 /* Paging */

書誌

無料:

  • rutgers-pxk-416 「メモリ管理:講義ノート」の章

    古いOSで使用されているメモリ構成手法の歴史的レビュー。

非フリー:

  • bovet05 「メモリアドレス指定」の章

    X86メモリアドレッシングの合理的な紹介。いくつかの良い簡単な例がありません。

非常に短い、高レベルの答えは次のとおりです。

X86プロセッサは、いくつかの可能なモード(大まかに言うと、実際の保護された64ビット)の1つで動作します。各モードは、いくつかの可能なメモリアドレス指定モデルのいずれかを使用できます(ただし、すべてのモードがすべてのモデルを使用できるわけではありません)。つまり、リアルモードアドレス指定、セグメントアドレス指定、フラットリニアアドレス指定です。

現代の世界では、保護モードまたは64ビットモードでのフラットリニアアドレッシングのみが関連しており、2つのモードは基本的に同じですが、主な違いはマシンワードのサイズ、したがってアドレス可能なメモリ量です。

現在、メモリアドレス指定モードは、マシン命令のメモリオペランドに意味を与えます(mov DWORD PTR [eax], 25は、値が25の32ビット(別名dword)整数を、アドレスがeax 32ビットレジスタに格納されているメモリに格納します。フラットリニアアドレッシングでは、eaxのこの数値は、ゼロから最大値までの単一の連続した範囲で実行できます(この場合は232− 1)。

ただし、フラットリニアアドレッシングはpagedまたはnot pagedのいずれかです。ページングを使用しない場合、アドレスは物理メモリを直接参照します。 Withページング、プロセッサのメモリ管理ユニット(またはMMU)は、目的のアドレス(現在は仮想アドレスと呼ばれる)をルックアップメカニズム、いわゆるページテーブル、および物理アドレスとして解釈される新しい値を取得します。元の操作は、ユーザーが仮想アドレスしか見ることができない場合でも、物理メモリ内のこの新しい変換されたアドレスで動作するようになりました。

ページングの主な利点は、ページテーブルがオペレーティングシステムによって管理されることです。したがって、オペレーティングシステムは、「タスクの切り替え」など、ページテーブルを任意に変更および置換できます。 「プロセス」ごとに1つのページテーブルのコレクション全体を保持でき、特定のプロセスが特定のCPUで実行されると判断するたびに、プロセスのページテーブルをそのCPUのMMU(各CPUには独自のページテーブルセットがあります。)結果は、各プロセスが独自の仮想を参照することです。物理メモリに直接アクセスできないため、他のプロセスのメモリについては決して知りません。

ページテーブルは、通常のメモリに格納されたネストされたツリーのようなデータ構造であり、OSによって書き込まれますが、ハードウェアによって直接読み取られるため、形式は固定されています。特別なCPU制御レジスタを設定して、最上位テーブルを指すことにより、MMUにロードされます。CPUはTLBと呼ばれるキャッシュを使用してルックアップを記憶するため、同じ数ページは、TLBミスの理由と通常のデータキャッシュの理由から、分散アクセスよりもはるかに高速です。 TLB。

また、プロセスがページングを無効にするか、ページテーブルを変更しようとするかもしれないと心配する場合は、x86が特権レベル(「リング」と呼ばれる)を実装し、ユーザーコードがCPUのページテーブルを変更するには低すぎる特権レベル。

15
Kerrek SB