转载:https://blog.csdn.net/tiantao2012/article/details/79060784
struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
用于根据属于某个进程的虚拟地址,找到其所属的进程虚拟区间,并返回相应的vm_area_struct 指针
其使用的例程如下:
static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift)
{
struct mm_struct *mm = vma->vm_mm;
unsigned long old_start = vma->vm_start;
unsigned long old_end = vma->vm_end;
unsigned long length = old_end - old_start;
unsigned long new_start = old_start - shift;
unsigned long new_end = old_end - shift;
struct mmu_gather tlb;
BUG_ON(new_start > new_end);
/*
* ensure there are no vmas between where we want to go
* and where we are
*/
if (vma != find_vma(mm, new_start))
return -EFAULT;
}
其源码分析如下:
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. */
#首先查找addr释放在vma的cache中是否有缓存
vma = vmacache_find(mm, addr);
#这里用likely修饰,看来有很大的几率可以找到
if (likely(vma))
return vma;
#进程的虚拟空间是按红黑树组织的。如果在cache中没有找到的话,这里会遍历红黑树查找
rb_node = mm->mm_rb.rb_node;
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;
#查找到的标准是 tmp->vm_start < addr < tmp->vm_end.
if (tmp->vm_start <= addr)
break;
rb_node = rb_node->rb_left;
} else
rb_node = rb_node->rb_right;
}
#如果找到vma的话,则将这个vma更新到缓存中
if (vma)
vmacache_update(addr, vma);
return vma;
}
find_vma_intersection
static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr)
这个函数用于查找在start_addr~end_addr ,之间的第一个vma
其使用的例程如下:
int get_vaddr_frames(unsigned long start, unsigned int nr_frames,
unsigned int gup_flags, struct frame_vector *vec)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
int ret = 0;
int err;
int locked;
if (nr_frames == 0)
return 0;
if (WARN_ON_ONCE(nr_frames > vec->nr_allocated))
nr_frames = vec->nr_allocated;
down_read(&mm->mmap_sem);
locked = 1;
vma = find_vma_intersection(mm, start, start + 1);
if (!vma) {
ret = -EFAULT;
goto out;
}
}
其源码分析如下:
static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr)
{
#调用find_vma找到包含start_addr 的vma
struct vm_area_struct * vma = find_vma(mm,start_addr);
#如果找到vma,但是vma 不在start_addr和end_addr 之前的话,就等于没有找到
#start_addr~end_addr之间的vma。就返回null
if (vma && end_addr <= vma->vm_start)
vma = NULL;
#找到start_addr~end_addr之间的第一个vma
return vma;
}