on
ページテーブルスキャニング
概要
ubuntu の 64bit において,あるプロセスのページテーブルをスキャンするカーネルモジュールを作成する.
Page Map Level 4
x86-64 では,ページング機構として,ページマップレベル 4 を導入しており,それぞれのアドレス変換テーブルは,PML4,PDPT,PD,PT と呼ばれる.
割り当てることができるページサイズ 4KB,2MB,1GB の3通りが用意されており,それぞれのページングは,次のようになる.
ページサイズが 4KB におけるアドレス変換
- PML4E ==> PDPE ==> PDE ==> PTE ==> Page
ページサイズが 2MB におけるアドレス変換
- PML4E ==> PDPE ==> PDE ==> Page
ページサイズが 1GB におけるアドレス変換
- PML4E ==> PDPE ==> Page
Linux の 64bit においても,4 段アドレス変換テーブルが採用されている.
Linux では,アドレス変換テーブルは,PGD,PUD,PMD,PTE と呼ばれ,それぞれのテーブルは,512 個のエントリから成る.
PGD へのポインタは,cr3 レジスタが担っており,この cr3 レジスタの値を変更することで,プロセス毎のアドレス空間の切り替えを実現している.
Linux におけるマクロ
Linux では,ページングに関わる多くのマクロが用意されており,以下のヘッダファイルをインクルードすることで利用可能となる.
<asm/pgtable.h>
自分の環境では,以下のパスにファイルがあった.
/usr/src/linux-headers-4.4.0-21/arch/x86/include/asm/pgtable.h
関連するマクロは以下の通りである.
macro/func | Contens |
---|---|
static inline int pgd_none(pgd_t pgd) | pgd エントリの有無を確認する |
static inline int pud_none(pud_t pud) | pud エントリの有無を確認する |
static inline int pmd_none(pmd_t pmd) | pmd エントリの有無を確認する |
static inline int pte_none(pte_t pte) | pte エントリの有無を確認する |
pgd_offset_k(address) | 仮想アドレスから対応する pgd エントリのポインタを返す |
static inline pud_t *pud_offset(pgd_t *pgd, unsigned long address) | pgd エントリへのポインタと仮想アドレスから対応する pud エントリのポインタを返す |
static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address) | pud エントリへのポインタと仮想アドレスから対応する pmd エントリのポインタを返す |
static inline pte_t *pte_offset_kernel(pmd_t *pmd, unsigned long address) | pmd エントリへのポインタと仮想アドレスから対応する pte エントリのポインタを返す |
static inline unsigned long pgd_page_vaddr(pgd_t pgd) | pgd エントリから pud の仮想アドレスを返す |
static inline unsigned long pud_page_vaddr(pud_t pud) | pud エントリから pmd の仮想アドレスを返す |
static inline unsigned long pmd_page_vaddr(pmd_t pmd) | pmd エントリから pte の仮想アドレスを返す |
実装
ページテーブルのスキャニングは非常に単純である. 前述した通り,Linux64bit では 4 段ページテーブルであり,それぞれのテーブルは 512 個のエントリから成る. よって,4 重ループ回せばよい.
|
|
実行結果
|
|
参考
2 章 Linux カーネル - メモリ管理 1 - SlideShare
Linux の仮想 - 物理アドレスと、カーネル空間の概要(その 2)