HugeTLB 页面¶
概述¶
此文件的目的是简要概述 Linux 内核中的 hugetlbpage 支持。此支持构建在大多数现代架构提供的多页大小支持之上。例如,x86 CPU 通常支持 4K 和 2M(如果架构支持,则为 1G)页面大小,ia64 架构支持多种页面大小:4K、8K、64K、256K、1M、4M、16M、256M,而 ppc64 支持 4K 和 16M。TLB 是虚拟到物理转换的缓存。通常,这是处理器上非常稀缺的资源。操作系统尝试充分利用有限数量的 TLB 资源。随着越来越大的物理内存(几个 GB)更容易获得,这种优化现在变得更加关键。
用户可以使用 Linux 内核中的大页支持,方法是使用 mmap 系统调用或标准 SYSV 共享内存系统调用(shmget、shmat)。
首先,需要使用 CONFIG_HUGETLBFS(位于“文件系统”下)和 CONFIG_HUGETLB_PAGE(当选择 CONFIG_HUGETLBFS 时自动选择)配置选项构建 Linux 内核。
/proc/meminfo
文件提供有关内核大页池中持久性 hugetlb 页面的总数的信息。它还显示默认的大页大小以及有关默认大小的大页池中空闲、保留和剩余大页数量的信息。大页大小是生成正确的对齐方式和系统调用参数大小所必需的,这些参数映射大页区域。
cat /proc/meminfo
的输出将包括如下行:
HugePages_Total: uuu
HugePages_Free: vvv
HugePages_Rsvd: www
HugePages_Surp: xxx
Hugepagesize: yyy kB
Hugetlb: zzz kB
其中
- HugePages_Total
是大页池的大小。
- HugePages_Free
是池中尚未分配的大页数量。
- HugePages_Rsvd
是“reserved”的缩写,是指已承诺从池中分配但尚未进行分配的大页数量。保留的大页保证应用程序能够在错误发生时从大页池中分配大页。
- HugePages_Surp
是 “surplus” 的缩写,是指池中超出
/proc/sys/vm/nr_hugepages
中的值的大页数量。剩余大页的最大数量由/proc/sys/vm/nr_overcommit_hugepages
控制。注意:当启用释放与每个 hugetlb 页面关联的未使用 vmemmap 页面的功能时,当系统处于内存压力下时,剩余大页的数量可能会暂时大于剩余大页的最大数量。- Hugepagesize
是默认的大页大小(以 kB 为单位)。
- Hugetlb
是所有大小的大页消耗的总内存量(以 kB 为单位)。如果正在使用不同大小的大页,则此数字将超过 HugePages_Total * Hugepagesize。要获取更详细的信息,请参考
/sys/kernel/mm/hugepages
(如下所述)。
/proc/filesystems
还应该显示内核中配置的类型为“hugetlbfs”的文件系统。
/proc/sys/vm/nr_hugepages
表示内核大页池中当前“持久”大页的数量。“持久”大页在任务释放时将返回到大页池。具有 root 权限的用户可以通过增加或减少 nr_hugepages
的值来动态分配更多或释放一些持久性大页。
注意:当启用释放与每个 hugetlb 页面关联的未使用 vmemmap 页面的功能时,当系统处于内存压力下时,我们可能无法释放用户触发的大页。请稍后重试。
用作大页的页面在内核内部保留,不能用于其他目的。在内存压力下,不能换出大页。
一旦将大量大页预分配到内核大页池,具有适当权限的用户可以使用 mmap 系统调用或共享内存系统调用来使用大页。请参阅下面对 使用大页的讨论。
管理员可以通过指定 “hugepages=N” 参数在内核启动命令行上分配持久性大页,其中 “N” = 请求的大页数量。这是分配大页最可靠的方法,因为内存尚未碎片化。
某些平台支持多种大页大小。要分配特定大小的大页,必须在大页启动命令参数前面加上大页大小选择参数 “hugepagesz=<size>”。<size> 必须以字节为单位指定,并带有可选的比例后缀 [kKmMgG]。可以使用 “default_hugepagesz=<size>” 启动参数选择默认的大页大小。
Hugetlb 启动命令行参数语义
- hugepagesz
指定大页大小。与 hugepages 参数结合使用以预先分配指定大小的大页数量。因此,hugepagesz 和 hugepages 通常以成对的方式指定,例如
hugepagesz=2M hugepages=512
hugepagesz 只能在命令行上针对特定的大页大小指定一次。有效的大页大小取决于架构。
- hugepages
指定要预先分配的大页数量。这通常跟随有效的 hugepagesz 或 default_hugepagesz 参数。但是,如果 hugepages 是第一个或唯一一个 hugetlb 命令行参数,则它隐式指定要分配的默认大小的大页数量。如果隐式指定了默认大小的大页数量,则不能被默认大小的 hugepagesz,hugepages 参数对覆盖。此参数还具有节点格式。节点格式指定要在特定节点上分配的大页数量。
例如,在具有 2M 默认大页大小的架构上
hugepages=256 hugepagesz=2M hugepages=512
将导致分配 256 个 2M 大页,并显示一条警告消息,指示 hugepages=512 参数被忽略。如果 hugepages 参数前面是无效的 hugepagesz 参数,则它将被忽略。
节点格式示例
hugepagesz=2M hugepages=0:1,1:2
它将在 node0 上分配 1 个 2M 大页,在 node1 上分配 2 个 2M 大页。如果节点号无效,则该参数将被忽略。
- default_hugepagesz
指定默认的大页大小。此参数只能在命令行上指定一次。default_hugepagesz 可以选择性地跟随 hugepages 参数,以预先分配特定数量的默认大小的大页。如上面的 hugepages 部分所述,也可以隐式指定要预先分配的默认大小的大页数量。因此,在具有 2M 默认大页大小的架构上
hugepages=256 default_hugepagesz=2M hugepages=256 hugepages=256 default_hugepagesz=2M
都将导致分配 256 个 2M 大页。有效的默认大页大小取决于架构。
- hugetlb_free_vmemmap
当设置 CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP 时,这将启用 HugeTLB Vmemmap 优化 (HVO)。
当支持多种大页尺寸时,/proc/sys/vm/nr_hugepages
表示当前预分配的默认尺寸的大页数量。因此,可以使用以下命令动态分配/释放默认尺寸的持久大页。
echo 20 > /proc/sys/vm/nr_hugepages
此命令将尝试将大页池中默认尺寸的大页数量调整为 20,根据需要分配或释放大页。
在 NUMA 平台上,内核将尝试将大页池分布在修改 nr_hugepages
的任务的 NUMA 内存策略指定的所有允许节点上。当任务具有默认内存策略时,允许节点的默认值是所有具有内存的在线节点。当分配持久大页时,具有可用连续内存不足以容纳大页的允许节点将被静默跳过。请参阅下面关于任务内存策略、cpusets 以及每个节点属性与持久大页的分配和释放之间的交互的讨论。
大页分配的成功或失败取决于分配尝试时系统中存在的物理连续内存量。如果内核无法从 NUMA 系统中的某些节点分配大页,它将尝试通过在其他具有足够可用连续内存的节点上分配额外的页面来弥补差异(如果有)。
系统管理员可能希望将此命令放入本地 rc init 文件之一。这将使内核能够在启动过程的早期分配大页,此时获取物理连续页面的可能性仍然很高。管理员可以通过检查 sysctl 或 meminfo 来验证实际分配的大页数量。要检查 NUMA 系统中每个节点的大页分布,请使用
cat /sys/devices/system/node/node*/meminfo | fgrep Huge
/proc/sys/vm/nr_overcommit_hugepages
指定如果应用程序请求的大页数量超过 /proc/sys/vm/nr_hugepages
,则大页池可以增长到多大。如果向此文件写入任何非零值,则表示当持久大页池耗尽时,hugetlb 子系统可以尝试从内核的普通页面池中获取该数量的“剩余”大页。当这些剩余大页不再使用时,它们将被释放回内核的普通页面池。
当通过 nr_hugepages
增加大页池大小时,任何现有的剩余页面将首先被提升为持久大页。然后,如果必要且可能,将分配额外的大页以满足新的持久大页池大小。
管理员可以通过将 nr_hugepages
sysctl 设置为较小的值来缩小默认大页尺寸的持久大页池。内核将尝试平衡修改 nr_hugepages
的任务的内存策略中所有节点上的大页释放。所选节点上的任何空闲大页都将被释放回内核的普通页面池。
注意:通过 nr_hugepages
缩小持久大页池,使其小于正在使用的大页数量,将会把正在使用的大页的余额转换为剩余大页。即使剩余页面的数量会超过超额分配值,也会发生这种情况。只要这种情况成立——也就是说,直到 nr_hugepages+nr_overcommit_hugepages
足够增加,或者剩余大页不再使用并被释放——将不允许分配更多的剩余大页。
由于支持在运行时提供多个大页池,因此 /proc/sys/vm
中的大多数大页用户空间接口已在 sysfs 中复制。上面讨论的 /proc
接口已保留以实现向后兼容。sysfs 中的根大页控制目录是
/sys/kernel/mm/hugepages
对于正在运行的内核支持的每个大页尺寸,都将存在一个子目录,其格式为
hugepages-${size}kB
在每个这样的目录中,都将存在 /proc
中包含的文件集。此外,可能存在两个用于降级大页的额外接口
demote
demote_size
nr_hugepages
nr_hugepages_mempolicy
nr_overcommit_hugepages
free_hugepages
resv_hugepages
surplus_hugepages
降级接口提供将大页拆分为更小的大页的能力。例如,x86 架构同时支持 1GB 和 2MB 的大页尺寸。可以将一个 1GB 的大页拆分为 512 个 2MB 的大页。最小的大页尺寸不提供降级接口。降级接口是
- demote_size
是降级页面的大小。当页面被降级时,将创建相应数量的 demote_size 大小的页面。默认情况下,demote_size 设置为下一个较小的大页尺寸。如果存在多个较小的大页尺寸,则可以将 demote_size 设置为这些较小尺寸中的任何一个。只允许小于当前大页尺寸的大页尺寸。
- demote
用于降级多个大页。具有 root 权限的用户可以写入此文件。可能无法降级请求数量的大页。要确定实际降级的页面数量,请比较写入降级接口之前和之后 nr_hugepages 的值。demote 是一个只写接口。
与 /proc
中相同的接口(除了 demote 和 demote_size 之外的所有接口)的功能与上述默认大页尺寸情况下的描述相同。
任务内存策略与大页分配/释放的交互¶
无论大页是通过 /proc
接口还是使用 nr_hugepages_mempolicy
属性的 /sysfs
接口分配和释放的,从中分配或释放大页的 NUMA 节点都由修改 nr_hugepages_mempolicy
sysctl 或属性的任务的 NUMA 内存策略控制。当使用 nr_hugepages
属性时,将忽略 mempolicy。
使用上述 nr_hugepages
示例,将大页分配到内核大页池或从中释放的推荐方法是
numactl --interleave <node-list> echo 20 \
>/proc/sys/vm/nr_hugepages_mempolicy
或者,更简洁地说
numactl -m <node-list> echo 20 >/proc/sys/vm/nr_hugepages_mempolicy
这将分别根据持久大页的数量最初是小于还是大于 20,将 abs(20 - nr_hugepages)
分配或释放到
当通过 nr_hugepages_mempolicy
调整持久大页计数时,可以使用任何内存策略模式——绑定、首选、本地或交错。对持久大页分配的最终影响如下:
无论 mempolicy 模式如何 [请参阅 NUMA 内存策略],持久大页都将像指定“交错”一样分布在 mempolicy 中指定的一个或多个节点上。但是,如果策略中的节点不包含足够连续的内存来容纳大页,则分配不会“回退”到具有足够连续内存的最近邻居节点。这样做会导致大页池分布中出现不希望的失衡,或者可能导致在任务的内存策略不允许的节点上分配持久大页。
可以使用绑定或交错策略指定一个或多个节点。如果使用首选策略指定多个节点,则只会使用数值 ID 最低的节点。本地策略将选择任务在构造 nodes_allowed 掩码时正在运行的节点。为了使本地策略具有确定性,任务必须绑定到单个节点中的一个或多个 CPU。否则,任务可能会在启动后的任何时间迁移到其他节点,并且最终的节点将是不确定的。因此,本地策略对于此目的不是很有用。可以使用任何其他 mempolicy 模式来指定单个节点。
允许的节点掩码将从任何非默认任务 mempolicy 中派生,无论此策略是由任务本身还是其祖先(例如 numactl)显式设置的。这意味着如果从具有非默认策略的 shell 调用任务,则将使用该策略。可以使用 numactl --interleave 或 --membind [-m] 指定“all”节点列表,以实现对系统或 cpuset 中所有节点的交错。
指定的任何任务 mempolicy(例如,使用 numactl)都将受到任务运行的任何 cpuset 的资源限制的约束。因此,在具有系统节点子集的 cpuset 中运行的具有非默认策略的任务将无法在 cpuset 之外分配大页,除非首先移动到包含所有所需节点的 cpuset。
启动时的大页分配尝试将请求数量的大页分布在所有具有内存的在线节点上。
每个节点的大页属性¶
上面描述的 sysfs 中根大页控制目录的部分内容将在中每个具有内存的 NUMA 节点的系统设备下复制
/sys/devices/system/node/node[0-9]*/hugepages/
在此目录下,每个支持的大页尺寸的子目录都包含以下属性文件
nr_hugepages
free_hugepages
surplus_hugepages
free_’ 和 surplus_’ 属性文件是只读的。它们分别返回父节点上空闲和剩余 [过度分配] 的大页数量。
nr_hugepages
属性返回指定节点上的大页总数。写入此属性时,无论任务的 mempolicy 或 cpuset 约束如何,如果存在足够的资源,则父节点上的持久大页数量将调整为指定值。
请注意,过提交和保留页面的数量仍然是全局性的,因为我们直到发生缺页故障时,才会应用故障任务的内存策略,从而得知将从哪个节点尝试分配大页。
在以下情况下,hugetlb 可能会在每个节点的大页池之间迁移:内存离线、内存故障、长期固定、系统调用(mbind、migrate_pages 和 move_pages)、alloc_contig_range()
和 alloc_contig_pages()
。现在,只有内存离线、内存故障和系统调用允许在当前节点无法分配的情况下回退到在不同节点上分配新的 hugetlb。这意味着这 3 种情况可能会破坏每个节点的大页池。
使用大页¶
如果用户应用程序要使用 mmap 系统调用请求大页,则系统管理员需要挂载一个类型为 hugetlbfs 的文件系统。
mount -t hugetlbfs \
-o uid=<value>,gid=<value>,mode=<value>,pagesize=<value>,size=<value>,\
min_size=<value>,nr_inodes=<value> none /mnt/huge
此命令在目录 /mnt/huge
上挂载一个类型为 hugetlbfs 的(伪)文件系统。在 /mnt/huge
上创建的任何文件都使用大页。
uid
和 gid
选项设置文件系统根目录的所有者和组。默认情况下,使用当前进程的 uid
和 gid
。
mode
选项将文件系统根目录的模式设置为值 & 01777。此值以八进制表示。默认情况下,选择值 0755。
如果平台支持多种大页大小,则可以使用 pagesize
选项指定大页大小和关联的池。pagesize
以字节为单位指定。如果未指定 pagesize
,将使用平台的默认大页大小和关联的池。
size
选项设置该文件系统(/mnt/huge
)允许的最大内存(大页)值。size
选项可以以字节为单位指定,也可以指定为指定的大页池(nr_hugepages
)的百分比。大小将向下舍入到 HPAGE_SIZE 边界。
min_size
选项设置文件系统允许的最小内存(大页)值。min_size
可以与 size
相同的方式指定,即字节或大页池的百分比。在挂载时,会为文件系统保留 min_size
指定的大页数量。如果可用的大页不足,挂载将失败。当大页分配给文件系统并释放时,将调整保留计数,以便已分配和保留的大页的总和始终至少为 min_size
。
nr_inodes
选项设置 /mnt/huge
可以使用的最大 inode 数量。
如果在命令行上未提供 size
、min_size
或 nr_inodes
选项,则不会设置任何限制。
对于 pagesize
、size
、min_size
和 nr_inodes
选项,可以使用 [G|g]/[M|m]/[K|k] 来表示千兆/兆/千。例如,size=2K 与 size=2048 具有相同的含义。
虽然在 hugetlb 文件系统上的文件支持 read 系统调用,但不支持 write 系统调用。
可以使用常规的 chown、chgrp 和 chmod 命令(具有正确的权限)来更改 hugetlbfs 上的文件属性。
此外,重要的是要注意,如果应用程序仅使用 shmat/shmget 系统调用或带有 MAP_HUGETLB 的 mmap,则不需要此类挂载命令。有关如何将 mmap 与 MAP_HUGETLB 一起使用的示例,请参见下面的 map_hugetlb。
希望通过共享内存段使用 hugetlb 内存的用户应是补充组的成员,并且系统管理员需要将该 gid 配置到 /proc/sys/vm/hugetlb_shm_group
中。相同或不同的应用程序可以使用 mmap 和 shm* 调用的任意组合,但是,如果要在不使用 MAP_HUGETLB 的情况下使用 mmap 调用,则需要挂载文件系统。
对由 hugetlb 页面支持的内存进行操作的系统调用仅将其长度与处理器本机页面大小对齐;如果未进行大页对齐,它们通常会因 errno 设置为 EINVAL 而失败,或者排除超出长度的 hugetlb 页面。例如,如果内存由 hugetlb 页面支持并且长度小于大页大小,则 munmap(2) 将失败。
示例¶
map_hugetlb
请参见 tools/testing/selftests/mm/map_hugetlb.c
hugepage-shm
请参见 tools/testing/selftests/mm/hugepage-shm.c
hugepage-mmap
请参见 tools/testing/selftests/mm/hugepage-mmap.c
libhugetlbfs 库提供了各种用户空间工具,以帮助提高大页的可用性、环境设置和控制。