内存管理数据结构的关系图
1 由mm数据结构和虚拟地址vaddr找到对应的VMA
find_vma()根据mm_struct和addr找到vma。
find_vma()首先在当前进程current->vmacache[]中查找addr对应的vma;如果未找到,则遍历mm->mm_rb红黑树,通过rb_node找到对应的vma,然后判断是否和addr吻合。
2 由page和VMA找到虚拟地址vaddr
vma_address()只针对匿名页面:
inline unsigned long
vma_address(struct page *page, struct vm_area_struct *vma)
{
unsigned long address = __vma_address(page, vma);
/* page should be within @vma mapping range */
VM_BUG_ON_VMA(address < vma->vm_start || address >= vma->vm_end, vma);
return address;
}
static inline unsigned long
__vma_address(struct page *page, struct vm_area_struct *vma)
{
pgoff_t pgoff = page_to_pgoff(page);
//根据page->index计算当前vma的偏移地址。
return vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
}
static inline pgoff_t page_to_pgoff(struct page *page)
{
if (unlikely(PageHeadHuge(page)))
return page->index << compound_order(page);
else
//page->index表示在一个vma中page的index。
return page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
}
4k页面的话
#define PAGE_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)
1UL << PAGE_SHIFT 即:0x0000 0001<<12 = 0x0000 1000 = 212 = 4K
page index
vma->vm_pgoff
viraddress = VMA->vm_start +(page->index - VMA->vm_pgoff)<< PAGE_SHIFT
3 由page找到所有映射的VMA
通过反向映射rmap系统rmap_walk()来实现,对于匿名页面来说是rmap_walk_anon()。
static int rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc)
{
struct anon_vma *anon_vma;
pgoff_t pgoff;
struct anon_vma_chain *avc;
int ret = SWAP_AGAIN;
anon_vma = rmap_walk_anon_lock(page, rwc);//由page->mapping找到anon_vma数据结构。
if (!anon_vma)
return ret;
pgoff = page_to_pgoff(page);
anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root, pgoff, pgoff) {-----遍历anon_vma->rb_root红黑树,取出avc数据结构。
struct vm_area_struct *vma = avc->vma;//每个avc数据结构指向每个映射的vma
unsigned long address = vma_address(page, vma);
if (rwc->invalid_vma && rwc->invalid_vma(vma, rwc->arg))
continue;
ret = rwc->rmap_one(page, vma, address, rwc->arg);
if (ret != SWAP_AGAIN)
break;
if (rwc->done && rwc->done(page))
break;
}
anon_vma_unlock_read(anon_vma);
return ret;
}
4 page和pfn之间的互换
page_to_pfn()和pfn_to_page()定义在linux/include/asm-generic/memory_mode.h中定义。
#define page_to_pfn __page_to_pfn
#define pfn_to_page __pfn_to_page
#define __pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET))//pfn减去ARCH_PFN_OFFSET得到page相对于mem_map的偏移。
#define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + \
ARCH_PFN_OFFSET)//page到mem_map的偏移加上ARCH_PFN_OFFSET得到当前page对应的pfn号。
5 pfn和paddr之间的互换
物理地址paddr和pfn的互换通过位移PAGE_SHIFT可以简单的得到,pfn坐移PAGE_SHIFT就可以得到paddr物理地址,PAGE_SHIFT 就是12。
PHYS_OFFSET - the physical address of the start of memory. */
/*
* Convert a physical address to a Page Frame Number and back
*/
#define __phys_to_pfn(paddr) ((unsigned long)((paddr) >> PAGE_SHIFT))
#define __pfn_to_phys(pfn) ((phys_addr_t)(pfn) << PAGE_SHIFT)
6 page和pte之间的互换
先由page到pfn,然后由pfn到pte,可以实现page到pte的转换。
1.page转化成pfn。
2.pfn怎么到pte?
#define pfn_pte(pfn,prot) __pte(__pfn_to_phys(pfn) | pgprot_val(prot))
#define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + \
ARCH_PFN_OFFSET)
由pte到page,通过pte_pfn()找到对应的pfn号,再由pfn号找到对应的page。
1.pte页表项里面有字段保存的就是pfn页帧号。
2.pfn就可以转化成page。
#define pte_page(pte) pfn_to_page(pte_pfn(pte))
#define pte_pfn(pte) ((pte_val(pte) & PHYS_MASK) >> PAGE_SHIFT)
参考文档: