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
项