目录
本专栏文章将有70篇左右,欢迎+关注,查看后续文章。
4.8 反向映射
反向/逆向映射:
reverse mapping,即rmap。
反向映射:
作用:
通过一个页,找到所有映射该页的VMA。
使用场景:
页换出、页回收、页迁移。
RMAP中两个重要数据结构:
struct vm_area_struct
struct anon_vma
struct anon_vma_chain
struct vm_area_struct {
struct list_head anon_vma_chain;
struct anon_vma *anon_vma;
...
}
struct anon_vma{
//管理匿名类型VMAs
}
struct anon_vma_chain { //VMA和anon_vma之间的桥梁
struct vm_area_struct *vma;
struct anon_vma *anon_vma;
//指向父进程或子进程的anon_vma数据结构
struct list_head same_vma;
//链表节点,把struct anon_vma_chain添加到vma->anon_vma_chain链表中。
struct rb_node rb;
//红黑树节点,把struct anon_vma_chain添加到anon_vma->rb_root的红黑树中。
unsigned long rb_subtree_last;
};
回收内存页时,如何找到使用该页的所有VMA:
1. page结构中struct address_space *mapping字段指向anon_vma
总结:
父进程每个VMA都有一个anon_vma数据结构,即vma->anon_vma。
和某个VMA相关物理页面page->mapping都指向anon_vma。
anon_vma_chain->vma指向VMA,anon_vma_chain->anon_vma指向anon_vma。
anon_vma_chain添加到VMA->anon_vma_chain链表中。
anon_vma_chain-添加到anon_vma->rb_root红黑树中
统计页所有使用者计数
page如何找到对应vma:
1. 通过page的struct address_space mapping找到VMA
2. VMA遍历自己管理的红黑树rb_root,找到树上的每个节点anon_vma_chain
3. anon_vma_chain通过成员指针anon_vma找到对应的VMA
vm_area_struct和vm_struct区别联系:
vm_area_struct:
进程虚拟地址空间中一段连续地址范围,即VMA。并且拥有属性和状态,例如读/写/执行权限、是否共享等。通过它完成虚拟地址到物理地址映射和管理。
vm_struct:
描述一段未映射的、未分配的、虚拟地址空间中的内存区域,Virtual Memory Block,简称VMB。每个VMB属性,例如起始地址、大小、对齐要求等。在分配新的内存时,内核会在VMB中寻找一块合适大小的空间进行分配。
vmalloc函数从vm_struct中申请一块新的内存块,并将该内存块映射到新的VMA中,完成虚拟到物理地址的映射。
4.8.1 数据结构
struct page {
atomic_t _mapcount;
//记录该页面被映射到多少个VMA
struct address_space *mapping;
}
成员介绍:
mapping
1. 若为文件映射:
mapping指向struct address_space实例。
2. 若为匿名映射:
此时mapping的最低位为PAGE_MAPPING_ANON,mapping指向struct anon_vma。
struct vm_area_struct {
//下面联合体用于文件映射
union {
struct {
struct rb_node rb;
//用于插入到address_space->i_mmap树中
unsigned long rb_subtree_last;
} linear;
struct list_head nonlinear;
//用于连接到address_space->i_mmap_nonlinear链表中
} shared;
//用于匿名映射
struct list_head anon_vma_chain;
struct anon_vma *anon_vma; 链表访问
}
匿名私有映射的VMA:如堆栈。只能在anon_vma链表中。
文件私有映射的VMA:可同时在i_mmap和anon_vma链表中。
共享映射的VMA:只能在i_mmap树中。
拓展
一个address_space管理了一个文件(inode)在内存中所有缓存。
举例:
有5个进程,每个进程用mmap映射同一个文件的不同部分,将生成10个VMA,而这10个VMA都连接到同一个address_space->i_mmap树中或nonlinear链表中。
使用一个address_space可很好管理同一文件(inode)的多个VMA。
体现了address_space与文件(inode)对应关系。
同理:匿名映射一个struct anon_vma也管理一个文件的多个映射(多个VMA)。
逆向映射:
作用:通过一个页找到所有映射该页的VMA。
4.8.2 建立逆向映射
创建逆向映射时应区分:
匿名映射。
文件映射。
因为管理这两种映射的数据结构不同。
4.8.2.1 匿名页
把匿名页插入逆向映射数据结构:
void __page_set_anon_rmap(struct page *page, struct vm_area_struct *vma, unsigned long address)
{
struct anon_vma *anon_vma = vma->anon_vma;
anon_vma = (void *) anon_vma + PAGE_MAPPING_ANON;
//加上PAGE_MAPPING_ANON作用:区分是匿名页和文件页
anon_vma最低位:
若为0,则是文件页。
若为PAGE_MAPPING_ANON,则是匿名页。
page->mapping = (struct address_space) *anon_vma;
page->index = linear_page_index(vma, address);
//page->index:映射页内偏移
}
4.8.2.2 文件页
比匿名页更简单。
void page_add_file_rmap(struct page *page)
//对page->_mapcount+1
4.8.3 使用逆向映射
等第18章讲解页交换时,就知道逆向映射的好处。
int try_to_unmap(struct page *page, enum ttu_flags flags)
作用:
从所有使用该页的进程的页表中删除该页。(需要借助于逆向映射)
后续章节详解该函数。
page_referenced 函数和 struct page 中的 _mapcount 成员的联系:
page_referenced() 函数:
作用:返回活跃使用某共享页的进程数目。
_mapcount 作用:
统计映射该页的所有VMA数。即用于逆向映射统计。
逆向映射作用:
通过一个页找到所有进程中映射了该页的VMA。
int page_referenced(struct page *page, int is_locked,
struct mem_cgroup *memcg, unsigned long *vm_flags)
{
int referenced = 0;
if (PageAnon(page)) {
//活跃使用该匿名页的进程数
referenced += page_referenced_anon(page, memcg, vm_flags);
} else if (page->mapping) {
//活跃使用该文件页的进程数
referenced += page_referenced_file(page, memcg, vm_flags);
}
return referenced;
}