コグノスケ


link 未来から過去へ表示(*)  link 過去から未来へ表示

link もっと前
2008年5月2日 >>> 2008年5月2日
link もっと後

2008年5月2日

Linuxのページテーブルが見当たらないときがある

目次: Linux

Linuxのページディレクトリを見ていると0xc000 0000以降(※)はページディレクトリエントリがあるのに、その先のページテーブルがありません。なぜこれで動くんでしょう?

調べてみると、なるほど、トリックがありました。

LinuxはCR4のPSEビット(Page Size Extension、ビット4)を1にして、4MB/4KBどちらのサイズでも使えるようにしています。

確かめ方は、右Alt + ScrLockを押してレジスタダンプすると簡単でしょうか?以下のようなメッセージが出るはずです。Pentium Proから導入されているはずですので、未対応というのはそうそうないかと…。

レジスタダンプの例
Pid: 0, comm:              swapper
EIP: 0060:[<c01019bd>] CPU: 0
EIP is at default_idle+0x31/0x59
 EFLAGS: 00010246    Not tainted  (2.6.18-6-486 #1)
EAX: 00000000 EBX: 00000800 ECX: c1101040 EDX: c030e000
ESI: 00099100 EDI: c0302800 EBP: 003a7007 DS: 007b ES: 007b
CR0: 8005003b CR2: c02bd7f4 CR3: 07175000 CR4: 00000690
 [<c0101a1c>] cpu_idle+0x37/0x4c
 [<c03105fa>] start_kernel+0x270/0x272

肝心なのはCR4レジスタの値です。CR4レジスタが0x0000 0690、2進数だと0110 1001 0000ですから、ビット4(ビット0から数えるので、右から5番目)のPSEビットが1になっていることが分かります。

(※)カーネルが使うリニアアドレス用のマッピングで、物理メモリ先頭896MB分を仮想アドレス0xc000 0000へマップしています。
物理: 0x0000 0000 .... 0x37ff ffff
リニア: 0xc000 0000 .... 0xf7ff ffff
というマッピングです。

二種類のページ

じゃあ4MBと4KBの区別はどこでやってるのさ?というと、ページディレクトリテーブルの要素である、ページディレクトリエントリでやっています。インテルのマニュアルによれば、ページディレクトリエントリの構成は以下のようになっています。


32ビットx86のページディレクトリエントリ

細かい説明は後述しますが、PSビット(Page Size、ビット7)が1になっていれば4MBのページを指し、0ならば4KBのページを使うことを意味します。

128MBの実メモリを割り当てた仮想マシン上でLinuxを起動して、適当なプロセスのページテーブルをダンプしてみると、768要素目から以下のようなページディレクトリエントリが見受けられます。

128MBしか実メモリがないので、768〜799番目(4MB x 32ページ)までしか値が埋まりません。余った800番目以降は別の用途に使われます。

カーネルのページディレクトリテーブルの一部
  768: 0x000001e3, 0x004001e3, 0x008001e3, 0x00c001e3, 
  772: 0x010001e3, 0x014001e3, 0x018001e3, 0x01c001e3, 
  776: 0x020001e3, 0x024001e3, 0x028001e3, 0x02c001e3, 
  780: 0x030001e3, 0x034001e3, 0x038001e3, 0x03c001e3, 
  784: 0x040001e3, 0x044001e3, 0x048001e3, 0x04c001e3, 
  788: 0x050001e3, 0x054001e3, 0x058001e3, 0x05c001e3, 
  792: 0x060001e3, 0x064001e3, 0x068001e3, 0x06c001e3, 
  796: 0x070001e3, 0x074001e3, 0x078001e3, 0x07c001e3, 
  800: 0x00000000, 0x00000000, 0x07baf067, 0x00000000, 

772番目を例に取ると、0xc100 0000〜0xc13f ffffの4MB分のアドレスをマップしています。0x010001e3は2進数だと0001 0000 0000 0000 0001 1110 0011ですので、先ほどの図に当てはめてみると、

ビット0: P(存在): 1
4KBページの時はページテーブルがロードされていることを表します。4MBページの場合はページが存在していることを表します。
ビット1: R/W(読み取り/書き込み): 1
0は読み取り専用、1なら書き込み可能であることを表します。
CR0のWPビット(書き込み保護、ビット16)を1にすると、スーパバイザからユーザの書き込み禁止ページに書き込んだときに、ページフォルト例外が発生するようになります。コピーオンライト機能の実現に役立ちます。
  • ページテーブルエントリEAが指すページAをコピーせよという命令に対し、実際はコピーせず、エントリEAを読み取り専用にし、さらに同じページを指すエントリEBを読み取り専用で作ります。
  • 読み取り時
    • エントリEAの指すページAへの読み取りは今まで通りです。
    • エントリEBは書き込みがない限りページAと同じ内容なので、読み取りだけならばエントリの指すページを変更する必要はありません。
  • 書き込み時
    • エントリEAの指すページAに書き込もうとすると、ページフォルト例外を発生させてページをコピーし、ページA' を作ります(書き込み時のコピー、コピーオンライト、COWとも)。
      以降はエントリEAは書き込み可能、指す先はページA' です(EA -> A', EB -> A)。
      そうしないとエントリEBが指すページAを使っている人が混乱します。エントリEBを使っている人から見ると、何もしていないのに(エントリEAの指すページAへの書き込み発生時に)勝手にページの内容が変わったように見えます。
    • 同様にエントリEBの指すページAに書き込もうとしても、ページフォルト例外が発生します。そのとき初めてページAの複製、ページBを作ります(コピーオンライト)。
      以降、エントリEBは書き込み可能、指す先はページBに変更されます。(EA -> A, EB -> B)
      ただし既にエントリEAがページAを指していないことが分かっているなら、コピー作業は無駄なのでしません。エントリEBはページAを指したままで、書き込み可能に変更されるだけです(EA -> A', EB -> A)。
ビット2: U/S(ユーザ/スーパバイザ): 0
0はスーパバイザ用の領域を意味し、1はユーザ/スーパバイザ用の領域を意味します。
ビット3: PWT(ページライトスルー): 0
0ならページへの書き込み時に、可能ならキャッシュのみを更新(ライトバック方式)します。1にするとキャッシュとメモリを同時に更新(ライトスルー方式)します。PCDが1のときは、そもそもキャッシュを使わないので無意味です。
  • CR0のNWビット(非ライトバック、ビット29)が1だと、ライトスルー方式が採用されます。こちらが優先です。
  • CR3のPWTビット(ページライトスルー、ビット3)が1だと、ライトスルー方式が採用されます。これはページディレクトリエントリだけのキャッシュ方式に適用されます。
しかしMTRRの設定とも関係します。ライトバックとライトスルーの設定がかち合ったときは、ライトスルーが優先されます。
ビット4: PCD(ページキャッシュ無効): 0
0にするとページをキャッシュします。1にするとページをキャッシュしなくなります。I/Oメモリなど、キャッシュしても意味がない領域などに使うそうです。
  • CR0のCDビット(キャッシュ無効、ビット30)が1だと、メモリに対するキャッシュ全てが無効になります。こちらが優先です。
  • CR3のPCDビット(ページキャッシュ無効、ビット4)が1だと、ページ単位のキャッシュは無効になります。これはページディレクトリエントリだけのキャッシュ方式に適用されます。
キャッシュの設定はMTRRの設定とも関係します。ライトバックとライトスルーがかち合ったときは、ライトスルーが優先されます。
ビット5: A(アクセス): 1
ページ番号が指しているページに読み込み、書き込みがあると1にセットされます。
ビット6: D(ダーティ): 1
ページ番号が指しているページに書き込みがあると1にセットされます。
ビット7: PS(ページサイズ): 1
0なら4KBページ、1なら4MBページを表します。ページディレクトリエントリ(一段目のテーブル)だけで有効です。
Linuxではページテーブル(二段目のテーブル)でアクセス権のないページを表すために、Pビットと合わせて使っています(当然CPUは無視します)。P:PS = 0:0なら存在しないページ、P:PS = 0:1ならアクセス権のないページを表します。
(4KBページ、ページテーブルエントリ(二段目のテーブル)のみ)Pentium Pro以降であれば、このビットはPATビットを表します。このビットが1であればPCDおよびPWTの2ビットで4つのメモリ属性(ライトバック、ライトスルー、MTRR優先のキャッシュ無効、MTRR上書きのキャッシュ無効)を表します。このビットが0であれば、従来と同じです。
ビット8: G(グローバル): 1
0ならTLBフラッシュの時に消されます、1なら消されずにずっと残ります。
CR4のPGEビット(グローバルページ有効、ビット7)を1にしないと意味がありません。
ビット12: PAT(ページ属性テーブル): 0
(4MBページのみ)0ならPAT機能を無効にして、1ならPAT機能を有効にします。
ビット31-22: ページ番号: 4(0x4)
先頭から16MB(4MB x 4個目)の位置にある4MB分の領域(0x0100 0000〜0x013f ffff)をマップすることを表します。

なぜ使い分けるのか

4MBのページングを使っているのはカーネル用のリニアアドレス(0xc000 0000〜)だけのようです。他のページディレクトリエントリにはPSビットが設定されていません。ページ番号の指す先には、もう一段ページテーブルがあり、その先にやっと4KBのページがあります。

理由ですが、インテルのマニュアルによると4MBと4KBのページは別のTLBに置かれるので、カーネルのように頻繁に使うコードやデータは4MBページに置いておくと、フラッシュされなくて良いよ、ってなことが書いてあります。また、ページサイズが大きければTLBミスも少ないよ、ってな解説もあります。恐らくそんな理由です。


あるプロセスのページディレクトリテーブル

絵にしてみるとこんな感じでしょうか。間違ってるかも…。

サイズはアドレスの範囲を意味するのではなくてテーブル自体の大きさのことです。ページディレクトリテーブル(一段目のテーブル)も、ページテーブル(二段目のテーブル)も 4バイトx 1024要素なので同じ4KBですよ、って言いたかっただけです。

編集者:すずき(2023/04/29 21:56)

コメント一覧

  • hdkさん(2008/05/03 14:11)
    あれ? PAT は? (・∀・)

    A ビットは書き込みがあっても 1 になりますね。A, D ビットは実際は TLB とも深く関わってます。R/W と CR0 の WR フラグも関係があります。
  • すずきさん(2008/05/03 16:23)
    PAT に MTRR ですか…正直、勘弁してくださいという領域ですorz
    良くわからんくせに、書き足しました。内容には全く自信ないです。
  • hdkさん(2008/05/03 18:07)
    MTRR ってわけわからんですよねー。
    たしか CR3 のキャッシュの設定って CPU がページディレクトリエントリを読み書きする時にキャッシュの設定をどうするかじゃなかったですか?
  • すずきさん(2008/05/03 19:19)
    >MTRR
    マニュアル読んだけど理解させる気ないね。難しすぎ。
    >CR3
    うお、早速間違ってた。直します。
  • すずきさん(2008/05/03 19:22)
    それにしても読みづらい日記だな…。
    hdk氏には感謝感謝。
  • すずきさん(2010/05/07 10:30)
    かなりいまさらになりますが…日記でやってるページテーブルダンプのやり方(bash, zsh 専用)
    # i=0x6e94000; dd if=/dev/mem skip=$((i / 4096)) bs=4096 count=1 | hexdump -v -e '" " 4/4 "0x%08x, " "\n"' | nl -v 0 -i 4 -s :
    です。

    i に代入する値は、
    ・適当にレジスタダンプ
    ・dmesg で結果見る
    ・CR3 レジスタ(= ページディレクトリエントリの値です)
    を確認してそれを 16進でそのまま入れてください。
  • すずきさん(2010/05/07 10:35)
    用語がふにゃふにゃしててすみません。訂正します。
    ページ「テーブル」のダンプじゃなくて、ページ「ディレクトリテーブル」のダンプです。
    日記に書いたとおり 4MB ページの場合は、ページテーブルはありません。
open/close この記事にコメントする



link もっと前
2008年5月2日 >>> 2008年5月2日
link もっと後

管理用メニュー

link 記事を新規作成

<2008>
<<<05>>>
----123
45678910
11121314151617
18192021222324
25262728293031

最近のコメント5件

  • link 24年4月22日
    hdkさん (04/23 20:52)
    「おお... うちのHHFZ4310より後...」
  • link 20年6月19日
    すずきさん (04/06 22:54)
    「ディレクトリを予め作成しておけば良いです...」
  • link 20年6月19日
    斎藤さん (04/06 16:25)
    「「Preferencesというメニューか...」
  • link 21年3月13日
    すずきさん (03/05 15:13)
    「あー、このプログラムがまずいんですね。ご...」
  • link 21年3月13日
    emkさん (03/05 12:44)
    「キャストでvolatileを外してアクセ...」

最近の記事20件

  • link 24年4月22日
    すずき (04/23 20:13)
    「[仕事部屋の照明が壊れた] いきなり仕事部屋のシーリングライトが消えました。蛍光管の寿命にしては去年(2022年10月19日の...」
  • link 24年4月17日
    すずき (04/18 22:44)
    「[VSCodeとMarkdownとPlantUMLのローカルサーバー] 目次: LinuxVSCodeのPlantUML Ex...」
  • link 23年4月10日
    すずき (04/18 22:30)
    「[Linux - まとめリンク] 目次: Linuxカーネル、ドライバ関連。Linuxのstruct pageって何?Linu...」
  • link 20年2月22日
    すずき (04/17 02:22)
    「[Zephyr - まとめリンク] 目次: Zephyr導入、ブート周りHello! Zephyr OS!!Hello! Ze...」
  • link 24年4月16日
    すずき (04/17 02:05)
    「[Zephyr SDKのhosttoolsは移動してはいけない、その2 - インストール時のバイナリ書き換え] 目次: Zep...」
  • link 24年4月15日
    すずき (04/17 01:47)
    「[Zephyr SDKのhosttoolsは移動してはいけない、その1 - 移動させると動かなくなる] 目次: ZephyrZ...」
  • link 24年4月11日
    すずき (04/17 00:37)
    「[VScodeとAsciiDocとKrokiローカルサーバー] 目次: LinuxAsciiDoc ExtensionはAsc...」
  • link 24年4月12日
    すずき (04/16 00:12)
    「[台湾東部沖地震に寄付] ささやかではありますが台湾東部沖地震に寄付しました。日本の赤十字社→台湾の赤十字(正式名称...」
  • link 22年9月3日
    すずき (04/16 00:08)
    「[MarkDownのその向こう] 目次: Linux簡単なドキュメントやメモはMarkDownで書くことが多いですが、気合を入...」
  • link 22年9月4日
    すずき (04/16 00:08)
    「[Asciidocをさらに活用] 目次: Linux前回(2022年9月3日の日記参照)、Asciidocのプレビュー環境の設...」
  • link 24年3月19日
    すずき (04/16 00:07)
    「[モジュラージャックの規格] 目次: Arduino古くは電話線で、今だとEthernetで良く見かけるモジュラージャックとい...」
  • link 23年6月2日
    すずき (04/16 00:07)
    「[Arduino - まとめリンク] 目次: Arduino一覧が欲しくなったので作りました。 M5Stackとesp32とA...」
  • link 24年4月9日
    すずき (04/12 12:44)
    「[初めて作ったボード動作せず(手で直した)] 目次: Arduino以前(2024年3月24日の日記参照)発注して、全く動ない...」
  • link 24年4月2日
    すずき (04/12 11:00)
    「[KiCadが動かなくなったのでビルド] 目次: ArduinoDebian Testingなマシンをapt-get upgr...」
  • link 24年4月3日
    すずき (04/12 11:00)
    「[初めて作ったボード動作せず(燃えた)] 目次: Arduino以前(2024年3月24日の日記参照)発注したPCBが届いたの...」
  • link 24年3月24日
    すずき (04/12 11:00)
    「[PCBを設計して注文] 目次: Arduinoシューティングの練習でいつもお世話になっているTARGET-1秋葉原店に、6つ...」
  • link 24年3月25日
    すずき (03/26 03:20)
    「[Might and Magic Book One TASのその後] 目次: Might and Magicファミコン版以前(...」
  • link 21年10月4日
    すずき (03/26 03:14)
    「[Might and Magicファミコン版 - まとめリンク] 目次: Might and Magicファミコン版TASに挑...」
  • link 24年3月18日
    すずき (03/19 11:47)
    「[画面のブランクを無効にする] 目次: LinuxROCK 3 model CのDebian bullseyeイメージは10分...」
  • link 24年3月3日
    すずき (03/19 11:07)
    「[解像度の設定を保存する] 目次: LinuxRaspberry Pi 3 Model B (以降RasPi 3B)のHDMI...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 04/23 20:52