Linux内存映射

  • Post author:
  • Post category:linux



Linux

内存映射


一linux ARM虚拟地址


本文讨论的

linux

版本是

linux-2.6.26.5

,体系结构是

SMDK2410

,采用

ARM S3C2410



在该版本中虚拟地址是

32

位地址,采用四级页表结构,依次是



|  PGD  |  PUD  |  PMD  |  PTE  |  OFFSET  |


|PAGE_SHIFT|


|  PMD_SHIFT      |


|   PUD_SHIFT              |


|    PGDIR_SHIFT                   |





ARM

中只用到了两级页表,如下所示,其中

PMD

的值即为

PGD

的值:



0000 0000 000 | 0 0000 0000  | 0000 0000 0000


11



9



12


PGD              PTE

页内偏移



#define PMD_SHIFT            21


#define PGDIR_SHIFT         21



#define PTRS_PER_PTE             512         //PTE

页表数

9

位,共有

512

个项


#define PTRS_PER_PMD            1           //1


#define PTRS_PER_PGD             2048        //PGD

页表数

11

位,共有

2048

个项



二.虚拟地址与物理地址之间的映射


#define pgd_offset(mm, addr)      ((mm)->pgd+pgd_index(addr))


2.1 handle_mm_fault


int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,


unsigned long address, int write_access)


{



……………………


pgd = pgd_offset(mm, address);  //

取虚拟地址中的

pgd


// #define pgd_offset(mm, addr)   ((mm)->pgd+pgd_index(addr))


pud = pud_alloc(mm, pgd, address);  //

取虚拟地址中的

pud

,因为未用,所以即为

pgd


if (!pud)


return VM_FAULT_OOM;


pmd = pmd_alloc(mm, pud, address);//

取虚拟地址中的

pmd,

因为未用,所以即为

pgd


if (!pmd)


return VM_FAULT_OOM;


pte = pte_alloc_map(mm, pmd, address); //

取虚拟地址中的

pte


if (!pte)


return VM_FAULT_OOM;


return handle_pte_fault(mm, vma, address,

pte

, pmd, write_access);


}



2.2 handle_pte_fault


static inline int handle_pte_fault(struct mm_struct *mm,


struct vm_area_struct *vma, unsigned long address,


pte_t *pte, pmd_t *pmd, int write_access)


{


return do_anonymous_page(mm, vma, address,//suyi

匿名映射



pte

, pmd, write_access);


}


//

传入的参数

pte,

即为虚拟地址转化中的

pte

页表项,在

do_anonymous_page

函数中要实现的功能是分配一个空闲的页描述符,然后转化成物理地址,即为页框的物理地址,将该地址左移

PAGE_SHIFT



12

位),再赋给

pte

项,所以可以将虚拟地址与实际的物理地址联系起来。



static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,


unsigned long address, pte_t *page_table, pmd_t *pmd,


int write_access)


{


page = alloc_zeroed_user_highpage_movable(vma, address);


//

分配页描述符


entry = mk_pte(page, vma->vm_page_prot);


//

将页转成物理地址,再左移

12


// #define mk_pte(page,prot)  pfn_pte(page_to_pfn(page),prot)


// #define pfn_pte(pfn,prot)   (__pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot)))


// #define __pte(x)        (x)


page_table = pte_offset_map_lock(mm, pmd, address, &ptl);


//

即为

pte

项,下文再具体解析


set_pte_at(mm, address, page_table, entry);


//



entry

的值赋给

page_table


//#define set_pte_at(mm,addr,ptep,pteval) do { \


//     set_pte_ext(ptep, pteval, (addr) >= TASK_SIZE ? 0 : PTE_EXT_NG); \


// } while (0)


// #define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)


// #define cpu_set_pte_ext(ptep,pte,ext)       processor.set_pte_ext(ptep,pte,ext)


}



接上文的


#define pte_offset_map_lock(mm, pmd, address, ptlp)       \


({                                              \


spinlock_t *__ptl = pte_lockptr(mm, pmd);  \


pte_t *__pte = pte_offset_map(pmd, address);    \


*(ptlp) = __ptl;                            \


spin_lock(__ptl);                         \


__pte;                                        \


})



#define pte_offset_map(dir,addr)   (pmd_page_vaddr(*(dir)) + __pte_index(addr))



static inline pte_t *pmd_page_vaddr(pmd_t pmd)


{


unsigned long ptr;


ptr = pmd_val(pmd) & ~(PTRS_PER_PTE * sizeof(void *) – 1);


ptr += PTRS_PER_PTE * sizeof(void *);


//

以上两行有点疑问。


2(9)     *   2(2) = 2 (11)


return __va(ptr);


}



#define __pte_index(addr)     (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE – 1))


//

虚拟地址对应的

pte



版权声明:本文为lieye_leaves原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。