#概要#
今回はページングから始めます。
前回がセグメントに関してでしたので、
あまりlinuxのkernelと関係のない部分でした(一部関係していることがありますが...)
ページングはメモリ関連でリニアアドレス(仮想アドレス)から物理アドレスにアドレス解析する段階で、
それをlinuxではkernelで実装しています。
カーネルのページングに関しての記事を載せるので「詳解LINUXカーネル」では2.5章から
進めていきます。
そのページ(本の)はページングの仮想アドレスから物理アドレスへの変換の仕方、バッファ、
ページエントリ、レジスタの説明などですので飛ばします(ただの説明なので)。
では、2.5章Linuxのページング機構について
#知っておいたほうがいい用語#
ページ
リニアアドレスを一定の大きさに領域にわけたもの
Linux Kernel Documents
ページフレーム
RAM上を固定の多きさにわけたもの
メモリ管理、Buddyシステム、kmalloc、スラブアロケータ
ページテーブル
リニアアドレスを物理アドレスにマッピングするデータ構造
参考サイトでいいのが見つかりませんでした。
cr3制御レジスタ
ページディレクトリの先頭アドレスを指定する
レジスタ
#Linuxのページング機構#
Linuxは32bitアーキテクチャ、64bitアーキテクチャに対応できるよう、ページにアクセスする際、
4つのページテーブルを使用します。
- ページグローバルディレクトリ
- ページアッパーディレクトリ
- ページミドルディレクトリ
- ページテーブル
64bitの場合はこの4つのページテーブルを使用しますが、
32bitでは4つも必要ありませんので、ページグローバルディレクトリとページテーブルのみ使用して、
ページアッパーディレクトリとページミドルディレクトリはビット数を0にすることで、参照はしますが、
実質的にはページグローバルディレクトリとページテーブルのみで処理しています。
(64bitと32bit両方に対応できるようにするため)
このページテーブルとリニアアドレスを使って物理アドレスを取得します。
リニアアドレスのフィールド
- グローバルディレクトリ
- アッパーディレクトリ
- ミドルディレクトリ
- テーブルディレクトリ
- オフセット
それではリニアアドレスを表すマクロを見ていきます。
PAGE_SHIFT マクロ
これは、オフセット長を表しています。
この値はアーキテクチャごとに異なります。
Architecture--PAGE_SHIFT
x86--12 Linux/arch/x86/include/asm/page_types.h 7行目
ARM--12 Linux/arch/arm/include/asm/page.h 14行目
上記はv2.6.31.13の場合です。
x86-----------------12 Linux/arch/x86/include/asm/page_types.h 8行目
PowerPC(256K_PAGE)--18 Linux/arch/powerpc/include/asm/page.h 28行目
PowerPC(64K_PAGE)---16 Linux/arch/powerpc/include/asm/page.h 30行目
PowerPC(16K_PAGE)---14 Linux/arch/powerpc/include/asm/page.h 32行目
PowerPC(上記以外)----12 Linux/arch/powerpc/include/asm/page.h 34行目
ARM-----------------12 Linux/arch/arm/include/asm/page.h 14行目
数が多いのでこれぐらいに
上記はv3.18の場合です。
このように同じアーキテクチャでもオフセットが変わってきます。
PMD_SHIFT マクロ
リニアアドレスのオフセットフィールドとテーブルフィールドのビット長の合計です。
x86アーキテクチャでは21です。
上記はx86アーキテクチャとARMのみです。
x86の場合2種類ありますが片方は64bit、もう一方はレベル3の場合です。
レベル3はリニアアドレスのフィールドがオフセット抜きで3つある、
ということはページテーブルが3つということです。
PMD_SIZEマクロはページミドルディレクトリの1つのエントリマッピングできる領域、
つまり、ページテーブルのエントリ数を表します。
このPMD_SHIFTをマスクするのがPMD_MASKです。
なのでPMD_SHIFTが21の場合は0xffe00000です。
linux/arch/x86/include/asm/pgtable_32_types.h 12行目
linux/arch/x86/include/asm/pgtable_64_types.h 84行目
MASKの値は(~(PMD_SIZE - 1))
PUD_SHIFT マクロ
ページアッパーディレクトリの1つのエントリがマッピングできる領域サイズの対数(低数は2)
つまり、ページミドルディレクトリのなかのエントリ数
x86--30 linux/arch/x86/include/asm/pgtable_64_types.h 32行目
PUD_SIZEはページグローバルディレクトリの1つのエントリがマッピングできる領域サイズ
PUD_MASKはオフセット、テーブル、ミドルディレクトリ内をマスクします。
PGDIR_SHIFT マクロ
ページグローバルディレクトリ内の1エントリがマッピングできる領域サイズの対数
つまり、ページアッパーディレクトリ内のエントリ数
PGDIR_SIZEはページグローバルディレクトリの1つのエントリがマッピングできる領域サイズ
PGDIR_MASKはオフセット、テーブル、ミドルディレクトリ、アッパーディレクトリの全フィールドをマスクします。
これまではマクロの定義でした。アーキテクチャごとで異なるので覚えるのが大変です。
なので自分が使うアーキテクチャごとで覚えるのがいいかも、、、
##ページテーブルの処理##
pte_t、pmd_t、pud_t、pgd_tはそれぞれ
ページテーブル、ページミドルディレクトリ、
ページアッパーディレクトリ、ページグローバルディレクトリ内の
エントリの型を定義しています。
x86アーキテクチャでのpte_t、pmd_t、pud_t、pgd_tは64bitと3レベルとで異なります。
64bitの場合はlong型をtypedefで型名変換し pteval_t、 pmdval_t、pudval_t、pgdval_tのメンバのみを持つ
構造体をtypedef で型名変換で宣言しています。
v2.6.31.13
linux/arch/x86/include/asm/pgtable_64_types.h 10,11,12,13行目
typedef unsigned long pteval_t;
typedef unsigned long pmdval_t;
typedef unsigned long pudval_t;
typedef unsigned long pgdval_t;
linux/arch/x86/include/asm/pgtable_64_types.h 16行目
typedef struct { pteval_t pte; } pte_t;
linux/arch/x86/include/asm/pgtable_types.h 228行目
typedef struct { pmdval_t pmd; } pmd_t;
linux/arch/x86/include/asm/pgtable_types.h 207行目
typedef struct { pudval_t pud; } pud_t;
linux/arch/x86/include/asm/pgtable_types.h 189行目
typedef struct { pgdval_t pgd; } pgd_t;
v3.18でも同じようです(書いてある行は異なりますが...)
3レベルの場合はu64で64bitの型を64bit同様pteval_t、 pmdval_t、pudval_t、pgdval_tに型名変換し、
longメンバ2つ持つ構造体とunionした変数をtypedef で型名変換したものです。
linux/arch/x86/include/asm/pgtable-3level_types.h 10,11,12,13行目
typedef u64 pteval_t;
typedef u64 pmdval_t;
typedef u64 pudval_t;
typedef u64 pgdval_t;
linux/arch/x86/include/asm/pgtable-3level_types.h 18行目
typedef union {
struct {
unsigned long pte_low, pte_high;
};
pteval_t pte;
} pte_t;
他のものは64bitの場合と同じです
ページテーブルのエントリないの操作をするマクロや関数の主な処理を書いていきます。
none
対応するエントリの値が0の場合は1を、それ以外の場合は0を返します。
/*
pte_none
linux/arch/x86/include/asm/pgtable.h 312行目
*/
static inline int pte_none(pte_t pte)
{
return !pte.pte;
}
/*
pmd_none
linux/arch/x86/include/asm/pgtable.h 338行目
*/
static inline int pmd_none(pmd_t pmd)
{
/* Only check low word on 32-bit platforms, since it might be
out of sync with upper half. */
return (unsigned long)native_pmd_val(pmd) == 0;
}
/*
native_pmd_val関数
linux/arch/x86/include/asm/pgtable_types.h 235
*/
static inline pmdval_t native_pmd_val(pmd_t pmd)
{
return pmd.pmd;
}
なのでnative_pmd_valは引数のメンバをそのまま帰します
pud_noneはpmd_noneと同様の構造をしています
pud_none linux/arch/x86/include/asm/pgtable.h 406行目
native_pud_val linux/arch/x86/include/asm/pgtable_types.h 214行目
/*
pgd_none
linux/arch/x86/include/asm/pgtable.h 488
*/
static inline int pgd_none(pgd_t pgd)
{
return !native_pgd_val(pgd);
}
native_pgd_valの返り値を反転させて返します
native_pgd_valはnative_pmd_valと処理は同じで引数の構造体のメンバをそのまま返します。
とりあえず、ここまでにしときます。
次はエントリの読み込み、変更のマクロと関数の続きをしていきます。