HugeTLB 控制器

可以通过首先挂载 cgroup 文件系统来创建 HugeTLB 控制器。

# mount -t cgroup -o hugetlb none /sys/fs/cgroup

完成上述步骤后,初始或父 HugeTLB 组将在 /sys/fs/cgroup 可见。在启动时,该组包含系统中的所有任务。/sys/fs/cgroup/tasks 列出了此 cgroup 中的任务。

可以在父组 /sys/fs/cgroup 下创建新组

# cd /sys/fs/cgroup
# mkdir g1
# echo $$ > g1/tasks

上述步骤创建了一个新组 g1 并将当前 shell 进程 (bash) 移入其中。

控制文件简要概述

hugetlb.<hugepagesize>.rsvd.limit_in_bytes            # set/show limit of "hugepagesize" hugetlb reservations
hugetlb.<hugepagesize>.rsvd.max_usage_in_bytes        # show max "hugepagesize" hugetlb reservations and no-reserve faults
hugetlb.<hugepagesize>.rsvd.usage_in_bytes            # show current reservations and no-reserve faults for "hugepagesize" hugetlb
hugetlb.<hugepagesize>.rsvd.failcnt                   # show the number of allocation failure due to HugeTLB reservation limit
hugetlb.<hugepagesize>.limit_in_bytes                 # set/show limit of "hugepagesize" hugetlb faults
hugetlb.<hugepagesize>.max_usage_in_bytes             # show max "hugepagesize" hugetlb  usage recorded
hugetlb.<hugepagesize>.usage_in_bytes                 # show current usage for "hugepagesize" hugetlb
hugetlb.<hugepagesize>.failcnt                        # show the number of allocation failure due to HugeTLB usage limit
hugetlb.<hugepagesize>.numa_stat                      # show the numa information of the hugetlb memory charged to this cgroup

对于支持三种大页大小(64k、32M 和 1G)的系统,控制文件包括

hugetlb.1GB.limit_in_bytes
hugetlb.1GB.max_usage_in_bytes
hugetlb.1GB.numa_stat
hugetlb.1GB.usage_in_bytes
hugetlb.1GB.failcnt
hugetlb.1GB.rsvd.limit_in_bytes
hugetlb.1GB.rsvd.max_usage_in_bytes
hugetlb.1GB.rsvd.usage_in_bytes
hugetlb.1GB.rsvd.failcnt
hugetlb.64KB.limit_in_bytes
hugetlb.64KB.max_usage_in_bytes
hugetlb.64KB.numa_stat
hugetlb.64KB.usage_in_bytes
hugetlb.64KB.failcnt
hugetlb.64KB.rsvd.limit_in_bytes
hugetlb.64KB.rsvd.max_usage_in_bytes
hugetlb.64KB.rsvd.usage_in_bytes
hugetlb.64KB.rsvd.failcnt
hugetlb.32MB.limit_in_bytes
hugetlb.32MB.max_usage_in_bytes
hugetlb.32MB.numa_stat
hugetlb.32MB.usage_in_bytes
hugetlb.32MB.failcnt
hugetlb.32MB.rsvd.limit_in_bytes
hugetlb.32MB.rsvd.max_usage_in_bytes
hugetlb.32MB.rsvd.usage_in_bytes
hugetlb.32MB.rsvd.failcnt
  1. 页错误记账

hugetlb.<hugepagesize>.limit_in_bytes
hugetlb.<hugepagesize>.max_usage_in_bytes
hugetlb.<hugepagesize>.usage_in_bytes
hugetlb.<hugepagesize>.failcnt

HugeTLB 控制器允许用户限制每个控制组的 HugeTLB 使用(页错误),并在页错误期间强制执行限制。由于 HugeTLB 不支持页回收,因此在页错误时强制执行限制意味着,如果应用程序尝试分配超出其限制的 HugeTLB 页,它将收到 SIGBUS 信号。因此,应用程序需要事先准确知道它使用了多少 HugeTLB 页,并且系统管理员需要确保机器上有足够的可用页来满足所有用户,以避免进程收到 SIGBUS。

  1. 预留记账

hugetlb.<hugepagesize>.rsvd.limit_in_bytes
hugetlb.<hugepagesize>.rsvd.max_usage_in_bytes
hugetlb.<hugepagesize>.rsvd.usage_in_bytes
hugetlb.<hugepagesize>.rsvd.failcnt

HugeTLB 控制器允许限制每个控制组的 HugeTLB 预留,并在预留时以及 HugeTLB 内存未被预留时发生页错误时强制执行控制器限制。由于预留限制在预留时(在 mmap 或 shget 上)强制执行,如果内存事先已预留,预留限制永远不会导致应用程序收到 SIGBUS 信号。对于 MAP_NORESERVE 分配,预留限制与页错误限制行为相同,在错误时强制执行内存使用,如果超出限制,则导致应用程序收到 SIGBUS。

预留限制优于上述页错误限制,因为预留限制在预留时(在 mmap 或 shget 上)强制执行,并且如果内存事先已预留,则永远不会导致应用程序收到 SIGBUS 信号。这允许更容易地回退到其他替代方案,例如非 HugeTLB 内存。在页错误记账的情况下,很难避免进程收到 SIGBUS,因为系统管理员需要精确地知道系统中所有任务的 HugeTLB 使用情况,并确保有足够的页来满足所有请求。在超额提交的系统上,通过页错误记账实际上不可能避免任务收到 SIGBUS。

  1. 共享内存的注意事项

对于共享 HugeTLB 内存,HugeTLB 预留和页错误都记入导致内存被预留或发生页错误的第一个任务,并且此后对该预留或发生页错误的内存的所有后续使用都不记账。

共享 HugeTLB 内存仅在取消预留或解除分配时才解除记账。这通常发生在 HugeTLB 文件被删除时,而不是导致预留或页错误的任务退出时。

  1. HugeTLB cgroup 离线的注意事项。

当一个 HugeTLB cgroup 离线时,如果它仍然记账有某些预留或页错误,其行为如下:

  • 页错误记账将记入父 HugeTLB cgroup(重新分配父级),

  • 预留记账保留在离线的 HugeTLB cgroup 上。

这意味着如果一个 HugeTLB cgroup 在仍记账有 HugeTLB 预留时离线,该 cgroup 将作为僵尸状态持续存在,直到所有 HugeTLB 预留都解除记账。HugeTLB 预留的这种行为与内存控制器匹配,内存控制器的 cgroup 也会作为僵尸状态持续存在,直到所有记账的内存都解除记账。此外,HugeTLB 预留的跟踪比 HugeTLB 页错误的跟踪更复杂一些,因此在离线时重新分配预留的父级要困难得多。