Chinaunix首页 | 论坛 | 博客
  • 博客访问: 325026
  • 博文数量: 100
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 665
  • 用 户 组: 普通用户
  • 注册时间: 2015-02-02 12:43
文章分类

全部博文(100)

文章存档

2015年(100)

我的朋友

分类: LINUX

2015-06-19 14:43:04

当一个新区域被加到进程的地址空间时,内核会检查它是否可以与一个或多个现存区域合并,vma_merge()函数在可能的情况下,将一个新区域与周边区域进行合并。参数:

mm:新区域所属的进程地址空间

prev:在地址上紧接着新区域的前面一个vma

addr:新区域的起始地址

end:新区域的结束地址

vm_flags:新区域的标识集

anon_vma:新区域所属的匿名映射

file:新区域映射的文件

pgoff:新区域映射文件的偏移

policy:和NUMA相关

  1. struct vm_area_struct *vma_merge(struct mm_struct *mm,  
  2.             struct vm_area_struct *prev, unsigned long addr,  
  3.             unsigned long end, unsigned long vm_flags,  
  4.                 struct anon_vma *anon_vma, struct file *file,  
  5.             pgoff_t pgoff, struct mempolicy *policy)  
  6. {  
  7.     pgoff_t pglen = (end - addr) >> PAGE_SHIFT;  
  8.     struct vm_area_struct *area, *next;  
  9.   
  10.     /* 
  11.      * We later require that vma->vm_flags == vm_flags, 
  12.      * so this tests vma->vm_flags & VM_SPECIAL, too. 
  13.      */  
  14.     if (vm_flags & VM_SPECIAL)  
  15.         return NULL;  
  16.   
  17.     if (prev)//指定了先驱vma,则获取先驱vma的后驱vma   
  18.         next = prev->vm_next;  
  19.     else     //否则指定mm的vma链表中的第一个元素为后驱vma   
  20.         next = mm->mmap;  
  21.     area = next;  
  22.   
  23.     /*后驱节点存在,并且后驱vma的结束地址和给定区域的结束地址相同, 
  24.       也就是说两者有重叠,那么调整后驱vma*/  
  25.     if (next && next->vm_end == end)     /* cases 6, 7, 8 */  
  26.         next = next->vm_next;  
  27.   
  28.     /* 
  29.      * 先判断给定的区域能否和前驱vma进行合并,需要判断如下的几个方面: 
  30.        1.前驱vma必须存在 
  31.        2.前驱vma的结束地址正好等于给定区域的起始地址 
  32.        3.两者的struct mempolicy中的相关属性要相同,这项检查只对NUMA架构有意义 
  33.        4.其他相关项必须匹配,包括两者的vm_flags,是否映射同一个文件等等 
  34.      */  
  35.     if (prev && prev->vm_end == addr &&  
  36.             mpol_equal(vma_policy(prev), policy) &&  
  37.             can_vma_merge_after(prev, vm_flags,  
  38.                         anon_vma, file, pgoff)) {  
  39.         /* 
  40.          *确定可以和前驱vma合并后再判断是否能和后驱vma合并,判断方式和前面一样, 
  41.           不过这里多了一项检查,在给定区域能和前驱、后驱vma合并的情况下还要检查 
  42.           前驱、后驱vma的匿名映射可以合并 
  43.          */  
  44.         if (next && end == next->vm_start &&  
  45.                 mpol_equal(policy, vma_policy(next)) &&  
  46.                 can_vma_merge_before(next, vm_flags,  
  47.                     anon_vma, file, pgoff+pglen) &&  
  48.                 is_mergeable_anon_vma(prev->anon_vma,  
  49.                               next->anon_vma)) {  
  50.                             /* cases 1, 6 */  
  51.             vma_adjust(prev, prev->vm_start,  
  52.                 next->vm_end, prev->vm_pgoff, NULL);  
  53.         } else                  /* cases 2, 5, 7 */  
  54.             vma_adjust(prev, prev->vm_start,  
  55.                 end, prev->vm_pgoff, NULL);  
  56.         return prev;  
  57.     }  
  58.   
  59.     /* 
  60.      * Can this new request be merged in front of next? 
  61.      */  
  62.      /*如果前面的步骤失败,那么则从后驱vma开始进行和上面类似的步骤*/  
  63.     if (next && end == next->vm_start &&  
  64.             mpol_equal(policy, vma_policy(next)) &&  
  65.             can_vma_merge_before(next, vm_flags,  
  66.                     anon_vma, file, pgoff+pglen)) {  
  67.         if (prev && addr < prev->vm_end)  /* case 4 */  
  68.             vma_adjust(prev, prev->vm_start,  
  69.                 addr, prev->vm_pgoff, NULL);  
  70.         else                    /* cases 3, 8 */  
  71.             vma_adjust(area, addr, next->vm_end,  
  72.                 next->vm_pgoff - pglen, NULL);  
  73.         return area;  
  74.     }  
  75.   
  76.     return NULL;  
  77. }  

vma_adjust会执行具体的合并调整操作

  1. void vma_adjust(struct vm_area_struct *vma, unsigned long start,  
  2.     unsigned long end, pgoff_t pgoff, struct vm_area_struct *insert)  
  3. {  
  4.     struct mm_struct *mm = vma->vm_mm;  
  5.     struct vm_area_struct *next = vma->vm_next;  
  6.     struct vm_area_struct *importer = NULL;  
  7.     struct address_space *mapping = NULL;  
  8.     struct prio_tree_root *root = NULL;  
  9.     struct file *file = vma->vm_file;  
  10.     struct anon_vma *anon_vma = NULL;  
  11.     long adjust_next = 0;  
  12.     int remove_next = 0;  
  13.   
  14.     if (next && !insert) {  
  15.         /*指定的范围已经跨越了整个后驱vma,并且有可能超过后驱vma*/  
  16.         if (end >= next->vm_end) {  
  17.             /* 
  18.              * vma expands, overlapping all the next, and 
  19.              * perhaps the one after too (mprotect case 6). 
  20.              */  
  21. again:          remove_next = 1 + (end > next->vm_end);//确定是否超过了后驱vma   
  22.             end = next->vm_end;  
  23.             anon_vma = next->anon_vma;  
  24.             importer = vma;  
  25.         } else if (end > next->vm_start) {/*指定的区域和后驱vma部分重合*/  
  26.           
  27.             /* 
  28.              * vma expands, overlapping part of the next: 
  29.              * mprotect case 5 shifting the boundary up. 
  30.              */  
  31.             adjust_next = (end - next->vm_start) >> PAGE_SHIFT;  
  32.             anon_vma = next->anon_vma;  
  33.             importer = vma;  
  34.         } else if (end < vma->vm_end) {/*指定的区域没到达后驱vma的结束处*/  
  35.             /* 
  36.              * vma shrinks, and !insert tells it's not 
  37.              * split_vma inserting another: so it must be 
  38.              * mprotect case 4 shifting the boundary down. 
  39.              */  
  40.             adjust_next = - ((vma->vm_end - end) >> PAGE_SHIFT);  
  41.             anon_vma = next->anon_vma;  
  42.             importer = next;  
  43.         }  
  44.     }  
  45.   
  46.     if (file) {//如果有映射文件   
  47.         mapping = file->f_mapping;//获取文件对应的address_space   
  48.         if (!(vma->vm_flags & VM_NONLINEAR))  
  49.             root = &mapping->i_mmap;  
  50.         spin_lock(&mapping->i_mmap_lock);  
  51.         if (importer &&  
  52.             vma->vm_truncate_count != next->vm_truncate_count) {  
  53.             /* 
  54.              * unmap_mapping_range might be in progress: 
  55.              * ensure that the expanding vma is rescanned. 
  56.              */  
  57.             importer->vm_truncate_count = 0;  
  58.         }  
  59.         /*如果指定了待插入的vma,则根据vma是否以非线性的方式映射文件来选择是将 
  60.         vma插入file对应的address_space的优先树(对应线性映射)还是双向链表(非线性映射)*/  
  61.         if (insert) {  
  62.             insert->vm_truncate_count = vma->vm_truncate_count;  
  63.             /* 
  64.              * Put into prio_tree now, so instantiated pages 
  65.              * are visible to arm/parisc __flush_dcache_page 
  66.              * throughout; but we cannot insert into address 
  67.              * space until vma start or end is updated. 
  68.              */  
  69.             __vma_link_file(insert);  
  70.         }  
  71.     }  
  72.   
  73.     /* 
  74.      * When changing only vma->vm_end, we don't really need 
  75.      * anon_vma lock. 
  76.      */  
  77.     if (vma->anon_vma && (insert || importer || start != vma->vm_start))  
  78.         anon_vma = vma->anon_vma;  
  79.     if (anon_vma) {  
  80.         spin_lock(&anon_vma->lock);  
  81.         /* 
  82.          * Easily overlooked: when mprotect shifts the boundary, 
  83.          * make sure the expanding vma has anon_vma set if the 
  84.          * shrinking vma had, to cover any anon pages imported. 
  85.          */  
  86.         if (importer && !importer->anon_vma) {  
  87.             importer->anon_vma = anon_vma;  
  88.             __anon_vma_link(importer);//将importer插入importer的anon_vma匿名映射链表中   
  89.         }  
  90.     }  
  91.   
  92.     if (root) {  
  93.         flush_dcache_mmap_lock(mapping);  
  94.         vma_prio_tree_remove(vma, root);  
  95.         if (adjust_next)  
  96.             vma_prio_tree_remove(next, root);  
  97.     }  
  98.   
  99.     /*调整vma的相关量*/  
  100.     vma->vm_start = start;  
  101.     vma->vm_end = end;  
  102.     vma->vm_pgoff = pgoff;  
  103.     if (adjust_next) {//调整后驱vma的相关量   
  104.         next->vm_start += adjust_next << PAGE_SHIFT;  
  105.         next->vm_pgoff += adjust_next;  
  106.     }  
  107.   
  108.     if (root) {  
  109.         if (adjust_next)//如果后驱vma被调整了,则重新插入到优先树中   
  110.             vma_prio_tree_insert(next, root);  
  111.         vma_prio_tree_insert(vma, root);//将vma插入到优先树中   
  112.         flush_dcache_mmap_unlock(mapping);  
  113.     }  
  114.   
  115.     if (remove_next) {//给定区域与后驱vma有重合   
  116.         /* 
  117.          * vma_merge has merged next into vma, and needs 
  118.          * us to remove next before dropping the locks. 
  119.          */  
  120.         __vma_unlink(mm, next, vma);//将后驱vma从红黑树中删除   
  121.         if (file)//将后驱vma从文件对应的address space中删除   
  122.             __remove_shared_vm_struct(next, file, mapping);  
  123.         if (next->anon_vma)//将后驱vma从匿名映射链表中删除   
  124.             __anon_vma_merge(vma, next);  
  125.     } else if (insert) {  
  126.         /* 
  127.          * split_vma has split insert from vma, and needs 
  128.          * us to insert it before dropping the locks 
  129.          * (it may either follow vma or precede it). 
  130.          */  
  131.         __insert_vm_struct(mm, insert);//将待插入的vma插入mm的红黑树,双向链表以及   
  132.                                        //匿名映射链表   
  133.     }  
  134.   
  135.     if (anon_vma)  
  136.         spin_unlock(&anon_vma->lock);  
  137.     if (mapping)  
  138.         spin_unlock(&mapping->i_mmap_lock);  
  139.   
  140.     if (remove_next) {  
  141.         if (file) {  
  142.             fput(file);  
  143.             if (next->vm_flags & VM_EXECUTABLE)  
  144.                 removed_exe_file_vma(mm);  
  145.         }  
  146.         mm->map_count--;  
  147.         mpol_put(vma_policy(next));  
  148.         kmem_cache_free(vm_area_cachep, next);  
  149.         /* 
  150.          * In mprotect's case 6 (see comments on vma_merge), 
  151.          * we must remove another next too. It would clutter 
  152.          * up the code too much to do both in one go. 
  153.          */  
  154.         if (remove_next == 2) {//还有待删除的区域   
  155.             next = vma->vm_next;  
  156.             goto again;  
  157.         }  
  158.     }  
  159.   
  160.     validate_mm(mm);  
  161. }  

insert_vm_struct()函数用于插入一块新区域

  1. int insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma)  
  2. {  
  3.     struct vm_area_struct * __vma, * prev;  
  4.     struct rb_node ** rb_link, * rb_parent;  
  5.   
  6.     /* 
  7.      * The vm_pgoff of a purely anonymous vma should be irrelevant 
  8.      * until its first write fault, when page's anon_vma and index 
  9.      * are set.  But now set the vm_pgoff it will almost certainly 
  10.      * end up with (unless mremap moves it elsewhere before that 
  11.      * first wfault), so /proc/pid/maps tells a consistent story. 
  12.      * 
  13.      * By setting it to reflect the virtual start address of the 
  14.      * vma, merges and splits can happen in a seamless way, just 
  15.      * using the existing file pgoff checks and manipulations. 
  16.      * Similarly in do_mmap_pgoff and in do_brk. 
  17.      */  
  18.     if (!vma->vm_file) {  
  19.         BUG_ON(vma->anon_vma);  
  20.         vma->vm_pgoff = vma->vm_start >> PAGE_SHIFT;  
  21.     }  
  22.     /*__vma用来保存和vma->start对应的vma(与find_vma()一样),同时获取以下信息: 
  23.       1.prev用来保存对应的前驱vma 
  24.       2.rb_link保存该vma区域插入对应的红黑树节点 
  25.       3.rb_parent保存该vma区域对应的父节点*/  
  26.     __vma = find_vma_prepare(mm,vma->vm_start,&prev,&rb_link,&rb_parent);  
  27.     if (__vma && __vma->vm_start < vma->vm_end)  
  28.         return -ENOMEM;  
  29.     if ((vma->vm_flags & VM_ACCOUNT) &&  
  30.          security_vm_enough_memory_mm(mm, vma_pages(vma)))  
  31.         return -ENOMEM;  
  32.     vma_link(mm, vma, prev, rb_link, rb_parent);//将vma关联到所有的数据结构中   
  33.     return 0;  
  34. }  
  1. static void vma_link(struct mm_struct *mm, struct vm_area_struct *vma,  
  2.             struct vm_area_struct *prev, struct rb_node **rb_link,  
  3.             struct rb_node *rb_parent)  
  4. {  
  5.     struct address_space *mapping = NULL;  
  6.   
  7.     if (vma->vm_file)//如果存在文件映射则获取文件对应的地址空间   
  8.         mapping = vma->vm_file->f_mapping;  
  9.   
  10.     if (mapping) {  
  11.         spin_lock(&mapping->i_mmap_lock);  
  12.         vma->vm_truncate_count = mapping->truncate_count;  
  13.     }  
  14.     anon_vma_lock(vma);  
  15.   
  16.     /*将vma插入到相应的数据结构中--双向链表,红黑树和匿名映射链表*/  
  17.     __vma_link(mm, vma, prev, rb_link, rb_parent);  
  18.     __vma_link_file(vma);//将vma插入到文件地址空间的相应数据结构中   
  19.   
  20.     anon_vma_unlock(vma);  
  21.     if (mapping)  
  22.         spin_unlock(&mapping->i_mmap_lock);  
  23.   
  24.     mm->map_count++;  
  25.     validate_mm(mm);  
  26. }  
阅读(4466) | 评论(0) | 转发(0) |
0

上一篇:linux进程地址空间

下一篇:非连续内存区

给主人留下些什么吧!~~