詳解 Linux カーネル 第3版 & Linux Kernel ソースコード 0202

#概要#
今回はページングから始めます。
[前回](http://qiita.com/kobaru/items/00b24358a49e37c378cb)がセグメントに関してでしたので、
あまりlinuxのkernelと関係のない部分でした(一部関係していることがありますが...)
ページングはメモリ関連でリニアアドレス(仮想アドレス)から物理アドレスにアドレス解析する段階で、
それをlinuxではkernelで実装しています。

カーネルのページングに関しての記事を載せるので「詳解LINUXカーネル」では2.5章から
進めていきます。
そのページ（本の）はページングの仮想アドレスから物理アドレスへの変換の仕方、バッファ、
ページエントリ、レジスタの説明などですので飛ばします(ただの説明なので)。

では、2.5章Linuxのページング機構について

#知っておいたほうがいい用語#
**ページ**
>リニアアドレスを一定の大きさに領域にわけたもの
>[Linux Kernel Documents](http://sourceforge.jp/projects/linux-kernel-docs/wiki/0.6%E3%80%80%E3%83%A1%E3%83%A2%E3%83%AA%E7%AE%A1%E7%90%86)

**ページフレーム**
>RAM上を固定の多きさにわけたもの
>[メモリ管理、Buddyシステム、kmalloc、スラブアロケータ](http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2011/2012-01-17/)

**ページテーブル**
>リニアアドレスを物理アドレスにマッピングするデータ構造
>参考サイトでいいのが見つかりませんでした。

**cr3制御レジスタ**
>ページディレクトリの先頭アドレスを指定する
>[レジスタ](http://www.wiki.os-project.jp/?x86%2FRegister#j68fe188)

#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の場合２種類ありますが片方は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と処理は同じで引数の構造体のメンバをそのまま返します。

```


***
とりあえず、ここまでにしときます。
次はエントリの読み込み、変更のマクロと関数の続きをしていきます。