0%

Linux内存管理(八)页面迁移和内存规整

原图

声明本文转载自知乎Linux内存管理:页面迁移Linux内存管理 (16)内存规整内存相关文章

页面迁移

概述

页面迁移(migrate)是内存管理中很多功能实现的基础,比方说内存规整、NUMA balance、内存热插拔、CMA、HugeTLB、内存气球等等,本文主要讲一下页面迁移的主要流程和应用。

页面迁移的API

migrate_pages( ) 这是页面迁移在内核态的主要接口,内核中涉及到页面迁移的功能大都会调到它。当然,在用户空间也存在着内存迁移相关的系统调用,最终也会调到它。这里我们通过migrate_pages( )的几个形参展开全文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
* migrate_pages - migrate the pages specified in a list, to the free pages
* supplied as the target for the page migration
*
* @from: The list of pages to be migrated.
* @get_new_page: The function used to allocate free pages to be used
* as the target of the page migration.
* @put_new_page: The function used to free target pages if migration
* fails, or NULL if no special handling is necessary.
* @private: Private data to be passed on to get_new_page()
* @mode: The migration mode that specifies the constraints for
* page migration, if any.
* @reason: The reason for page migration.
*/
int migrate_pages(struct list_head *from, new_page_t get_new_page,
free_page_t put_new_page, unsigned long private,
enum migrate_mode mode, int reason)

形参含义根据上面的注释很好理解,我们单独说下mode和reason,他们分别表示迁移模式和迁移原因。

迁移模式

迁移模式主要会影响迁移过程中对一些行为的过滤:

1
2
3
4
MIGRATE_ASYNC        //异步迁移,过程中不会发生阻塞
MIGRATE_SYNC_LIGHT //轻度同步迁移,允许大部分的阻塞操作,唯独不允许脏页的回写操作
MIGRATE_SYNC //同步迁移,迁移过程会发生阻塞,若需要迁移的某个page正在writeback或被locked会等待它完成
MIGRATE_SYNC_NO_COPY //同步迁移,但不等待页面的拷贝过程。页面的拷贝通过回调migratepage(),过程可能会涉及DMA

举例:内存规整(compact)中会调用migrate_pages(),同时也会设置迁移模式(位于compact_control->mode)。若是sysfs主动触发的内存规整会用MIGRATE_SYNC模式;若是kcompactd触发的规整会用MIGRATE_SYNC_LIGHT模式;若是内存分配slowpath中触发的会根据compact prior去设置用MIGRATE_ASYNC或MIGRATE_SYNC_LIGHT模式。

迁移原因

迁移原因主要使用来记录是什么功能触发了迁移的行为,毕竟页面迁移对系统本身是个不小的overhead,所以知道迁移的原因很有必。

1
2
3
4
5
6
7
MR_COMPACTION      //内存规整导致的迁移
MR_MEMORY_FAILURE //当内存出现硬件问题(ECC校验失败等)时触发的页面迁移。 参考memory-failure.c
MR_MEMORY_HOTPLUG //内存热插拔导致的迁移
MR_SYSCALL //应用层主动调用migrate_pages()或move_pages()触发的迁移
MR_MEMPOLICY_MBIND //调用mbind系统调用设置memory policy时触发的迁移
MR_NUMA_MISPLACED //numa balance触发的页面迁移(node之间)
MR_CONTIG_RANGE //调用alloc_contig_range()为CMA或HugeTLB分配连续内存时触发的迁移(和compact相关)

延伸:对上面MR_SYSCALL迁移原因中提到的两个API(位于libnuma库)的简单介绍。

1
2
3
4
5
// 含义:把某pid进程的所有页面从源numa node迁移到目标numa nodes组中,对应系统调用sys_migrate_pages()
migrate_pages() //和kernel中接口同名,别搞混了

// 含义:把进程到部分页面迁移目标numa node中,对应系统调用sys_move_pages()
move_pages()

migrate_pages()介绍

通过上面对迁移原因的介绍,我们知道了页面迁移的原因各种各样,这里我们通过内存规整(memory compact)这样一个实际的例子展开介绍,下图主要体现了内存规整和页面迁移的关系:

通过上图,我们看出页面迁移在内存规整中举足轻重的地位。下面则是内存规整调用migrate_pages()的函数关系:

1
2
3
compact_zone           //内存规整
->migrate_pages //页面迁移的入口函数
->unmap_and_move //页面迁移的核心实现

现在切入重点,页面迁移(migrate)过程做了那些事情:

迁移前的准备

  • 获取old page的页面锁PG_locked (异步模式拿不到锁就直接跳过此页面)
  • 若是正在writeback的页面,则根据迁移模式判断是否等待页面wirteback(MIGRATE_SYNC_LIGHT和MIGRATE_ASYNC不等待)
  • 获取new page的页面锁PG_locked (new page正常情况下不会获取失败)

解除old page的页表映射

调用try_to_unmap(),通过反向映射机制解除old page所有相关的PTEs

设置new page的页面内容、元数据及映射关系

调用move_to_new_page拷贝old page的内容和struct page元数据到new page,并通过反向映射机制建立new page的映射关系。

页面迁移的收尾工作

  • 迁移完成释放新旧页面的PG_locked
  • 设置迁移原因
  • 调用put_page释放old page引用计数(_refcount减1)

补充:什么样的页面才支持迁移呢?

  • lru上的page,因为上面挂的是user spcace用的pages,都是从buddy分配器migrate type为movable或reclaim的pageblock上分来的
  • no-lru但是movable的页面。no-lru的页面通常是为kernel space分配的page,这种页面通常是unmovable的,但是下面这个commit使得驱动中用到的页面也可以支持迁移。但是在驱动实现的过程中需要置位page->mapping中的second bit,并且实现page->mapping->a_ops中的相关方法

内存规整

内存碎片的产生:伙伴系统以页为单位进行管理,经过大量申请释放,造成大量离散且不连续的页面。这时就产生了很多碎片。 n内存规整也即内存碎片整理,内存碎片也是以页面为单位的。实现基础是内存页面按照可移动性进行分组。内存规整的实现基础是页面迁移。 Linux内核以pageblock为单位来管理页的迁移属性。

内存规整的触发

下面是内存页面分配,以及分配失败之后采取的措施,以便促成分配成功。 可以看出采取的措施,越来越重。首先采用kswapd来进行页面回收,然后尝试页面规整、直接页面回收,最后是OOM杀死进程来获取更多内存空间

1
2
3
4
5
6
7
8
9
alloc_pages-------------------------------------页面分配的入口
->__alloc_pages_nodemask
->get_page_from_freelist--------------------直接从zonelist的空闲列表中分配页面
->__alloc_pages_slowpath--------------------在初次尝试分配失败后,进入slowpath路径分配页面
->wake_all_kswapds------------------------唤醒kswapd内核线程进行页面回收
->get_page_from_freelist------------------kswapd页面回收后再次进行页面分配
->__alloc_pages_direct_compact------------进行页面规整,然后进行页面分配
->__alloc_pages_direct_reclaim------------直接页面回收,然后进行页面分配
->__alloc_pages_may_oom-------------------尝试触发OOM

另一条路径是在kswapd的balance_pgdat中会判断是否需要进行内存规整。

1
2
3
kswapd
->balance_pgdat-------------------------------遍历内存节点的zone,判断是否处于平衡状态即WMARK_HIGH。
->compact_pgdat-----------------------------针对整个内存节点进行内存规整

其中compact_pddat->__compact_pgdat->compact_zone,最终的实现和__alloc_pages_direct_compact调用compact_zone一样。

内存规整相关节点

内存规整相关有两个节点,compact_memory用于触发内存规整;extfrag_threshold影响内核决策是采用内存规整还是直接回收来满足大内存分配。

节点入口代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static struct ctl_table vm_table[] = {
...
#ifdef CONFIG_COMPACTION
{
.procname = "compact_memory",
.data = &sysctl_compact_memory,
.maxlen = sizeof(int),
.mode = ,
.proc_handler = sysctl_compaction_handler,
},
{
.procname = "extfrag_threshold",
.data = &sysctl_extfrag_threshold,
.maxlen = sizeof(int),
.mode = ,
.proc_handler = sysctl_extfrag_handler,
.extra1 = &min_extfrag_threshold,
.extra2 = &max_extfrag_threshold,
},

#endif /* CONFIG_COMPACTION */
...
{ }
}
  • /proc/sys/vm/compact_memory
    打开compaction Tracepoint:echo 1 > /sys/kernel/debug/tracing/events/compaction/enable
    触发内存规整:sysctl -w vm.compact_memory=1
    查看Tracepoint:cat /sys/kernel/debug/tracing/trace

  • /proc/sys/vm/extfrag_threshold
    在compact_zone中调用函数compaction_suitable->__compaction_suitable进行判断是否进行内存规整
    和extfrag_threshold相关部分如下,如果当前fragindex不超过sysctl_extfrag_threshold,则不会继续进行内存规整
    所以这个参数越小越倾向于进行内存规整,越大越不容易进行内存规整

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    static unsigned long __compaction_suitable(struct zone *zone, int order,
    int alloc_flags, int classzone_idx)
    {
    ...
    fragindex = fragmentation_index(zone, order);
    if (fragindex >= 0 && fragindex <= sysctl_extfrag_threshold)
    return COMPACT_NOT_SUITABLE_ZONE;

    return COMPACT_CONTINUE;
    }

    设置extfrag_threshold:sysctl -w vm.extfrag_threshold=500

  • 其它Debug信息 /sys/kernel/debug/extfrag/extfrag_index /sys/kernel/debug/extfrag/unusable_index

参考文献

https://zhuanlan.zhihu.com/p/270366827 https://www.bbsmax.com/A/ZOJPrQgodv/