本文讨论的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))
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);
}
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项