Linux内核深度解析
上QQ阅读APP看书,第一时间看更新

3.4.4 虚拟内存过量提交策略

虚拟内存过量提交,是指所有进程提交的虚拟内存的总和超过物理内存的容量,内存管理子系统支持3种虚拟内存过量提交策略。

(1)OVERCOMMIT_GUESS(0):猜测,估算可用内存的数量,因为没法准确计算可用内存的数量,所以说是猜测。

(2)OVERCOMMIT_ALWAYS(1):总是允许过量提交。

(3)OVERCOMMIT_NEVER(2):不允许过量提交。

默认策略是猜测,用户可以通过文件“/proc/sys/vm/overcommit_memory”修改策略。


在创建新的内存映射时,调用函数__vm_enough_memory根据虚拟内存过量提交策略判断内存是否足够,主要代码如下:

    mm/util.c
    1   int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin)
    2   {
    3    long free, allowed, reserve;
    4    …
    5    if (sysctl_overcommit_memory == OVERCOMMIT_ALWAYS)
    6         return 0;
    7
    8    if (sysctl_overcommit_memory == OVERCOMMIT_GUESS) {
    9         free = global_page_state(NR_FREE_PAGES);
    10        free += global_node_page_state(NR_FILE_PAGES);
    11
    12        free -= global_node_page_state(NR_SHMEM);
    13
    14        free += get_nr_swap_pages();
    15
    16        free += global_page_state(NR_SLAB_RECLAIMABLE);
    17
    18        if (free <= totalreserve_pages)
    19              goto error;
    20        else
    21              free -= totalreserve_pages;
    22
    23        if (! cap_sys_admin)
    24              free -= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10);
    25
    26        if (free > pages)
    27              return 0;
    28
    29        goto error;
    30   }
    31
    32   allowed = vm_commit_limit();
    33
    34   if (! cap_sys_admin)
    35        allowed -= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10);
    36
    37   if (mm) {
    38        reserve = sysctl_user_reserve_kbytes >> (PAGE_SHIFT - 10);
    39        allowed -= min_t(long, mm->total_vm / 32, reserve);
    40   }
    41
    42   if (percpu_counter_read_positive(&vm_committed_as) < allowed)
    43        return 0;
    44   error:
    45    vm_unacct_memory(pages);
    46
    47    return -ENOMEM;
    48   }

第5行代码,如果使用总是允许过量提交的策略,那么允许创建新的内存映射。

第8行代码,如果使用猜测的过量提交策略,那么估算可用内存的数量,处理如下。

1)第9行和第10行代码,空闲页加上文件页,文件页有后备存储设备支持,可以回收。

2)第12行代码,共享内存页不应该算作空闲页,它们不能被释放,只能换出到交换区。

3)第14行代码,加上交换区的空闲页数。

4)第16行代码,加上可回收的内存缓存页。使用SLAB_RECLAIM_ACCOUNT标志创建的内存缓存,宣称可回收,dentry和inode缓存应该属于这种情况。

5)第21行代码,减去保留的页数。

6)第23行和第24行代码,如果进程没有系统管理权限,那么减去为根用户保留的页数。

7)第26行和第27行代码,如果可用内存的页数大于申请的页数,那么允许创建新的内存映射。


如果使用不允许过量提交的策略,那么处理如下。

1)第32行代码,计算提交内存的上限。有两个控制参数:sysctl_overcommit_kbytes是字节数,sysctl_overcommit_ratio是比例值,sysctl_overcommit_kbytes的默认值是0, sysctl_overcommit_ratio的默认值是50。如果sysctl_overcommit_kbytes不是0,那么上限等于“sysctl_overcommit_kbytes + 交换区的空闲页数”,否则上限等于“(物理内存容量 − 巨型页总数)* sysctl_overcommit_ratio/100 + 交换区的空闲页数”。

2)第34行和第35行代码,如果进程没有系统管理权限,那么需要为根用户保留一部分内存。

3)第37~40行代码,为了防止一个用户启动一个消耗内存大的进程,保留一部分内存:取“进程虚拟内存长度的1/32”和“用户保留的页数”的较小值。

4)第42行和第43行代码,vm_committed_as是所有进程提交的虚拟内存的总和,如果它小于allowed,那么允许创建新的内存映射。