随着系统的运行,经过不同用户的分配请求后,页框会变得十分分散,导致此段页框被这些正在使用的零散页框分为一小段一小段非连续页框,这使得在需要分配内存时很难找到物理上连续的页框。
现代处理器不再限于使用传统的4K大小的页框;它们可以在进程的部分地址空间中支持大得多的页(huge pages)。使用巨页会带来真正的性能优势,主要原因是减小了对处理器的转换后备缓冲区(translation lookaside buffer)的压力。但是使用巨页要求系统能够找到物理上连续的内存区域,这些区域不仅要足够大,而且还必须确保按适当方式满足字节对齐的要求。
在一个已经运行了一段时间的系统上会产生大量的不连续的page, 要想找到符合这些高阶(high-order)条件的内存空间非常具有挑战性,memory compaction的作用就是解决high-order内存分配失败问题,与buddy system机制做一个互补。
内存碎片整理以pageblock为单位。 在内存碎片整理开始前,会在zone的头和尾各设置一个指针,头指针从头向尾扫描可移动的页,而尾指针从尾向头扫描空闲的页,当他们相遇时终止整理。
简单示意图:需要明确的是:实际情况并不是与图示的情况完全一致。头指针每次扫描一个符合要求的pageblock里的所有页框,当pageblock不为MIGRATE_MOVABLE、MIGRATE_CMA、MIGRATE_RECLAIMABLE时会跳过这些pageblock,当扫描完这个pageblock后有可移动的页框时,会变为尾指针以pageblock为单位向前扫描可移动页框数量的空闲页框,但是在pageblock中也是从开始页框向结束页框进行扫描,最后会将前面的页框内容复制到这些空闲页框中。
这里的移动是将页框中的数据copy拷贝到可移动的空闲页框当中,此时原有的movable page变成free page。所以并不是页框自身的移动而是数据的移动。
通过下图的操作就可以分配出一个order = 2或者是order = 3的连续的可用空间,可用于满足更high-order的内存分配。当然,这里展示的流程和真实系统比起来已经大大简化了。实际的内存域会大得多,这意味着扫描的工作量也会大很多,但由此获得的空闲区也可能更大。
实际的内存碎片,还有一个问题就是在整理算法中会将扫描中识别为不满足整理要求的内存块标识为 “可忽略”(“skip”,即不执行规整)。作为一种优化,目的是防止运行没必要的规整操作。
比如系统正在对zone进行内存碎片整理,首先,会从可移动页框开始位置向后扫描一个pageblock,得到一些可移动页框,然后空闲页框从开始位置向前扫描一个pageblock,得到一些空闲页框,然后将可移动页框移动到空闲页框中,之后再继续循环扫描。对一个pageblock进行扫描后,如果无法从此pageblock隔离出一个要求的页框,这时候就会将此pageblock标记为跳过(skip)。
假设内存碎片整理可移动页扫描是从zone的第一个页框开始,扫描完一个pageblock后,没有隔离出可移动页框,则标记此pageblock的跳过标记PB_migrate_skip,然后将zone-
compact_cached_migrate_pfn设置为此pageblock的结束页框。
这样,在下次对此zone进行内存碎片整理时,就会直接从此pageblock的下一个pageblock开始,把此pageblock跳过了。同理,对于空闲页扫描也是一样。这样就必须更新zone pageblock的起始地址与结束地址:
以上就是内存碎片整理的基本原理了。
在内存碎片整理中,可以移动的页框有MIGRATE_RECLAIMABLE、MIGRATE_MOVABLE与MIGRATE_CMA这三种类型的页框。
而因为内存碎片整理分为同步和异步。在异步过程中,只会移动MIGRATE_MOVABLE和MIGRATE_CMA这两种类型的页框。因为这两种类型的页框处理,是不会涉及到IO操作的。而在同步过程中,这三种类型的页框都会进行移动,因为MIGRATE_RECLAIMABLE基本上都是文件页,在移动过程中,有可能要将脏页回写,会涉及到IO操作,也就是在同步过程中,是会涉及到IO操作的。
1、migrate_mode迁移模式:
enum migrate_mode {
MIGRATE_ASYNC,
MIGRATE_SYNC_LIGHT,
MIGRATE_SYNC,
};
2、compact_priority-
enum compact_priority {
COMPACT_PRIO_SYNC_FULL,
MIN_COMPACT_PRIORITY = COMPACT_PRIO_SYNC_FULL,
COMPACT_PRIO_SYNC_LIGHT,
MIN_COMPACT_COSTLY_PRIORITY = COMPACT_PRIO_SYNC_LIGHT,
DEF_COMPACT_PRIORITY = COMPACT_PRIO_SYNC_LIGHT,
COMPACT_PRIO_ASYNC,
INIT_COMPACT_PRIORITY = COMPACT_PRIO_ASYNC
};
3、compact_result用于压缩处理函数的返回值-
enum compact_result {
/* For more detailed tracepoint output - internal to compaction */
COMPACT_NOT_SUITABLE_ZONE,//trace用于调试输出或内部使用
/*
* compaction didn't start as it was not possible or direct reclaim
* was more suitable
*/
COMPACT_SKIPPED,//跳过压缩,因为无法执行压缩或直接回收更合适
/* compaction didn't start as it was deferred due to past failures */
COMPACT_DEFERRED,
/* compaction not active last round */
COMPACT_INACTIVE = COMPACT_DEFERRED,
/* For more detailed tracepoint output - internal to compaction */
COMPACT_NO_SUITABLE_PAGE,
/* compaction should continue to another pageblock */
COMPACT_CONTINUE,
/*
* The full zone was compacted scanned but wasn't successfull to compact
* suitable pages.
*/
COMPACT_COMPLETE,//已完成所有区域的压缩,但是尚未确保可以通过压缩分配的页面
/*
* direct compaction has scanned part of the zone but wasn't successfull
* to compact suitable pages.
*/
COMPACT_PARTIAL_SKIPPED,
/* compaction terminated prematurely due to lock contentions */
COMPACT_CONTENDED,
/*
* direct compaction terminated after concluding that the allocation
* should now succeed
*/
COMPACT_SUCCESS,//在确保可分配页面安全后,直接压缩结束
};
4、compact_control需要进行内存碎片整理时,总是需要初始化该结构体
struct compact_control {
/* 扫描到的空闲页的页的链表 */
struct list_head freepages; /* List of free pages to migrate to */
/* 扫描到的可移动的页的链表 */
struct list_head migratepages; /* List of pages being migrated */
/* 空闲页链表中的页数量 */
unsigned long nr_freepages; /* Number of isolated free pages */
/* 可移动页链表中的页数量 */
unsigned long nr_migratepages; /* Number of pages to migrate */
/* 空闲页框扫描所在页框号 */
unsigned long free_pfn; /* isolate_freepages search base */
/* 可移动页框扫描所在页框号 */
unsigned long migrate_pfn; /* isolate_migratepages search base */
/* 内存碎片整理使用的模式: 同步,轻同步,异步 */
enum migrate_mode mode; /* Async or sync migration mode */
/* 是否忽略pageblock的PB_migrate_skip标志对需要跳过的pageblock进行扫描 ,并且也不会对pageblock设置跳过
* 只有两种情况会使用
* 1.调用alloc_contig_range()尝试分配一段指定了开始页框号和结束页框号的连续页框时;
* 2.通过写入1到sysfs中的/vm/compact_memory文件手动实现同步内存碎片整理。
*/
bool ignore_skip_hint; /* Scan blocks even if marked skip */
/* 本次内存碎片整理是否隔离到了空闲页框,会影响zone的空闲页扫描起始位置 */
bool finished_update_free; /* True when the zone cached pfns are
* no longer being updated
*/
/* 本次内存碎片整理是否隔离到了可移动页框,会影响zone的可移动页扫描起始位置 */
bool finished_update_migrate;
/* 申请内存时需要的页框的order值 */
int order; /* order a direct compactor needs */
const gfp_t gfp_mask; /* gfp mask of a direct compactor */
/* 扫描的管理区 */
struct zone *zone;
/* 保存结果,比如异步模式下是否因为需要阻塞而结束了本次内存碎片整理 */
int contended; /* Signal need_sched() or lock
* contention detected during
* compaction
*/
};
5、Node zone 扫描推迟-
struct zone
{
.....
unsigned int compact_considered;
unsigned int compact_defer_shift;
int compact_order_failed;
......
}
当一个zone要进行内存碎片整理时,首先会判断本次整理需不需要推迟,如果本次内存碎片整理使用的order值小于zone内存碎片整理失败最大order值compact_order_failed时,不用进行推迟,可以直接进行内存碎片整理;
当order值大于zone内存碎片整理失败最大order值compact_order_failed,会增加内存碎片整理推迟计数器compact_considered,如果内存碎片整理推迟计数器compact_considered未达到内存碎片整理推迟阀值defer_limit,则会跳过本次内存碎片整理,如果达到了,那就需要进行内存碎片整理。
总结:也就是当order小于zone内存碎片整理失败最大order值时,不用进行推迟,而order大于zone内存碎片整理失败最大order值时,才考虑是否进行推迟,此时推迟就是continue扫描node当中的下一个zone区域,这里并不是想下文一下设置zone SKIP标志。
6、Pageblock skip
struct zone
{
......
unsigned long compact_cached_free_pfn;
/* pfn where async and sync compaction migration scanner should start */
unsigned long compact_cached_migrate_pfn[2];
......
}
内存碎片整理移动发生条件:
内存分配不足时触发direct compact整理内存
Kswapd内存回收后唤醒kcompactd内核线程执行compact操作,获取连续内存
手动设置echo 1 > /proc/sys/vm/compact_memory
分析的重点就放在内存分配不足的情况,入口函数从try_to_compact_pages开始
对源码详细分析参见代码:https://github.com/linuxzjs/linux-4.14
重点分析5个关键函数:
1、compaction_suitable
/* 判断该zone是否可以做内存碎片压缩整理 */
enum compact_result compaction_suitable(struct zone *zone, int order,
unsigned int alloc_flags,
int classzone_idx)
{
enum compact_result ret;
int fragindex;
/*
* 根据watermask判断zone中离散的page是否满足2^order的内存分配请求,如果满足则继续对zone进行内存的compact整理zone的内存碎片
* 说明该zone时可以做内存碎片的压缩整理的。
*/
ret = __compaction_suitable(zone, order, alloc_flags, classzone_idx,zone_page_state(zone, NR_FREE_PAGES));
/* 如果return返回值为COMPACT_CONTINUE,且order > PAGE_ALLOC_COSTLY_ORDER(3)则进入一下判断当中 */
if (ret == COMPACT_CONTINUE && (order > PAGE_ALLOC_COSTLY_ORDER)) {
/*
* 为了确定zone区域是否执行压缩,找到所请求区域zone和顺序的碎片系数。
* 如果碎片系数值返回-1000,则存在要分配的页面,因此不需要压缩。
* 在其他情况下,该值在0到500的范围内,并且如果它小于sysctl_extfrag_threshold,则直接return COMPACT_NOT_SUITABLE_ZONE不执行压缩
*/
fragindex = fragmentation_index(zone, order);
if (fragindex >= 0 && fragindex <= sysctl_extfrag_threshold)
ret = COMPACT_NOT_SUITABLE_ZONE;
}
.....
return ret;
}
由此可以知道,判断是否执行内存的碎片整理,需要满足以下三个条件:在__compaction_suitable当中可以得出:
减去申请的页面,空闲页面数将低于水印值;或者虽然大于等于水印值,但是没有一个足够大的连续的空闲页块;
空闲页面减去两倍的申请页面,高于水印值;在fragmentation_index中:
申请的order大于PAGE_ALLOC_COSTLY_ORDER时,计算碎片指数fragindex来判断;
2、compact_finished
通过该函数判断zone区域碎片整理compact是否完成
static enum compact_result __compact_finished(struct zone *zone,struct compact_control *cc)
{
unsigned int order;
/* 获取zone的移动类型 */
const int migratetype = cc->migratetype;
.....
/* Compaction run completes if the migrate and free scanner meet */
/* 当cc->free_pfn <= cc->migrate_pfn空闲扫描于可移动页面扫描相遇则说明zone碎片扫描压缩完成 */
if (compact_scanners_met(cc)) {
/* Let the next compaction start anew. */
/* 重置压缩扫描起始地址于结束地址的位置 */
reset_cached_positions(zone);
/* 如果是直接压缩模式则设置compact_blockskip_flush = true,清除PG_migrate_skip的skip属性 */
if (cc->direct_compaction)
zone->compact_blockskip_flush = true;
/*
* 如果whole_zone = 1说明zone是从头开始扫描,扫描zone整个区域 return COMPACT_COMPLETE,表示zone扫描完成
* 如果whole_zone = 0说明zone是从局部开始扫描的,也就是在zone的更新的free_page或者是migrate_page当中扫描
* 也就是也就是局部的pageblock的扫描,return COMPACT_PARTIAL_SKIPPED表示跳过该pageblock,扫描下一个pageblock
*/
if (cc->whole_zone)
return COMPACT_COMPLETE;
else
return COMPACT_PARTIAL_SKIPPED;
}
/* 执行压缩时,将返回COMPACT_CONTINUE以强制压缩整个块,这个于手动模式有关
* echo 1> /proc/sys/vm/compact_memory
*/
if (is_via_compact_memory(cc->order))
return COMPACT_CONTINUE;
/* 如果扫描完成,则进入判断当中,做进一步判断验证 */
if (cc->finishing_block) {
/* 再次检查迁移扫描程序与pageblock是否对齐,如果对齐则说明页面压缩已经完成重置cc->finishing_block = false
* 如果没有对齐则,并返回COMPACT_CONTINUE以继续扫描进行zone的页面扫描压缩操作
*/
if (IS_ALIGNED(cc->migrate_pfn, pageblock_nr_pages))
cc->finishing_block = false;
else
return COMPACT_CONTINUE;
}
/* Direct compactor: Is a suitable page free? */
/*
* 从当前order开始扫描,order -> MAX_ORDER进行,
*/
for (order = cc->order; order < MAX_ORDER; order++) {
/* 根据order获取free_area */
struct free_area *area = &zone->free_area[order];
bool can_steal;
/* Job done if page is free of the right migratetype */
/* 如果该area->free_list[migratetype])不为NULL,不为空则COMPACT_SUCCESS压缩扫描成功 */
if (!list_empty(&area->free_list[migratetype]))
return COMPACT_SUCCESS;
/* 如果定义了CONFIG_CMA如果移动类型为MIGRATE_MOVABLE可移动类型,且area->free_list[MIGRATE_CMA])不为空则return COMPACT_SUCCESS */
#ifdef CONFIG_CMA
/* MIGRATE_MOVABLE can fallback on MIGRATE_CMA */
if (migratetype == MIGRATE_MOVABLE &&
!list_empty(&area->free_list[MIGRATE_CMA]))
return COMPACT_SUCCESS;
#endif
/* 如果area->free_list[migratetype]以及area->free_list[MIGRATE_CMA])均为空则取对应的migratetype的fallback当中寻找合适可用的page
* 判断是否能够完成页面的压缩。
*/
if (find_suitable_fallback(area, order, migratetype,
true, &can_steal) != -1) {
/* movable pages are OK in any pageblock */
/* 如果可移动类型为MIGRATE_MOVABLE则直接return COMPACT_SUCESS
* 说明只要是可以移动的page都可用作页面压缩功能。
*/
if (migratetype == MIGRATE_MOVABLE)
return COMPACT_SUCCESS;
/* 如果正在执行aync异步压缩,或者如果迁移扫描程序已完成一页代码块,则返回COMPACT_SUCCESS */
if (cc->mode == MIGRATE_ASYNC ||
IS_ALIGNED(cc->migrate_pfn,
pageblock_nr_pages)) {
return COMPACT_SUCCESS;
}
/* 如果fallback当中没有找到合适可用的page则设置cc->finishing_block = true;return COMPACT_CONTINUE zone还需要继续扫描,
* skip到下一个pageblock或者是下一个zone
*/
cc->finishing_block = true;
return COMPACT_CONTINUE;
}
}
/* 如果从order -> max_order都没有找到可用的page用作直接的页面迁移压缩则return COMPACT_NO_SUITABLE_PAGE表明没有可用的页面用于压缩 */
return COMPACT_NO_SUITABLE_PAGE;
}
3、isolate_migratepages
在zone当中以pageblock为单位,扫描找到migratepage可移动页,并将page添加struct compact_control *cc的migratepages链表当中,便于后边做页面内容的拷贝移动。其实隔离的作用就是将可移动页面拿出来,单独存放,与之前的pageblock分开
4、isolate_freepages
freepages的过程与migratepages的过程基本上是完全一致的,隔离结束的条件基本上也是一致的。
不同点就是freepage在找到pageblock的page进行isolate隔离操作前会判断这个page是如何组成的,是一个复合page还是非复合页,如果不是要获取这个page的order,如果该page是由2^order个单独的page组合起来的还要将这个page拆分成单独的page也就是order = 0的这种情况,然后将单独的page移动到freepages链表上,并设置page新的类型为MIGRATE_MOVABLE供后续使用。
5、migrate_pages
当完成freepages、migratepages完成隔离后就调migrate_pages完成两个链表的页面迁移。
err = migrate_pages(&cc->migratepages, compaction_alloc,
compaction_free, (unsigned long)cc, cc->mode,
MR_COMPACTION);
compact_alloc函数,从zone区域当中扫描freepages并提填充到cc->freepages链表当中,再从cc->freepages链表中取出一个空闲页
static struct page *compaction_alloc(struct page *migratepage,
unsigned long data, int **result)
{
struct compact_control *cc = (struct compact_control *)data;
struct page *freepage;
/*
* Isolate free pages if necessary, and if we are not aborting due to
* contention.
*/
/* 如果cc中的空闲页框链表为空 */
if (list_empty(&cc->freepages)) {
if (!cc->contended)
isolate_freepages(cc);/* 从cc->free_pfn开始向前获取空闲页 */
if (list_empty(&cc->freepages))
return NULL;
}
/* 从cc->freepages链表取出一个空闲的freepages */
freepage = list_entry(cc->freepages.next, struct page, lru);
/* 将该page从lru链表当中删除 */
list_del(&freepage->lru);
cc->nr_freepages--;
/* 返回空闲页框 */
return freepage;
}
static void compaction_free(struct page *page, unsigned long data)
{
struct compact_control *cc = (struct compact_control *)data;
list_add(&page->lru, &cc->freepages);
cc->nr_freepages++;
}
这里先避开PageHuge不谈,migrate_pages通过调用unmap_and_move、__unmap_and_move、move_to_new_page、try_to_unmap完成页面最终的整理工作。这里面涉及的rmap反向映射这里不再展开。
分析过reclaim内存回收代码就会发现,在内存回收当中同样会wakeup_kcompactd触发compaction碎片整理机制,在kswpad异步内存回收当中存在同样的操作。同时与kswapd机制类似目前内核在node节点当中也引入了kcompactd线程机制,定时的休眠唤醒该内核线程完成内存碎片的整理,在新的patch当中更是将kswapd与kcompactd结合起来共同完成内存碎片的整理。内存回收工作。(END)
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/a9HUIIzBtTxjdDP-Ki0Hiw
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。