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_SHIFT12位),再赋给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

你可能感兴趣的:(Linux内存映射)