我们讲页框分配器的时侯提到了快速分配和慢速分配,其中伙伴算法是在快速分配里做的,忘掉的男子伴我们再看下:

static struct page *
get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
const struct alloc_context *ac)
{
for_next_zone_zonelist_nodemask(zone, z, ac->zonelist, ac->high_zoneidx, ac->nodemask)
{
if (!zone_watermark_fast(zone, order, mark, ac_classzone_idx(ac), alloc_flags))
{
ret = node_reclaim(zone->zone_pgdat, gfp_mask, order);
switch (ret) {
case NODE_RECLAIM_NOSCAN:
continue;
case NODE_RECLAIM_FULL:
continue;
default:
if (zone_watermark_ok(zone, order, mark, ac_classzone_idx(ac), alloc_flags))
goto try_this_zone;

continue;
}
}

try_this_zone: //本zone正常水位
page = rmqueue(ac->preferred_zoneref->zone, zone, order, gfp_mask, alloc_flags, ac->migratetype);
}

return ;
}

可以看见在进行伙伴算法分配前有个关于水位的判定,明天我们就看下水位的概念。

简单的说在使用分区页面分配器中会将可以用的freepages与zone里的水位(watermark)进行比较。

水位初始化

buffer linux_buffer linux_buffer linux

int __meminit init_per_zone_wmark_min(void)
{
unsigned long lowmem_kbytes;
int new_min_free_kbytes;

//nr_free_buffer_pages是获取ZONE_DMA和ZONE_NORMAL区中高于high水位的总页数nr_free_buffer_pages = managed_pages - high_pages
lowmem_kbytes = nr_free_buffer_pages * (PAGE_SIZE >> 10);
new_min_free_kbytes = int_sqrt(lowmem_kbytes * 16);

if (new_min_free_kbytes > user_min_free_kbytes) {
min_free_kbytes = new_min_free_kbytes;
//min的值必须在128kib-65536kib之间
if (min_free_kbytes < 128)
min_free_kbytes = 128;
if (min_free_kbytes > 65536)
min_free_kbytes = 65536;
} else {
pr_warn("min_free_kbytes is not updated to %d because user defined value %d is preferredn",
new_min_free_kbytes, user_min_free_kbytes);
}
//得到总的min后,就可以根据各个zone在总内存中的占比,通过do_div计算出他们各自的min值。
setup_per_zone_wmarks;
refresh_zone_stat_thresholds;
setup_per_zone_lowmem_reserve;

return 0;
}

buffer linux_buffer linux_buffer linux

从这张图可以看出:

buffer linux_buffer linux_buffer linux

安卓系统中对水位的调节

为了防止directreclaimbuffer linuxbuffer linux,我们须要空余的显存大小仍然保持在min值以上。但安卓这些大量用户操作网路接收的系统中,难免会碰到数据量猛然减小,须要临时申请大量的显存,此时有可能kswapd回收的显存速率大于显存分配的速率,即发生directreclaim,进而阻塞应用严重影响性能。

我们晓得在显存分配时,只有low和min之间的区域才是kswapd活动的区域。而linux中默认的low与min之间的值又比较小,所以就很容易导致directreclaim的情况。

「extra_free_kbytes」:

buffer linux_buffer linux_buffer linux

始于此,安卓在linux水位的基础上降低了extra_free_kbytes的变量,这个extra时额外加在low和min之间的,它在min不变的情况下,让low值有所减小。

源码如下:

static void __setup_per_zone_wmarks(void)
{
unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10);
unsigned long pages_low = extra_free_kbytes >> (PAGE_SHIFT - 10);

......

for_each_zone(zone) {
......
do_div(min, lowmem_pages);
......
}

calculate_totalreserve_pages;
}

想要晓得extra_free_kbytes的引入是否取得疗效,可以通过/proc/vmstat中的pageoutrun和allocstall来看,二者分别代表了kswapd和directreclaim启动的次数。

「watermark_scale_factor」:

内核总是在进步的linux命令tar,在linux内核4.6版本中,又诞生了一种新的调节水位的方法,即watermark_scale_factor系数,其默认值是10,对应显存占比10/10000=0.1%,可通过

/proc/sys/vm/watermark_scale_factor设置,最大值是1000。举个反例:当其被设为1000时,意味着min与low之间的差值,low与high之间的差值都将是显存大小的10%(1000/10000)。

buffer linux_buffer linux_buffer linux

后面讲的extra_free_kbytes的方法只减小了min和low之间的差值,而watermark_scale_factor则同时减小了min和low,low和high之间的差值。

至此,页框分配器中的快速分配方法早已结束linux解压命令,下一篇让我们步入页框分配器的慢速分配方法:__alloc_pages_slowpath,涉及到显存碎片的整理和显存回收。

Tagged:
Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复