目次と内容が充実したこの回答のバージョン。
報告されたエラーを修正します。大規模な変更を加えたり、不足している部分を追加したりする場合は、自分の答えに基づいて変更を加え、適切な担当者に依頼してください。小さな編集は直接マージできます。
サンプルコード
最小限の例:https : //github.com/cirosantilli/x86-bare-metal-examples/blob/5c672f73884a487414b3e21bd9e579c67cd77621/paging.S
プログラミングの他のすべてと同様に、これを本当に理解する唯一の方法は、最小限の例で遊ぶことです。
これを「難しい」主題にするのは、独自の小さなOSを作成する必要があるため、最小限の例が大きいということです。
Intelマニュアル
例がないと理解することはできませんが、できるだけ早くマニュアルに慣れるようにしてください。
Intelは、Intel Manual Volume 3システムプログラミングガイド-325384-056US 2015年9月の第4章「ページング」でページングについて説明しています。
特に興味深いのは、主要なデータ構造を示す図4-4「32ビットページングを使用したCR3およびページング構造エントリのフォーマット」です。
MMU
ページングは、CPU のメモリ管理ユニット(MMU)部分によって行われます。他の多くのように(例えば x87コプロセッサ、APIC)、これは初期の頃は個別のチップでしたが、後でCPUに統合されました。しかし、この用語はまだ使用されています。
一般的な事実
論理アドレスは、「通常の」ユーザーランドコードで使用されるメモリアドレスです(たとえば、 rsi
でmov eax, [rsi]
)。
最初のセグメンテーションはそれらを線形アドレスに変換し、次にページングは線形アドレスを物理アドレスに変換します。
(logical) ------------------> (linear) ------------> (physical)
segmentation paging
ほとんどの場合、物理アドレスは実際のRAMハードウェアメモリセルにインデックスを付けると考えることができますが、次の理由により、これは100%正しくありません。
ページングはプロテクトモードでのみ使用できます。プロテクトモードでのページングの使用はオプションです。ページングがオンの場合PG
、cr0
レジスタが設定さです。
ページングとセグメンテーション
ページングとセグメンテーションの主な違いの1つは次のとおりです。
- ページングはRAMをページと呼ばれる同じサイズのチャンクに分割します
- セグメンテーションはメモリを任意のサイズのチャンクに分割します
チャンクのサイズが等しいと物事が管理しやすくなるため、これがページングの主な利点です。
ページングは非常に人気が高まり、64ビットモードのx86-64では、IA32をエミュレートする互換モードでのみ存在する新しいソフトウェアの主要な動作モードである、セグメンテーションのサポートが削除されました。
応用
ページングは、最新のOSにプロセス仮想アドレス空間を実装するために使用されます。仮想アドレスを使用すると、OSは次の方法で単一のRAMに2つ以上の同時プロセスを適合させることができます。
- 両方のプログラムが他のプログラムについて何も知る必要がない
- 両方のプログラムのメモリは、必要に応じて拡大および縮小できます
- プログラム間の切り替えは非常に高速です
- あるプログラムが別のプロセスのメモリにアクセスすることはできません
ページングは歴史的にはセグメンテーション後に行われ、可変長セグメントではなくページの固定サイズのメモリチャンクを管理する方が簡単であるため、Linuxなどの最新のOSで仮想メモリを実装するために主に置き換えられました。
ハードウェアの実装
プロテクトモードでのセグメンテーションと同様に(セグメントレジスタを変更すると、GDTまたはLDTからのロードがトリガーされます)、ページングハードウェアはメモリ内のデータ構造を使用してジョブ(ページテーブル、ページディレクトリなど)を実行します。
これらのデータ構造の形式はハードウェアによって固定されていますが、これらのデータ構造をRAMに正しく設定および管理し、ハードウェアにそれらを見つける場所を(ハードウェアに指示する)のはOSの責任です。cr3
です。
他の一部のアーキテクチャでは、ページングがほぼソフトウェアの手に委ねられているため、TLBミスはOS提供の関数を実行して、ページテーブルをウォークし、新しいマッピングをTLBに挿入します。これにより、OSがページテーブル形式を選択できるようになりますが、ハードウェアがページウォークと他の命令の順不同の実行をx86のようにオーバーラップさせることができなくなります。
例:簡略化された単一レベルのページング方式
これは、ページングが単純化された仮想メモリ空間を実装するため x86アーキテクチャのバージョン。
ページテーブル
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の初期位置。
サンプル値:0x00000000
、0x12345678
など
これらの値を決定するのは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
アクセスを高速化することを選択し、残りのビットを後で使用するために予約するだけです。の実際の値L
x86では32ビットです。
シングルレベルスキームでのアドレス変換
OSによってページテーブルがセットアップされると、ハードウェアによってリニアアドレスと物理アドレス間のアドレス変換が行われます。
OSは、アクティブプロセス1に望んでいるとき、それはセットcr3
にPT1
、プロセスのいずれかのテーブルの開始を。
プロセス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 * L
(1
ページパーツのため)。
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:
いずれの場合も、OSは問題に対処するためにページフォールトを生成したアドレスを知る必要があります。これが、優れたIA32開発者cr2
がページ違反が発生するたびにそのアドレスにの値を設定する理由です。例外ハンドラーはcr2
、アドレスを取得するために調べるだけです。
簡略化
この例を理解しやすくするための現実の簡略化:
すべての実際のページング回路はスペースを節約するためにマルチレベルページングを使用しますが、これは単純なシングルレベルスキームを示しています。
ページテーブルには、20ビットアドレスと1ビット存在フラグの2つのフィールドしか含まれていませんでした。
実際のページテーブルには合計12のフィールドが含まれているため、省略されているその他の機能があります。
例:マルチレベルのページングスキーム
単一レベルのページング方式の問題は、RAMを大量に消費することです。4G/ 4K = プロセスあたり 1Mエントリ。各エントリが4バイト長の場合、プロセスごとに 4Mになります。これは、デスクトップコンピュータの場合でも多すぎます。ps -A | wc -l
現在244のプロセスを実行しているため、RAMが約1GB必要です。
このため、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
OSによってプロセス1に与えられたページテーブルPT1 = 0x10000000
(0x10000
* 4K):
RAM location physical address present
--------------- ----------------- --------
PT1 + 0 * L 0x00001 1
PT1 + 1 * L 0
PT1 + 2 * L 0x0000D 1
... ...
PT1 + 0x3FF * L 0x00005 1
OSによってプロセス1に与えられたページテーブルPT2 = 0x80000000
(0x80000
* 4K):
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
であるため、ハードウェアはのページテーブルのエントリ1を調べ0x80000000
、物理ページがアドレスにあることを通知します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のみをアドレス指定できます。
これは大規模サーバーの制限になり始めたため、インテルはPentium ProにPAEメカニズムを導入しました。
この問題を解決するために、Intelは4つの新しいアドレス行を追加しました。これにより、64GBに対応できます。
PAEがオンの場合、ページテーブルの構造も変更されます。それが変更される正確な方法は、PSEがオンかオフかによって異なります。
PAEは、のPAE
ビットを介してオン/オフされcr4
ます。
アドレス可能なメモリの合計が64GBであっても、個々のプロセスは最大4GBしか使用できません。ただし、OSは異なるプロセスを異なる4GBチャンクに置くことができます。
PSE
ページサイズ拡張。
ページの長さを4Kではなく4M(PAEがオンの場合は2M)にすることができます。
PSEはのPAE
ビットを介してオン/オフされcr4
ます。
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)と呼ばれる新しいページレベルです。これは、ページディレクトリを指し、32ビットの線形アドレスを入力するためです。PDPTも64ビット幅です。
cr3
アドレッシング効率のために、最初の4 GBのメモリ上にあり、32ビットの倍数で整列する必要があるPDPTを指すようになりました。これはcr3
、最初の4GBの2 ^ 32を完成させるために、32の倍数* 2 ^ 27に対して20:2 ^ 5ではなく27の有効ビットがあることを意味します。
PAEおよびPSE: 2 | 9 | 21
デザイナーは、1ページに収まるように9ビット幅のフィールドを維持することを決定しました。
これは23ビットを残します。PSEなしのPAEケースで物事を均一に保つためにPDPTに2を残すと、オフセット用に21が残されます。つまり、ページの幅は4Mではなく2Mになります。
TLB
変換先読みバッファ(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
への2回目の変換後、次の00007
よう00009
になります。
valid linear physical
------ ------- ---------
1 00003 00005
1 00007 00009
> 0 00000 00000
0 00000 00000
次に、00003
再度変換する必要がある場合、ハードウェアは最初にTLBを検索し、1回の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を使用すると、TLB レベルごとに1つのアクセスが必要になるため、変換が速くなります。これは、単純な32ビットスキームでは2、64ビットアーキテクチャでは3または4であることを意味します。
TLBは通常、連想メモリ(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は、invlpg
単一のTLBエントリを明示的に無効にする命令も提供します。他のアーキテクチャは、特定の範囲のすべてのエントリを無効にするなど、無効化された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.h
定義しCR0
、特にPG
ビット位置:
#define X86_CR0_PG_BIT 31 /* Paging */
参考文献
自由:
非フリー: