Linuxカーネルは、32ビットシステムに適しただけでなく、64ビットシステムのためだけではない、一般的な4ページのページングモデルを、使用しています。ページングユニットはMMU(Memory Management Unit)の一部であり、リニアアドレスを物理アドレスに変換します。
物理アドレスへの仮想アドレス変換のプロセスをシミュレートするためのカーネルモジュールを作成しました。私はあなたがページングシステムの原理を知っていると仮定しています。
static void get_pgtable_macro(void)
{
printk("PAGE_OFFSET = 0x%lx\n", PAGE_OFFSET);
printk("PGDIR_SHIFT = %d\n", PGDIR_SHIFT);
printk("PUD_SHIFT = %d\n", PUD_SHIFT);
printk("PMD_SHIFT = %d\n", PMD_SHIFT);
printk("PAGE_SHIFT = %d\n", PAGE_SHIFT);
printk("PTRS_PER_PGD = %d\n", PTRS_PER_PGD);
printk("PTRS_PER_PUD = %d\n", PTRS_PER_PUD);
printk("PTRS_PER_PMD = %d\n", PTRS_PER_PMD);
printk("PTRS_PER_PTE = %d\n", PTRS_PER_PTE);
printk("PAGE_MASK = 0x%lx\n", PAGE_MASK);
}
static unsigned long vaddr2paddr(unsigned long vaddr)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
unsigned long paddr = 0;
unsigned long page_addr = 0;
unsigned long page_offset = 0;
pgd = pgd_offset(current->mm, vaddr);
printk("pgd_val = 0x%lx\n", pgd_val(*pgd));
printk("pgd_index = %lu\n", pgd_index(vaddr));
if (pgd_none(*pgd)) {
printk("not mapped in pgd\n");
return -1;
}
pud = pud_offset(pgd, vaddr);
printk("pud_val = 0x%lx\n", pud_val(*pud));
if (pud_none(*pud)) {
printk("not mapped in pud\n");
return -1;
}
pmd = pmd_offset(pud, vaddr);
printk("pmd_val = 0x%lx\n", pmd_val(*pmd));
printk("pmd_index = %lu\n", pmd_index(vaddr));
if (pmd_none(*pmd)) {
printk("not mapped in pmd\n");
return -1;
}
pte = pte_offset_kernel(pmd, vaddr);
printk("pte_val = 0x%lx\n", pte_val(*pte));
printk("pte_index = %lu\n", pte_index(vaddr));
if (pte_none(*pte)) {
printk("not mapped in pte\n");
return -1;
}
/* Page frame physical address mechanism | offset */
page_addr = pte_val(*pte) & PAGE_MASK;
page_offset = vaddr & ~PAGE_MASK;
paddr = page_addr | page_offset;
printk("page_addr = %lx, page_offset = %lx\n", page_addr, page_offset);
printk("vaddr = %lx, paddr = %lx\n", vaddr, paddr);
return paddr;
}
static int __init v2p_init(void)
{
unsigned long vaddr = 0;
printk("vaddr to paddr module is running..\n");
get_pgtable_macro();
printk("\n");
vaddr = (unsigned long)vmalloc(1000 * sizeof(char));
if (vaddr == 0) {
printk("vmalloc failed..\n");
return 0;
}
printk("vmalloc_vaddr=0x%lx\n", vaddr);
vaddr2paddr(vaddr);
printk("\n\n");
vaddr = __get_free_page(GFP_KERNEL);
if (vaddr == 0) {
printk("__get_free_page failed..\n");
return 0;
}
printk("get_page_vaddr=0x%lx\n", vaddr);
vaddr2paddr(vaddr);
return 0;
}
static void __exit v2p_exit(void)
{
printk("vaddr to paddr module is leaving..\n");
vfree((void *)vaddr);
free_page(vaddr);
}
Get_pgtable_macro()は、現在のシステムのページング機構の一部のマクロを印刷します。
vaddr2paddr()を呼び出すと、仮想アドレスの物理アドレスに変換され、カーネル空間内のメモリ空間の割り当てでvmalloc()が呼び出されます。
- __get_free_pages()でカーネル空間にフレームを割り当てることによって、仮想アドレスを物理アドレスに変換するには、vaddr2paddr()を使用します。
- 要求されたメモリ空間を、それぞれvfree()とfree_page()で解放します。次のように
Vaddr2paddr()が実行される:
メモリ記述子MM及び線形アドレスVADDRを渡し、pgd_offsetによってページグローバルカタログエントリの線形アドレスPGDを計算します。次に、pgdが指すページのグローバルカタログエントリを表示します。
ページの親ディレクトリエントリの線形アドレスpudをpud_offsetで計算し、パラメータをページのグローバルディレクトリエントリのリニアアドレスpgdとリニアアドレスvaddrに渡します。次に、親ディレクトリー項目を参照してpudを印刷します。
pmd_offsetによってページ中間ディレクトリエントリのリニアアドレスpmdを計算し、パラメータをリニアアドレスpudおよび親ディレクトリエントリのリニアアドレスvaddrに渡します。次に、pmdディレクトリエントリに参照されるページの中央を表示します。
リニアアドレスpteによって計算されたpte_offset_kernel pte_offset_kernelは、リニアアドレスpmdのリニアアドレスとアドレスvaddrのディレクトリエントリの中間のパラメータです。その後、pteが指すページテーブル項目を印刷します。
pte_val(* pte)ページテーブルエントリとPAGE_MASKフェーズを削除し、結果はページの物理アドレスにアクセスします。 vaddr &〜PAGE_MASKは、リニアアドレスオフセットフィールドを取得するために使用されます。 2つまたは最終的な物理アドレス計算を実行する。
印刷する物理アドレス
これが動作しているようだが、私は疑問を持っている:私は私のシステムでこの操作を行うとき、私も全範囲をmlockingた後、私の物理RAM上の物理アドレスを取得します。物理アドレスは0から始まっていませんか? – user318904