build_all_zonelists
该函数在系统初始化阶段建立每个node下的zonelist结构
函数第3行:设置初始化每一个node下zonelist的策略:举个例子说明:x86_64架构,64位系统下,假设系统中有node0和node1,node0中有DMA,DMA32,NORMAL,MOVABLE四个内存去,node1中有NORMAL,MOVABLE两个内存区。假设node0的初始化zonelist由一下两种方案:
第一种方案:[node0(MOVABLE),node0(NORMAL),node0(DMA32),node(DMA),node1(MAVABLE),node1(NORMAL)]
第二种方案:[node0(MOVABLE),node1(MOVABLE),node0(NORMAL),node1(NORMAL),node0(DMA32),node0(DMA)]
根据buddy系统的zone的fallback规则:
第一种方案考虑的时优先分配举例比较近的cpu,这样会加快cpu每次内存的访问速度;但是缺点是:会加快DMA32和DMA内存区的消耗,例如:当分buddy系统选中node0作为分配节点时,第一种方案对于要分配normal区和movable区的请求,如果node0上这两个区内存紧张,就会fallback到node0的DMA32和DMA区。如果DMA,DMA32内存去内存过渡消耗(本身这两个内存去空间就比较小DMA为固定16M,DMA32固定为4G)到真正必须要从DMA,DMA32分配内存时,就很容易出发OOM.
第二种方案优先考虑了减少内存分配请求对DMA,DMA32内存区的压力,次优先考虑了node节点之间的距离,这样会降低cpu每次访问内存的熟读。原因:还用上面的例子:如果node0上的movable和normal区紧张时,会优先分被fallback到node1上的movable和normal区,只有node0和node1上的normal,movable都紧张时,才会fallback到DMA和DMA32区。这样就减少了因”必须要申请DMA,DMA32内存去内存而失败的概率。
内核的做法:
1.默认由一个规则通过内核的内存现状选择上面的一种方案来初始化node的zonelist
2.导出了配置文件到proc文件系统或者通过sysctl命令来让用户自己设定使用种方案来初始化node的zonelist
第3行的逻辑:如果用户通过sysctl或者proc设置了zonelist初始化的策略,就用用户的设置,如果没有就根据系统种的内存情况选择两种方案种的一种(具体怎么选择看下文关于:default_zonelist_order的介绍
接下来重点说一下第15行到第26行的逻辑(TODO:补充完整对本函数的说明)
第16行计算了所有zone中高于high内存水线的页数(举个例子:假设系统中只有两个zone,zone0总共由10个页,zone1总共由15个页,zone0的high水线时5,zone1的high水线时8,那么vm_total_pages就等于(10-5+15-8))第23行到第26行初始化了一个重要的全局变量:page_group_by_mobility_disabled,这个全局变量内核没有提供用户态接口,并且这里设置后,整个系统运行期间没有办法改变。也就是说如果系统中所有zone中高于内存水线的总页数如果小于pageblock_nr_pages*MIGRATE_TYPE那么就关闭按页的移动性分组的功能。相反则开启该功能。
【注】作者见过的所有生产环境中(通用服务器):vm_total_pages都远远大于pageblock_nr_pages*MIGRATE_TYPES,因此在通用服务器应用场景可以认为page_group_by_mobility_disabled始终等于0(可能有些嵌入式系统中内存比较少时,会为1)
void build_all_zonelists(void) { set_zonelist_order(); if (system_state == SYSTEM_BOOTING) { __build_all_zonelists(NULL); mminit_verify_zonelist(); cpuset_init_current_mems_allowed(); } else { /* we have to stop all cpus to guarantee there is no user of zonelist */ stop_machine(__build_all_zonelists, NULL, NULL); /* cpuset refresh routine should be here */ } vm_total_pages = nr_free_pagecache_pages(); /* * Disable grouping by mobility if the number of pages in the * system is too low to allow the mechanism to work. It would be * more accurate, but expensive to check per-zone. This check is * made on memory-hotadd so a system can start with mobility * disabled and enable it later */ if (vm_total_pages < (pageblock_nr_pages * MIGRATE_TYPES)) page_group_by_mobility_disabled = 1; else page_group_by_mobility_disabled = 0; printk("Built %i zonelists in %s order, mobility grouping %s. " "Total pages: %ld\n", nr_online_nodes, zonelist_order_name[current_zonelist_order], page_group_by_mobility_disabled ? "off" : "on", vm_total_pages); #ifdef CONFIG_NUMA printk("Policy zone: %s\n", zone_names[policy_zone]); #endif }
default_zonelist_order
--------------------------------------------------------------------------------------------------
TODO1: