• linux进程地址空间--vma的基本操作【转】


           在32位的系统上,线性地址空间可达到4GB,这4GB一般按照3:1的比例进行分配,也就是说用户进程享有前3GB线性地址空间,而内核独享最后1GB线性地址空间。由于虚拟内存的引入,每个进程都可拥有3GB的虚拟内存,并且用户进程之间的地址空间是互不可见、互不影响的,也就是说即使两个进程对同一个地址进行操作,也不会产生问题。在前面介绍的一些分配内存的途径中,无论是伙伴系统中分配页的函数,还是slab分配器中分配对象的函数,它们都会尽量快速地响应内核的分配请求,将相应的内存提交给内核使用,而内核对待用户空间显然不能如此。用户空间动态申请内存时往往只是获得一块线性地址的使用权,而并没有将这块线性地址区域与实际的物理内存对应上,只有当用户空间真正操作申请的内存时,才会触发一次缺页异常,这时内核才会分配实际的物理内存给用户空间。

           用户进程的虚拟地址空间包含了若干区域,这些区域的分布方式是特定于体系结构的,不过所有的方式都包含下列成分:

    • 可执行文件的二进制代码,也就是程序的代码段
    • 存储全局变量的数据段
    • 用于保存局部变量和实现函数调用的栈
    • 环境变量和命令行参数
    • 程序使用的动态库的代码
    • 用于映射文件内容的区域

    由此可以看到进程的虚拟内存空间会被分成不同的若干区域,每个区域都有其相关的属性和用途,一个合法的地址总是落在某个区域当中的,这些区域也不会重叠。在linux内核中,这样的区域被称之为虚拟内存区域(virtual memory areas),简称vma。一个vma就是一块连续的线性地址空间的抽象,它拥有自身的权限(可读,可写,可执行等等) ,每一个虚拟内存区域都由一个相关的struct vm_area_struct结构来描述

    [cpp] view plain copy
     
    1. <span style="font-size:12px;">struct vm_area_struct {  
    2.     struct mm_struct * vm_mm;   /* 所属的内存描述符 */  
    3.     unsigned long vm_start;    /* vma的起始地址 */  
    4.     unsigned long vm_end;       /* vma的结束地址 */  
    5.   
    6.     /* 该vma的在一个进程的vma链表中的前驱vma和后驱vma指针,链表中的vma都是按地址来排序的*/  
    7.     struct vm_area_struct *vm_next, *vm_prev;  
    8.   
    9.     pgprot_t vm_page_prot;      /* vma的访问权限 */  
    10.     unsigned long vm_flags;    /* 标识集 */  
    11.   
    12.     struct rb_node vm_rb;      /* 红黑树中对应的节点 */  
    13.   
    14.     /* 
    15.      * For areas with an address space and backing store, 
    16.      * linkage into the address_space->i_mmap prio tree, or 
    17.      * linkage to the list of like vmas hanging off its node, or 
    18.      * linkage of vma in the address_space->i_mmap_nonlinear list. 
    19.      */  
    20.     /* shared联合体用于和address space关联 */  
    21.     union {  
    22.         struct {  
    23.             struct list_head list;/* 用于链入非线性映射的链表 */  
    24.             void *parent;   /* aligns with prio_tree_node parent */  
    25.             struct vm_area_struct *head;  
    26.         } vm_set;  
    27.   
    28.         struct raw_prio_tree_node prio_tree_node;/*线性映射则链入i_mmap优先树*/  
    29.     } shared;  
    30.   
    31.     /* 
    32.      * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma 
    33.      * list, after a COW of one of the file pages.  A MAP_SHARED vma 
    34.      * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack 
    35.      * or brk vma (with NULL file) can only be in an anon_vma list. 
    36.      */  
    37.     /*anno_vma_node和annon_vma用于管理源自匿名映射的共享页*/  
    38.     struct list_head anon_vma_node; /* Serialized by anon_vma->lock */  
    39.     struct anon_vma *anon_vma;  /* Serialized by page_table_lock */  
    40.   
    41.     /* Function pointers to deal with this struct. */  
    42.     /*该vma上的各种标准操作函数指针集*/  
    43.     const struct vm_operations_struct *vm_ops;  
    44.   
    45.     /* Information about our backing store: */  
    46.     unsigned long vm_pgoff;     /* 映射文件的偏移量,以PAGE_SIZE为单位 */  
    47.     struct file * vm_file;          /* 映射的文件,没有则为NULL */  
    48.     void * vm_private_data;     /* was vm_pte (shared mem) */  
    49.     unsigned long vm_truncate_count;/* truncate_count or restart_addr */  
    50.   
    51. #ifndef CONFIG_MMU  
    52.     struct vm_region *vm_region;    /* NOMMU mapping region */  
    53. #endif  
    54. #ifdef CONFIG_NUMA  
    55.     struct mempolicy *vm_policy;    /* NUMA policy for the VMA */  
    56. #endif  
    57. };  
    58. </span>  


     

    进程的若干个vma区域都得按一定的形式组织在一起,这些vma都包含在进程的内存描述符中,也就是struct mm_struct中,这些vma在mm_struct以两种方式进行组织,一种是链表方式,对应于mm_struct中的mmap链表头,一种是红黑树方式,对应于mm_struct中的mm_rb根节点,和内核其他地方一样,链表用于遍历,红黑树用于查找。

    下面以文件映射为例,来阐述文件的address_space和与其建立映射关系的vma是如何联系上的。首先来看看struct address_space中与vma相关的变量

    [cpp] view plain copy
     
    1. struct address_space {  
    2.     struct inode        *host;      /* owner: inode, block_device */  
    3.     ...  
    4.     struct prio_tree_root   i_mmap;     /* tree of private and shared mappings */  
    5.     struct list_head    i_mmap_nonlinear;          /*list VM_NONLINEAR mappings */  
    6.     ...  
    7. } __attr  


    与此同时,struct file和struct inode中都包含有一个struct address_space的指针,分别为f_mapping和i_mapping。struct file是一个特定于进程的数据结构,而struct inode则是一个特定于文件的数据结构。每当进程打开一个文件时,都会将file->f_mapping设置到inode->i_mapping,下图则给出了文件和与其建立映射关系的vma的联系

    下面来看几个vma的基本操作函数,这些函数都是后面实现具体功能的基础

    find_vma()用来寻找一个针对于指定地址的vma,该vma要么包含了指定的地址,要么位于该地址之后并且离该地址最近,或者说寻找第一个满足addr<vma_end的vma

    [cpp] view plain copy
     
    1. struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)  
    2. {  
    3.     struct vm_area_struct *vma = NULL;  
    4.   
    5.     if (mm) {  
    6.         /* Check the cache first. */  
    7.         /* (Cache hit rate is typically around 35%.) */  
    8.         vma = mm->mmap_cache; //首先尝试mmap_cache中缓存的vma  
    9.         /*如果不满足下列条件中的任意一个则从红黑树中查找合适的vma 
    10.           1.缓存vma不存在 
    11.           2.缓存vma的结束地址小于给定的地址 
    12.           3.缓存vma的起始地址大于给定的地址*/  
    13.         if (!(vma && vma->vm_end > addr && vma->vm_start <= addr)) {  
    14.             struct rb_node * rb_node;  
    15.   
    16.             rb_node = mm->mm_rb.rb_node;//获取红黑树根节点  
    17.             vma = NULL;  
    18.   
    19.             while (rb_node) {  
    20.                 struct vm_area_struct * vma_tmp;  
    21.   
    22.                 vma_tmp = rb_entry(rb_node,   //获取节点对应的vma  
    23.                         struct vm_area_struct, vm_rb);  
    24.   
    25.                 /*首先确定vma的结束地址是否大于给定地址,如果是的话,再确定 
    26.                   vma的起始地址是否小于给定地址,也就是优先保证给定的地址是 
    27.                   处于vma的范围之内的,如果无法保证这点,则只能找到一个距离 
    28.                   给定地址最近的vma并且该vma的结束地址要大于给定地址*/  
    29.                 if (vma_tmp->vm_end > addr) {  
    30.                     vma = vma_tmp;  
    31.                     if (vma_tmp->vm_start <= addr)  
    32.                         break;  
    33.                     rb_node = rb_node->rb_left;  
    34.                 } else  
    35.                     rb_node = rb_node->rb_right;  
    36.             }  
    37.             if (vma)  
    38.                 mm->mmap_cache = vma;//将结果保存在缓存中  
    39.         }  
    40.     }  
    41.     return vma;  
    42. }  


     

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

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

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

    addr:新区域的起始地址

    end:新区域的结束地址

    vm_flags:新区域的标识集

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

    file:新区域映射的文件

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

    policy:和NUMA相关

    [cpp] view plain copy
     
    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会执行具体的合并调整操作

    [cpp] view plain copy
     
    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()函数用于插入一块新区域

    [cpp] view plain copy
     
    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. }  


     

    [cpp] view plain copy
     
    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. }  


    在创建新的vma区域之前先要寻找一块足够大小的空闲区域,该项工作由get_unmapped_area()函数完成,而实际的工作将会由mm_struct中定义的辅助函数来完成。根据进程虚拟地址空间的布局,会选择使用不同的映射函数,在这里考虑大多数系统上采用的标准函数arch_get_unmapped_area();

    [cpp] view plain copy
     
    1. unsigned long  
    2. arch_get_unmapped_area(struct file *filp, unsigned long addr,  
    3.         unsigned long len, unsigned long pgoff, unsigned long flags)  
    4. {  
    5.     struct mm_struct *mm = current->mm;  
    6.     struct vm_area_struct *vma;  
    7.     unsigned long start_addr;  
    8.   
    9.     if (len > TASK_SIZE)  
    10.         return -ENOMEM;  
    11.   
    12.     if (flags & MAP_FIXED)  
    13.         return addr;  
    14.   
    15.     if (addr) {  
    16.         addr = PAGE_ALIGN(addr);//将地址按页对齐  
    17.         vma = find_vma(mm, addr);//获取一个vma,该vma可能包含了addr也可能在addr后面并且离addr最近  
    18.         /*这里确定是否有一块适合的空闲区域,先要保证addr+len不会 
    19.           超过进程地址空间的最大允许范围,然后如果前面vma获取成功的话则要保证 
    20.           vma位于addr的后面并且addr+len不会延伸到该vma的区域*/  
    21.         if (TASK_SIZE - len >= addr &&  
    22.             (!vma || addr + len <= vma->vm_start))  
    23.             return addr;  
    24.     }  
    25.     /*前面获取不成功的话则要调整起始地址了,根据情况选择缓存的空闲区域地址 
    26.       或者TASK_UNMAPPED_BASE=TASK_SIZE/3*/  
    27.     if (len > mm->cached_hole_size) {  
    28.             start_addr = addr = mm->free_area_cache;  
    29.     } else {  
    30.             start_addr = addr = TASK_UNMAPPED_BASE;  
    31.             mm->cached_hole_size = 0;  
    32.     }  
    33.   
    34. full_search:  
    35.     /*从addr开始遍历用户地址空间*/  
    36.     for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {  
    37.         /* At this point:  (!vma || addr < vma->vm_end). */  
    38.         if (TASK_SIZE - len < addr) {//这里判断是否已经遍历到了用户地址空间的末端  
    39.             /* 
    40.              * Start a new search - just in case we missed 
    41.              * some holes. 
    42.              */  
    43.              //如果上次不是从TAKS_UNMAPPED_BASE开始遍历的,则尝试从TASK_UNMAPPED_BASE开始遍历  
    44.             if (start_addr != TASK_UNMAPPED_BASE) {  
    45.                 addr = TASK_UNMAPPED_BASE;  
    46.                     start_addr = addr;  
    47.                 mm->cached_hole_size = 0;  
    48.                 goto full_search;  
    49.             }  
    50.             return -ENOMEM;  
    51.         }  
    52.         if (!vma || addr + len <= vma->vm_start) {//判断是否有空闲区域  
    53.             /* 
    54.              *找到空闲区域的话则记住我们搜索的结束处,以便下次搜索 
    55.              */  
    56.             mm->free_area_cache = addr + len;  
    57.             return addr;  
    58.         }  
    59.         /*该空闲区域不符合大小要求,但是如果这个空闲区域大于之前保存的最大值的话 
    60.           则将这个空闲区域保存,这样便于前面确定从哪里开始搜索*/  
    61.         if (addr + mm->cached_hole_size < vma->vm_start)  
    62.                 mm->cached_hole_size = vma->vm_start - addr;  
    63.         addr = vma->vm_end;  
    64.     }  
    65. }  
  • 相关阅读:
    DBImport v3.44 中文版发布:数据库数据互导及文档生成工具(IT人员必备)
    IT人生知识分享:博弈论的理性思维
    IT人生知识分享:概率与运气
    开源:秋式广告杀手源码
    浅说秋色园域名被国家互联网应急中心封与解的过程
    自定义可视化调试工具(Microsoft.VisualStudio.DebuggerVisualizers)
    Excel导入导出的业务进化场景及组件化的设计方案(上)
    回忆录:30岁那年,你成长了吗?(上篇)
    Excel导入导出组件的设计
    DBImport v3.3 中文版发布:数据库数据互导及文档生成工具(IT人员必备)
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/5663379.html
Copyright © 2020-2023  润新知