码迷,mamicode.com
首页 > 系统相关 > 详细

Linux kernel 内存 - 页表映射

时间:2019-01-21 12:06:57      阅读:1152      评论:0      收藏:0      [点我收藏+]

标签:依赖   map   数据结构   determine   src   sign   状态   clear   code   

0. Intro

如下是在32位下的情况,32位下,只有三级页表:PGD,PMD,PTE

在64位情况下,会有四级页表:PGD,PUD,PMD,PTE

但是原理基本上是一样的,本文主要是想记录一下页表转换中的几个 基本概念宏:SHITF,SIZE,MASK以及之间的转换。

1. Linux虚拟内存三级页表

Linux虚拟内存三级管理由以下三级组成:

  • PGD: Page Global Directory (页目录)
  • PMD: Page Middle Directory (页目录)
  • PTE:  Page Table Entry  (页表项)

    
每一级有以下三个关键描述宏:

  • SHIFT
  • SIZE
  • MASK

如页的对应描述为:

/* PAGE_SHIFT determines the page size  asm/page.h */
#define PAGE_SHIFT      12
#define PAGE_SIZE       (_AC(1,UL) << PAGE_SHIFT)
#define PAGE_MASK       (~(PAGE_SIZE-1))

数据结构定义如下:

/* asm/page.h */
typedef unsigned long pteval_t;
 
typedef pteval_t pte_t;
typedef unsigned long pmd_t;
typedef unsigned long pgd_t[2];
typedef unsigned long pgprot_t;
 
#define pte_val(x)      (x)
#define pmd_val(x)      (x)
#define pgd_val(x)  ((x)[0])
#define pgprot_val(x)   (x)
 
#define __pte(x)        (x)
#define __pmd(x)        (x)
#define __pgprot(x)     (x)

2 Page Directory (PGD and PMD)

每个进程有它自己的PGD( Page Global Directory),它是一个物理页,并包含一个pgd_t数组。其定义见<asm/page.h>。 进程的pgd_t数据见 task_struct -> mm_struct -> pgd_t * pgd;    

ARM架构的PGD和PMD的定义如下<arch/arm/include/asm/pgtable.h>:

#define PTRS_PER_PTE  512 // PTE中可包含的指针<u32>数 (21-12=9bit) #define PTRS_PER_PMD  1 #define PTRS_PER_PGD  2048 // PGD中可包含的指针<u32>数 (32-21=11bit)

#define PTE_HWTABLE_PTRS (PTRS_PER_PTE) #define PTE_HWTABLE_OFF  (PTE_HWTABLE_PTRS * sizeof(pte_t)) #define PTE_HWTABLE_SIZE (PTRS_PER_PTE * sizeof(u32))

/*  * PMD_SHIFT determines the size of the area a second-level page table can map  * PGDIR_SHIFT determines what a third-level page table entry can map  */ #define PMD_SHIFT  21 #define PGDIR_SHIFT  21

虚拟地址SHIFT宏图:

技术分享图片

虚拟地址MASK和SIZE宏图:

技术分享图片

3. Page Table Entry

PTEs, PMDs和PGDs分别由pte_t, pmd_t 和pgd_t来描述。为了存储保护位,pgprot_t被定义,它拥有相关的flags并经常被存储在page table entry低位(lower bits),其具体的存储方式依赖于CPU架构。

每个pte_t指向一个物理页的地址,并且所有的地址都是页对齐的。因此在32位地址中有PAGE_SHIFT(12)位是空闲的,它可以为PTE的状态位。

PTE的保护和状态位如下图所示:

技术分享图片

4. 如何通过3级页表访问物理内存

为了通过PGD、PMD和PTE访问物理内存,其相关宏在asm/pgtable.h中定义。

  • gd_offset 

根据当前虚拟地址和当前进程的mm_struct获取pgd项的宏定义如下: 

/* to find an entry in a page-table-directory */
#define pgd_index(addr)     ((addr) >> PGDIR_SHIFT)  //获得在pgd表中的索引
 
#define pgd_offset(mm, addr)    ((mm)->pgd + pgd_index(addr)) //获得pmd表的起始地址
 
/* to find an entry in a kernel page-table-directory */
#define pgd_offset_k(addr)  pgd_offset(&init_mm, addr)
         ? pmd_offset
             根据通过pgd_offset获取的pgd 项和虚拟地址,获取相关的pmd项(即pte表的起始地址) 

/* Find an entry in the second-level page table.. */
#define pmd_offset(dir, addr)   ((pmd_t *)(dir))   //即为pgd项的值
        ? pte_offset


      根据通过pmd_offset获取的pmd项和虚拟地址,获取相关的pte项(即物理页的起始地址)

#ifndef CONFIG_HIGHPTE
#define __pte_map(pmd)      pmd_page_vaddr(*(pmd))
#define __pte_unmap(pte)    do { } while (0)
#else
#define __pte_map(pmd)      (pte_t *)kmap_atomic(pmd_page(*(pmd)))
#define __pte_unmap(pte)    kunmap_atomic(pte)
#endif
 
#define pte_index(addr)     (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
 
#define pte_offset_kernel(pmd,addr) (pmd_page_vaddr(*(pmd)) + pte_index(addr))
 
#define pte_offset_map(pmd,addr)    (__pte_map(pmd) + pte_index(addr))
#define pte_unmap(pte)          __pte_unmap(pte)
 
#define pte_pfn(pte)        (pte_val(pte) >> PAGE_SHIFT)
#define pfn_pte(pfn,prot)   __pte(__pfn_to_phys(pfn) | pgprot_val(prot))
 
#define pte_page(pte)       pfn_to_page(pte_pfn(pte))
#define mk_pte(page,prot)   pfn_pte(page_to_pfn(page), prot)
 
#define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)
#define pte_clear(mm,addr,ptep) set_pte_ext(ptep, __pte(0), 0)

其示意图如下图所示:

技术分享图片

Linux kernel 内存 - 页表映射

标签:依赖   map   数据结构   determine   src   sign   状态   clear   code   

原文地址:https://www.cnblogs.com/muahao/p/10297852.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!