通过虚拟地址addr来查找VMA是内核中常用的操作,内核提供一个API函数来实现这个查找操作。find_vma()函数根据给定地址addr查找满足如下条件之一的VMA,如下图:
-
addr在VMA空间范围内,即vma->vm_start <= addr < vma->vm_end。[vma->vm_start, vma->vm_end)
-
距离addr最近并且VMA的结束地址大于addr的一个VMA。
find_vma函数的实现如下:
mm/mmap.c
/* Look up the first VMA which satisfies addr < vm_end, NULL if none. */
struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
{
struct rb_node *rb_node;
struct vm_area_struct *vma;
/* Check the cache first. */
/*首先查找vma cache中的VMA是否满足要求。
vmacache_find()是内核中最近出现的一个查找VMA的优化方法。
在task_struct结构中,有一个存放最近访问过的VMA的数组
vmacache[VMACACHE_SIZE],其中可以存放4个最近使用的VMA,
充分利用了局部性原理。如果在vmacache中没有找到VMA,那么
遍历这个用户进程的mm_rb红黑树,这个红黑树存放该用户进程所有
的VMA。
*/
vma = vmacache_find(mm, addr);
if (likely(vma))
return vma;
rb_node = mm->mm_rb.rb_node;
vma = NULL;
/*while循环要找一块满足上述要求的VMA*/
while (rb_node) {
struct vm_area_struct *tmp;
tmp = rb_entry(rb_node, struct vm_area_struct, vm_rb);
if (tmp->vm_end > addr) {
vma = tmp;
if (tmp->vm_start <= addr)
break;
rb_node = rb_node->rb_left;
} else
rb_node = rb_node->rb_right;
}
if (vma)
vmacache_update(addr, vma);
return vma;
}
find_vma_intersection()函数是另外一个API接口,用于查找start_addr、end_addr和现存的VMA有重叠的一个VMA,它基于find_vma()来实现
include/linux/mm.h
/* Look up the first VMA which intersects the interval start_addr..end_addr-1,
NULL if none. Assume start_addr < end_addr. */
static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr)
{
struct vm_area_struct * vma = find_vma(mm,start_addr);
if (vma && end_addr <= vma->vm_start)
vma = NULL;
return vma;
}
find_vma_prev()函数的逻辑和find_vma()一样,但是返回VMA的前继成员vma->vm_prev。
mm/mmap.c
/*
* Same as find_vma, but also return a pointer to the previous VMA in *pprev.
*/
struct vm_area_struct *
find_vma_prev(struct mm_struct *mm, unsigned long addr,
struct vm_area_struct **pprev)
{
struct vm_area_struct *vma;
vma = find_vma(mm, addr);
if (vma) {
*pprev = vma->vm_prev;
} else {
struct rb_node *rb_node = mm->mm_rb.rb_node;
*pprev = NULL;
while (rb_node) {
*pprev = rb_entry(rb_node, struct vm_area_struct, vm_rb);
rb_node = rb_node->rb_right;
}
}
return vma;
}