Kvm Thp   

在linux2.6.38之前,处理大容量内存工作集的高性能关键计算应用是运行在libhugetlbfs之上,必须依赖于 hugetlbfs,。透明Hugepage支持是一种替代手段,它使用大内存页,并且虚拟内存页的大小可以动态变化,没有hugetlbfs的缺点。 目前只适用于匿名内存映射,但未来可以扩展到tmpfs的papecache层。

大内存页的优点: 巨大的页面可以通过减少缺页提升性能(一次缺页分配大块的内存),还可以通过减少虚拟地址到物理地址的转换成本(减少转化次数),甚至可以避免地址转换。如 果处理器必须转换一个虚拟地址,它必须通过多达四层页表。处理器保持一个“翻译后备缓冲器(TLB)的缓存的转换结果。TLB的容量很小,一般是128条 指令转换、32条数据转换。非常容易被填满,从而必须进行大量的地址转换。例如:2m的页大小只需要一个tlb项,而4k的页大小就需要512个tlb 项。通过内核地址使用大内存页,可以减少tlb的压力。 基于以上原因,使用大内存页的程序会运行的很快。

hugetlbfs 缺点:

(1)巨页必须在内核启动时刻进行设置,并且需要应用程序显示映射。 (2)更倾向于大的专有数据库管理系统

设计

与 hugetlbfs通过将不可用内存用来作为cache或者可移动的(或者甚至无法移动实体)的保留内存方式相比,透明Hugepage可以使空闲内存利 最大化。它不需要保留内存,以防止大内存页分配。这对用户态是透明的。大内存页允许分页和所有其他的先进的虚拟技术,并且对用户是透明的,不需要修改应用 程序来使用这些技术。

应用可以更好的使用这些功能。例如可以避免有优化之前的malloc(4k)的mmap系统洪水。这种优化不是强制的,khugepaged会处理理那些不使用大内存的来处理大量内存的应用程序。

在某些情况下,系统范围使用大页面,会导致应用分配更多的内存资源。例如应用程序MMAP了一大块内存,但是只涉及1个字节,在这种情况下,2M页面代替4K页的分配没有任何好处,这就是为什么禁用全系统大页面,而只针对MADV_HUGEPAGE区使用的原因。

嵌入式系统仅仅在madvise区域使能大页面分配可以消除浪费内存的任何风险,并且程序运行的更快。

== sysfs==

透明Hugepage支持可以被完全禁用(多为调试目的)或只启用MADV_HUGEPAGE(以避免消耗更多的内存资源的风险)或启用系统 范围。 echo always >/sys/kernel/mm/transparent_hugepage/enabled echo madvise >/sys/kernel/mm/transparent_hugepage/enabled echo never >/sys/kernel/mm/transparent_hugepage/enabled

它 也可以限制在创造大页面的内存碎片整理的影响,他们不是立即对madvise区空闲或从未尝试碎片整理内存而使用规则页面,除非是立即可用。显然,我们使 用CPU来碎片整理内存,希望获得的更多的大内存页,而不是普通页面。这并不是保证,但它更可能的情况下分配为MADV_HUGEPAGE区。

echo always >/sys/kernel/mm/transparent_hugepage/defrag echo madvise >/sys/kernel/mm/transparent_hugepage/defrag echo never >/sys/kernel/mm/transparent_hugepage/defrag

khugepaged会自动启动时当transparent_hugepage/enabled设置为“always”或“madvise”,它会自动关机,如果它被设置为“never”。

khugepaged通常在低频运行。

echo 0 >/sys/kernel/mm/transparent_hugepage/khugepaged/defrag echo 1 >/sys/kernel/mm/transparent_hugepage/khugepaged/defrag

你还可以控制khugepaged每次扫描多少页。

/sys/kernel/mm/transparent_hugepage/khugepaged/pages_to_scan

扫描间隔(毫秒) /sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs

当分配大内存失败等待多少毫秒来压制下一个大内存页的分配尝试。 /sys/kernel/mm/transparent_hugepage/khugepaged/alloc_sleep_millisecs

可以看出,倒塌的内存页数量

/sys/kernel/mm/transparent_hugepage/khugepaged/pages_collapsed

每次扫描:

/sys/kernel/mm/transparent_hugepage/khugepaged/full_scans

引导参数

你可以改变透明Hugepage的默认支持。 通过内核命令行参数”transparent_hugepage=always” or “transparent_hugepage=madvise” or “transparent_hugepage=never”。

需要重新启动应用程序

transparent_hugepage/enabled 只影响未来行为。因此,使它们有效,你需要重新启动任何 应用程序可以一直使用大页面。这也适用于注册khugepaged的内存区。

get_user_pages和follow_page

get_user_pages和follow_page在hugepage上运行,将返回 像往常一样的头或尾页(正如hugetlbfs所做的)。

多数GUP用户只关心实际的物理页面地址和其I/O完成后的释放,所以他们将不会注意到的页面其实是巨页的。但如果驱动程序操作页面结构(如检查起始页、末尾页的page->mapping或相关的其他位),它应去检查头页。 注意:对GUP API没有新的约束,跟hugetlbfs具有相同的约束。所以任何在hugetlbfs中操作GUP的驱动可以在 透明hugepage支持映射中运行的很多。

如果你不能处理通过follow_page返回的混合页,FOLL_SPLIT位可以被指定为follow_page参数,这样它将会返回之前分离巨页。

优化应用程序

要保证内核立即映射到一个2M的页面在任何内存区域中,在mmap区域必须是hugepage对齐。 posix_memalign()可以提供这种保证。

Hugetlbfs

您可以使用在配置透明巨页功能的内核中使用hugetlbfs。除了没有整体内存碎片外,其他没有区别。所有属于hugetlbfs的通用功能都保存并且不受影响。 libhugetlbfs也将像以前一样正常工作。

优雅的回退

查询pagetables的时候可以使用split_huge_page_pmd 检查pmd_offset返回的pmd。

如果没有查询pagetables并且碰上一个物理hugepage但你不能在你的代码处理它本身,可以使用split_huge_page分离它。

了解hugepage代码的锁定

我们要尽可能知道hugepage代码,使用split_huge_page()或split_huge_page_pmd()有代价的。

为 了使查询页表时意识到 huge pmd,你需要做的是调用pmd_trans_huge 在pmd_offset返回后。您必须持有mmap_sem(读或写),确保huge pmd不能被khugepaged创建(khugepaged collapse_huge_page在获取mmap_sem和anon_vma lock在写模式下执行)。 如 果pmd_trans_huge返回false,就走旧代码路径。相反,如果pmd_trans_huge返回true,你必须要获取 page_table_lock和重新执行pmd_trans_huge。page_table_lock将防止将huge pmd转换成常规pmd(split_huge_page可以并行执行检查页表)。如果第二次pmd_trans_huge返回flase,你应该丢弃 page_table_lock和执行以前的代码路径。否则你应该执行pmd_trans_splitting。pmd_trans_splitting 返回true,这意味着split_huge_page已经在分裂页面中。因此,如果pmd_trans_splitting返回true,丢弃 page_table_lock和执行wait_split_huge_page,然后执行旧的代码路径。当wait_split_huge_page返 回时候,pmd不再是巨页的。如果pmd_trans_splitting返回false,就可以进行处理hugepmd和hugepage。一旦完成, 你可以释放page_table_lock。

compound_lock,get_user_pages和put_page

在 从页结构清除所有PG_head/tai位之前,split_huge_page函数必须从头页到尾页设置refcounts值。对huge pmd映射的refcounts很容易做到。get_page和put_page运行在尾页的时候(仅在尾页),它们必须找到其各自的头页, 然后减少头页和尾页的refcount。为了得到可靠的头页和在无竞争状态下减少refcount值,put_page必须串行获取每页锁compound_lock使用__split_huge_page_refcount。 compound_lock。

https://www.kernel.org/doc/Documentation/vm/transhuge.txt

2. 透明大页

使用大页可以提高系统内存的使用效率和性能,不过大页有如下几个缺点:

1) 大页必须在使用前就预留下来(1GB 大页还只能在启动时分配)。
2) 应用程序代码必须显式使用大页(一般是调用 libhugetlbfs API 来分配大页)。
3) 大页必须常驻物理内存中,不能给交换到交换分区中。
4) 需要超级用户权限来挂载 hugetlbfs文件系统。
5) 如果预留了大页内存但没有实际使用就会造成物理内存的浪费。

透明大页(Transparent Hugepage)正是发挥了大页的一些优点,又能避免了上述缺点。 透明大页,如它的名词描述的一样,对所有应用程序都是透明的,应用程序不需要任何修改即可享受透明大页带来的好处。在使用透明大页时,普通的使用 hugetlbfs 大页依然可以正常使用,而在没有普通的大页可供使用时,才使用透明大页。透明大页时可交换的,当需要交换到交换空间时,透明的大页被打碎为常规的 4KB 大小的内存页。在使用透明大页时,如果因为内存碎片导致大页内存分配失败,这时系统可以优雅地使用常规的 4KB 页替换,而且不会发生任何错误、故障或用户态的通知。而当系统内存较为充裕、有 很多的大页面可用时,常规的页分配的物理内存可以通过 khugepaged 进程自动迁往透明大页内存。内核进程 khugepaged的作用是,扫描正在运行的进程,然后试图将使用的常规内存页转换到使用大页。目前,透明大页仅仅支持匿名内存的映射,对磁盘缓存和共享内存的透明大页支持还处于开发之中。

使用透明大页的步骤如下: 1) 在编译 Linux 内核时,配置好透明大页的支持,配置文件中的示例如下:

CONFIG_TRANSPARENT_HUGEPAGE=y
CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set
这表示默认对所有应用程序的内存分配都尽可能地使用透明大页。当然,还可以在系统启动时修改 Linux 内核的启动参数来调整这个默认值(transparent_hugepage),其取值为如下 3 个值之一:
    transparent_hugepage=[always|madvise|never]

2) 在运行的宿主机中配置透明大页的使用方式,命令行如下

# cat /sys/kernel/mm/transparent_hugepage/enabled
# cat /sys/kernel/mm/transparent_hugepage/defrag
# cat /sys/kernel/mm/transparent_hugepage/khugepage/defrag
# echo "never" > /sys/kernel/mm/transparent_hugepage/defrag
# cat /sys/kernel/mm/transparent_hugepage/defrag

/sys/kernel/mm/transparent_hugepage/enabled 接口的值为 always,表示尽可能地在内存分配中使用透明大页。 /sys/kernel/mm/transparent_hugepage/defrag接口是表示系统在发生页故障(page fault)时同步地做内存碎片的整理工作,其运行的频率较高(某些情况下会带来额外的负担); /sys/kernel/mm/transparent_hugepage/khugepage/defrag 接口表示在 khugepaged 进程运行时进行内存碎片的整理工作它的运行频率较低。 当然还可以在 KVM 客户机中也使用透明大页,这样在宿主机和客户机同事使用的情况下,更容易提高内存使用的性能。

3) 查看 系统使用透明大页的效果

# cat /proc/meminfo | grep -i AnonHugePages
    AnonHugePages:   688128 KB
# echo $((688128/2048))

关于透明大页的使用,这里有一篇文章: https://access.redhat.com/solutions/46111