# HugeTLB Pages
Date: 2020-01-07


这篇文章 [Huge TLB Pages](https://www.kernel.org/doc/html/latest/admin-guide/mm/hugetlbpage.html)是出Linux Kernel文档

## 概述

该文档目的是简要概述在Linux内核HugePage。 现代CPU体系架构支持基于多个页面。 例如，x86 CPU通常支持4K和2M（如果体系结构支持，则为1G）页面大小，ia64架构支持多种页面大小4K，8K，64K，256K，1M，4M，16M，256M和ppc64支持4K和16M。 TLB是虚拟到物理的缓存转化。 通常，这是处理器上非常稀缺的资源。 操作系统试图充分利用有限数量的TLB资源。 现在，随着物理内存越来越大，这种优化变得越来越（几个GB）容易获得。

用户可以通过使用mmap系统调用来使用Linux内核中的Huge Page或标准SYSV共享内存系统调用(shmget，shmat)。

首先，Linux内核需要使用`CONFIG_HUGETLBFS构建`和`CONFIG_HUGETLB_PAGE`配置选项。

`/proc/meminfo` 文件提供内核`Huge Page`池中的持久性`Huge Page`的总数。它还显示默认`Huge Page`大小和空闲的数量，保留数量的信息 以及默认大小的`Huge Page`池中的多余`Huge Page`。 需要`Huge Page`大小才能生成正确的对齐方式， 映射`Huge Page`区域的系统调用的参数大小。

`/proc/meminfo` 文件中包含`Huge Page`信息如下:

```C
    HugePages_Total: uuu
    HugePages_Free:  vvv
    HugePages_Rsvd:  www
    HugePages_Surp:  xxx
    Hugepagesize:    yyy kB
    Hugetlb:         zzz kB
```

```C
HugePages_Total
    总页面数量
HugePages_Free
    未使用页面数量
HugePages_Rsvd
    预留未使用数量
HugePages_Surp
    预留未使用数量, 指大于/proc/sys/vm/nr_hugepages部分，
    最大保留值由/proc/sys/vm/nr_overcommit_hugepages控制.
Hugepagesize
    每个页的大小单位Kb
Hugetlb
    占用内存大小（kB）相当于 HugePages_Total * Hugepagesize
    详细信息可以查看 /sys/kernel/mm/hugepages
```

`/proc/filesystems` 可以看到内核中的的`hugetlbfs`文件系统类型

`/proc/sys/vm/nr_hugepages`表示当前Linux内核中`持久化`HugePage页面数量, 内核的巨大页面池中的页面。 `持久化`的HugePage将当任务释放时返回到Huge Page池。 具有root用户特权可以通过增加或减少`nr_hugepages`的值动态分配更多或释放一些`持久化`的Huge Page.

用作Huge Page的页面保留在内核内部，不能用于其他目的。大页面内存不足时不能换出。

一旦将许多Huge Page预分配给内核大页面池，具有适当特权的用户可以使用mmap系统调用或共享内存系统调用以使用大页面。

管理员可以在内核启动时分配持久的Huge Pages命令行通过指定`hugepages=N`参数，其中'N'=请求的Huge Pages数。 这是最可靠的方法. 由于内存尚未分散，因此分配大页面。

一些平台支持多种Huge Pages尺寸。分配Huge Page特定大小的文件，必须在大页面启动命令参数之前具有Huge Page大小选择参数`hugepagesz = <size>`。 必须以字节为单位指定，并带有可选的标度后缀\[kKmMgG\]。 默认巨大可以使用`default_hugepagesz = <size>`启动参数选择页面大小

临时的修改方案是，当支持多个Huge Page大小时，/proc/sys/vm/nr\_hugepages 表示当前具有默认大小的预分配大页面的数量。 因此，可以使用以下命令动态分配/取消分配默认大小的永久大页面：：

```C
   echo 20 > /proc/sys/vm/nr_hugepages
```

该命令将尝试调整默认尺寸的Huge Page数量。 Huge Page池（最多20个），根据需要分配或释放巨大的页面。

在NUMA平台上，通过修改`nr_hugepages`,内核将尝试分配Huge Page Pool到由NUMA内存策略指定的所有的节点集上。当使用默认内存策略时，所有联机节点配置内存。 分配持久的`Huge Page`时，将自动跳过`Huge Page`的连续内存。 请参见下面有关任务内存策略，cpuset和每个节点属性与持久`Huge page`的分配和释放的交互的讨论。

大量页面分配的成功与否取决于发生故障时系统中存在的物理连续内存分配尝试。 如果内核无法从 NUMA系统中的某些节点，它将尝试通过以下方式弥补差异, 在具有足够可用连续性的其他节点上分配额外的页面内存。

系统管理员可能希望将此命令放在本地rc文件中。这将使内核能够在早期分配大量页面引导过程中是否有可能获得物理连续页面仍然很高。 管理员可以验证大页面的数量通过检查sysctl或meminfo实际分配。 检查每个节点在NUMA系统中分发Huge Page，请使用：

```C
   cat /sys/devices/system/node/node*/meminfo | fgrep Huge
```

`/proc/sys/vm/nr_overcommit_hugepages` 指定Huge Page池的增长大小 如果比`/proc/sys/vm/nr_hugepages` 更多的Huge Page由应用程序请求。 将任何非零值写入此文件表示允许hugetlb子系统尝试获取内核正常页面池中的`多余`Huge Pages，当持久的Huge Pages池已耗尽。随着这些多余的Huge Page变成未使用时，它们被释放回内核的普通页面池。

当通过`nr_hugepages`增加Huge Page池大小时，任何现有的多余的页面将首先被提升为持久的Huge Page。 然后，如果有必要，将分配Huge Page以满足新的持久性Huge Page池大小。

管理员可以通过将`nr_hugepages` sysctl设置为较小的值来将持久的Huge Page 池缩小为默认的大页面大小。 内核将尝试在修改`nr_hugepages`的任务的内存策略中平衡所有节点之间的Huge page释放。 所选节点上的所有免费大页面都将被释放回内核的普通页面池。

警告：通过`nr_hugepages`缩小持久的Huge Page池它变得少于正在使用的Huge Page数，它将转换为余额使用中的Huge Page中多余的Huge Page。 即使发生多余的页面数将超过过量使用值。 只要此条件成立-即直到`nr_hugepages + nr_overcommit_hugepages`为增加足够多，或者多余的Huge Page不再使用并被释放-不再允许分配多余的大页面。

通过在运行时支持多个Huge Page池，许多 `/proc/sys/vm`中的Huge Page用户空间接口已被复制到sysfs。 上面讨论的`/proc`接口已保留以供向后使用兼容性。 sysfs中的控制目录是：

```C
    /sys/kernel/mm/hugepages
```

对于运行中的内核支持的每个Huge Page大小，都有一个子目录将以以下形式存在：

```C
    hugepages-${size}kB
```

在每个目录中，将存在相同的文件集：

```C
    nr_hugepages
    nr_hugepages_mempolicy
    nr_overcommit_hugepages
    free_hugepages
    resv_hugepages
    surplus_hugepages
```

对于默认的Huge Page大小的情况如上所述。

## 任务内存策略与Huge Page分配/释放的交互

无论是使用`nr_hugepages_mempolicy`属性通过`/proc`接口还是`/sysfs`接口分配和释放Huge Page，分配或释放Huge Page的NUMA节点都由修改`nr_hugepages_mempolicy` sysctl或属性的任务的NUMA内存策略控制。 使用`nr_hugepages`属性时，`mempolicy`将忽略。

使用上面的`nr_hugepages`示例，推荐的向内核分配或从中释放Huge Page的方法是：

```
   numactl --interleave <node-list> echo 20 > \
   >/proc/sys/vm/nr_hugepages_mempolicy
```

或

```
numactl -m <node-list> echo 20 > \
/proc/sys/vm/nr_hugepages_mempolicy 
```

这将向或从中指定的节点分配或释放 abs(20-nr\_hugepages)，具体取决于持久性Huge Page的数量最初是否小于或大于20。 在未包含在指定的中的任何节点上，不会分配或释放任何Huge Page。

通过`nr_hugepages_mempolicy`调整持久性Huge Page计数时，可以使用任何内存策略模式(bind, preferred, local or interleave)。对持久性Huge Page分配的最终影响如下：

1. 无论采用哪种内存模式\[请参见Documentation/admin-guide/mm /numa\_memory\_policy.rst\]，持久性Huge Page都将分布在内存中指定的一个或多个节点上，就像已指定`interleave`一样。但是，如果策略中的节点不包含用于Huge Page的足够的连续内存，则分配不会`fallback`到具有足够连续内存的最近邻居节点。这样做可能会导致Huge Page池的分配出现不希望的不平衡，或者可能导致任务的内存策略不允许的节点上的持久Huge Page分配。
    
2. 可以使用`bind`或`interleave`策略指定一个或多个节点。如果使用首选策略指定了多个节点，则仅使用最低的数字ID。`local`策略将在构建`nodes_allowed`掩码时选择任务正在运行的节点。为了确定`local`策略，必须将任务绑定到单个节点中的cpu或cpus。否则，任务可以在启动后随时迁移到其他某个节点，并且最终的节点将不确定。因此，`local`策略对于此目的不是很有用。可以使用任何其他记忆模式来指定单个节点。
    
3. 无论此策略是由任务本身还是由其祖先之一(例如`numactl`)显式设置的，允许的节点掩码都将从任何非默认任务内存中得出。 这意味着，如果使用非默认策略从shell调用任务，则将使用该策略。 可以使用numactl –interleave或–membind \[-m\]指定节点列表`all`，以实现对系统或cpuset中所有节点的交叉。
    
4. 指定的任何任务记忆(例如:使用numactl)都会受到运行任务的任何CPU的资源限制的约束。 因此，没有默认策略的任务无法在具有系统节点子集的cpuset中运行，而没有先移动到包含所有所需节点的cpuset的情况下，就在cpuset之外分配Huge Page。
    
5. 引导时Huge Page分配尝试在所有具有内存的在线节点上分配请求数量的Huge Page。
    

## 每个节点的Huge Page属性

如上所述，将在`sysfs`中的root huge page控制目录的内容的子集复制到每个NUMA节点的每个系统设备下，并将内存存储在

```C
    /sys/devices/system/node/node[0-9]*/hugepages/
```

在这些子目录下又包含一下属性文件

```C
    nr_hugepages
    free_hugepages
    surplus_hugepages
```

`free_`和`extra_`属性文件是只读的。 它们分别返回父节点上的空闲和多余\[overcommitted\] Huge pages的数量。

`nr_hugepages`属性返回指定节点上的Huge page总数。 写入此属性后，如果存在足够的资源，则无论任务的内存条件或`cpuset`约束如何，父节点上的持久性Huge page数都会被调整为指定的值。

请注意，`overcommit`页面和`reserve`页面的数量仍然是全局数量，直到故障时间（当应用了故障任务的`mempolicy`），我们才知道该错误，然后才尝试从哪个节点分配Huge page。

## Huge Pages的使用

如果用户应用程序打算使用mmap系统调用来请求Huge Page，则要求系统管理员安装类型为`hugettlbfs`的文件系统

```C
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`目录中挂载类型为`hugettlbfs`的（伪）文件系统。在`/mnt/huge`上创建的任何文件都使用Huge Page。

`uid`和`gid`选项:设置文件系统根目录的所有者和组。默认情况下，采用当前进程的uid和gid。

`mode`选项:将文件系统的root模式设置为值＆01777。此值以八进制形式给出。默认情况下，选择值0755。

`pagesize`选项：如果平台支持多个Huge Page大小，则可以使用pagesize选项指定Huge Page大小和关联的池。 pagesize以字节为单位指定。如果未指定pagesize，则将使用平台的默认尺寸和关联的池。

`size`选项:设置该文件系统`/mnt/huge`允许的内存最大值(huge pages)。 size选项可以以字节为单位，也可以以指定的大页面池（nr\_hugepages）的百分比形式指定。大小四舍五入到`HPAGE_SIZE`边界。

`min_size`选项设置文件系统允许的最小内存(Huge pages)值。 min\_size可以用与大小相同的方式指定，可以是字节，也可以是Huge Page池的百分比。在安装时，由min\_size指定的Huge Page数保留给文件系统使用。如果没有足够的可用免费Huge Page，则挂载将失败。当将Huge Pages分配给文件系统并释放时，将调整保留计数，以使分配和保留的Huge Pages的总和始终至少为`min_size`。

`nr_inodes`选项设置 `/mnt/huge`可以使用的最大索引节点数。

如果命令行上未提供`size`，`min_size`或`nr_inodes`选项，则不设置限制。

For pagesize, size, min\_size and nr\_inodes options, you can use \[G|g\]/\[M|m\]/\[K|k\] to represent giga/mega/kilo. For example, size=2K has the same meaning as size=2048.

对于`pagesize`，`size`，`min_size`和`nr_inodes`选项，可以使用\[G|g\]/\[M|m\]/\[K|k\]表示. 例如，size = 2K具有与size = 2048相同的含义。

在hugetlb文件系统上的文件支持读取系统调用，但不支持写入系统调用。

常规的chown，chgrp和chmod命令可用于更改hugetlbfs上的文件属性。

另外，请务必注意，如果应用程序仅使用`shmat`/ `shmget`系统调用或`MAP_HUGETLB`的`mmap`，则不需要此类安装命令。 有关如何在MAP\_HUGETLB中使用mmap的示例，请参见下面的map\_hugetlb。

希望通过共享内存段使用`hugetlb`内存的用户应该是补充组的成员，并且系统管理员需要将该`gid`配置为 `/proc/sys/vm/ugetlb_shm_group`。 相同或不同的应用程序可以使用`mmap`和`shm*`调用的任意组合，尽管使用不带`MAP_HUGETLB`的`mmap`调用将需要安装文件系统。

> _a supplementary group: 对应useradd -G选项_ _[鸟哥私房菜-Linux 帐号管理](http://linux.vbird.org/linux_basic/0410accountmanager.php#account_group)_

在`Huge page`支持的内存上运行的系统调用的长度只能与处理器的本机页面大小对齐; 它们通常会在`errno`设置为`EINVAL`的情况下失败，或者如果不对`Huge page`进行对齐，则会排除超出长度的`Huge Page`。 例如，如果内存由`hugetlb`页支持并且长度小于`hugepage size`，则munmap(2)将失败。

## 示例

```C
map_hugetlb
    see tools/testing/selftests/vm/map_hugetlb.c

hugepage-shm
    see tools/testing/selftests/vm/hugepage-shm.c

hugepage-mmap
    see tools/testing/selftests/vm/hugepage-mmap.c
```

The [libhugetlbfs](https://github.com/libhugetlbfs/libhugetlbfs) 库提供了广泛的用户态工具以帮助实现Huge Page可用性，环境设置和控制.

