2016-12-11 19 views
0

enter image description hereLinuxカーネル - 物理アドレスを取得する方法(メモリ管理)? Linuxで

ページグローバルディレクトリアドレス(CR3 +指数)はpgd_offset()マクロを用いて算出することができるオフセット。

ページ上部ディレクトリのオフセットアドレスは、pud_offset()APIを使用して計算できます。

ページ中間ディレクトリオフセットアドレスは、pmd_offset()APIを使用して計算できます。

Page Tableエントリオフセットアドレスは、pte_offset_map()MACROを使用して計算できます。

次に、物理アドレスを取得する方法は? (yellow line in above picture)

物理アドレスを計算する関数またはMACROはありますか?

edit : x86-64 architecture.

答えて

2

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); 
    } 
  1. Get_pgtable_macro()は、現在のシステムのページング機構の一部のマクロを印刷します。

  2. vaddr2paddr()を呼び出すと、仮想アドレスの物理アドレスに変換され、カーネル空間内のメモリ空間の割り当てでvmalloc()が呼び出されます。

  3. __get_free_pages()でカーネル空間にフレームを割り当てることによって、仮想アドレスを物理アドレスに変換するには、vaddr2paddr()を使用します。
  4. 要求されたメモリ空間を、それぞれvfree()とfree_page()で解放します。次のように

Vaddr2paddr()が実行される:

  1. メモリ記述子MM及び線形アドレスVADDRを渡し、pgd_offsetによってページグローバルカタログエントリの線形アドレスPGDを計算します。次に、pgdが指すページのグローバルカタログエントリを表示します。

  2. ページの親ディレクトリエントリの線形アドレスpudをpud_offsetで計算し、パラメータをページのグローバルディレクトリエントリのリニアアドレスpgdとリニアアドレスvaddrに渡します。次に、親ディレクトリー項目を参照してpudを印刷します。

  3. pmd_offsetによってページ中間ディレクトリエントリのリニアアドレスpmdを計算し、パラメータをリニアアドレスpudおよび親ディレクトリエントリのリニアアドレスvaddrに渡します。次に、pmdディレクトリエントリに参照されるページの中央を表示します。

  4. リニアアドレスpteによって計算されたpte_offset_kernel pte_offset_kernelは、リニアアドレスpmdのリニアアドレスとアドレスvaddrのディレクトリエントリの中間のパラメータです。その後、pteが指すページテーブル項目を印刷します。

  5. pte_val(* pte)ページテーブルエントリとPAGE_MASKフェーズを削除し、結果はページの物理アドレスにアクセスします。 vaddr &〜PAGE_MASKは、リニアアドレスオフセットフィールドを取得するために使用されます。 2つまたは最終的な物理アドレス計算を実行する。

  6. 印刷する物理アドレス

+0

これが動作しているようだが、私は疑問を持っている:私は私のシステムでこの操作を行うとき、私も全範囲をmlockingた後、私の物理RAM上の物理アドレスを取得します。物理アドレスは0から始まっていませんか? – user318904

関連する問題