内核时常需要对进程的某个虚存区进行操作,那就需要找到该虚存区,内核里面
就提供了一个内核API,可以由一个虚拟地址,找到该地址所在的虚存区。
-------------------------------------------------------------------------------
1,该内核API解析。
- 1590 /* Look up the first VMA which satisfies addr < vm_end, NULL if none. */
- 1591 struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
- 1592 {
- 1593 struct vm_area_struct *vma = NULL;
- 1594
- 1595 if (mm) {
- 1596 /* Check the cache first. */
- 1597 /* (Cache hit rate is typically around 35%.) */
- 1598 vma = mm->mmap_cache;//先把缓存中的一个vma拿出来,看是否符合要求。
- //如果符合要求则把缓存中的地址返回后退出,不符合要求则到红黑树中去找
- 1599 if (!(vma && vma->vm_end > addr && vma->vm_start <= addr)) {
- 1600 struct rb_node * rb_node;
- 1601
- 1602 rb_node = mm->mm_rb.rb_node;//获取红黑树的根。
- 1603 vma = NULL;
- 1604
- 1605 while (rb_node) {
- 1606 struct vm_area_struct * vma_tmp;
- 1607
- 1608 vma_tmp = rb_entry(rb_node,//由红黑树节点返回到该节点的 struct vm_area_struct *
- 1609 struct vm_area_struct, vm_rb);
- 1610
- 1611 if (vma_tmp->vm_end > addr) { //如果addr小于vm_end
- 1612 vma = vma_tmp;
- 1613 if (vma_tmp->vm_start <= addr)
- 1614 break;
- 1615 rb_node = rb_node->rb_left;//朝左走
- 1616 } else
- 1617 rb_node = rb_node->rb_right;//朝右走
- 1618 }
- 1619 if (vma)
- 1620 mm->mmap_cache = vma;//将找到的vma写入到缓存中
- 1621 }
- 1622 }
- 1623 return vma;//返回vma指针
- 1624 }
该函数还是很好理解的,mm_struct中有一个字段mmap_cache,用来缓存上一次使用的vma
的指针,在这次查找vma时,先在该缓存中取出这个vma,看这个vma是不是要查找的vma,
如果是就将这个vma的指针返回,不是然后再到红黑树中去找。
--------------------------------------------------------------------------------------
2,该函数的返回。
该函数的返回和一般想象的不太一样,分如上三种情况。
case 1,如果addr 位于某个虚存区上,则返回该虚存区的指针。
case 2,如果addr 没有位于那个虚存区,而是在空洞上,则返回该空洞的上一个虚存区的指针。
例如图上的case 2,查询addr返回的就是vma2
case 3,如果addr 位于空洞上,而且没有上一个虚存区,则返回NULL。
----------------------------------------------------------------------------------------------
4,实战。(代码验证理论)
- static int __init find_vma_init(void)
- {
- struct mm_struct *mm;
- struct vm_area_struct *vma;
- unsigned long addr;
- unsigned long addr2 = (unsigned long)0xffff0000;
- mm = current->mm;
- addr = mm->mmap->vm_start + 1;
- printk("<0>addr = 0x%lx\n",addr);
- vma = find_vma(mm,addr);
- if (vma != NULL) {
- printk("<0>vma->vm_start = 0x%lx\n",vma->vm_start);
- printk("<0>vma->vm_end = 0x%lx\n",vma->vm_end);
- printk("<0>page_number = %lu\n",vma_pages(vma));
- } else {
- printk("<0>don't find the vma!\n");
- }
- printk("<0>-----------------------------------\n");
- addr = mm->mmap->vm_start - 1;
- printk("<0>addr = 0x%lx\n",addr);
- vma = find_vma(mm,addr);
- if (vma != NULL) {
- printk("<0>vma->vm_start = 0x%lx\n",vma->vm_start);
- printk("<0>vma->vm_end = 0x%lx\n",vma->vm_end);
- printk("<0>page_number = %lu\n",vma_pages(vma));
- } else {
- printk("<0>don't find the vma!\n");
- }
- printk("<0>-----------------------------------\n");
- addr = addr2;
- printk("<0>addr = 0x%lx\n",addr);
- vma = find_vma(mm,addr);
- if (vma != NULL) {
- printk("<0>vma->vm_start = 0x%lx\n",vma->vm_start);
- printk("<0>vma->vm_end = 0x%lx\n",vma->vm_end);
- printk("<0>page_number = %lu\n",vma_pages(vma));
- } else {
- printk("<0>don't find the vma!\n");
- }
- printk("<0>-----------------------------------\n");
- return 0;
- }
内核API:vma_pages()用来求得找到的虚存区有多少个虚拟页,实现也很简单。
虚存区的尾地址-首地址,然后除以4KB,即虚存区的页的个数。
实现代码:
- 1367 static inline unsigned long vma_pages(struct vm_area_struct *vma)
- 1368 {
- 1369 return (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
- 1370 }
---------------------------------------------------------------------------
4,模块完整代码。
find_vma.rar ----------------------------------------------------------------------------
参考:http://edsionte.com/techblog/archives/3403
阅读(1371) | 评论(0) | 转发(0) |