Riesenseiten auf Raspberry Pi 4

Nov 19 2020

Ich brauche Hilfe bei der Verwaltung von Hugepages auf Himbeer-Pi 4 mit Himbeer-Pi OS 64-Bit.
Ich habe online nicht viele verlässliche Informationen gefunden.
Zuerst habe ich die Memory Management options --->Transparent Hugepage SupportOption zur Aktivierung der Kernelquelle neu kompiliert . Wenn ich den Befehl ausführe:

grep -i huge /proc/meminfo

Die Ausgabe ist:

AnonHugePages:    319488 kB
ShmemHugePages:        0 kB
FileHugePages:         0 k

und Ausführen des Befehls:

cat /sys/kernel/mm/transparent_hugepage/enabled

Die Ausgabe ist:

[always] madvise never

Daher denke ich, dass transparente riesige Seiten (AnonHugePages) festgelegt werden sollten. Ich muss HugePages verwenden, um den größten zusammenhängenden Speicherblock mithilfe der mmap-Funktion, c-Code, zuzuordnen.

mem = mmap(NULL,buf_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);

Anschauen https://www.man7.org/linux/man-pages/man2/mmap.2.html Es gibt zwei Flags zum Verwalten der riesigen Seiten: MAP_HUGETLB-Flag und MAP_HUGE_2MB, MAP_HUGE_1GB-Flag.

Meine Frage ist: Um HugePages zu verwenden, sollte ich auf diese Weise zuordnen?

mem = mmap(NULL,buf_size,PROT_READ|PROT_WRITE,MAP_SHARED,MAP_HUGETLB,fd,0);

Kernelkonfiguration:

CONFIG_SYS_SUPPORTS_HUGETLBFS=y
CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y
CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y
CONFIG_HAVE_ARCH_HUGE_VMAP=y
CONFIG_TRANSPARENT_HUGEPAGE=y
CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set
CONFIG_TRANSPARENT_HUGE_PAGECACHE=y
# CONFIG_HUGETLBFS is not set

Vielen Dank.

Antworten

1 RachidK. Nov 19 2020 at 22:06

Riesige Seiten sind eine Möglichkeit, die Leistung der Anwendungen zu verbessern, indem die Anzahl der TLB-Fehler verringert wird. Der Mechanismus fasst zusammenhängende physische Standardseiten (typische Größe von 4 KB) zu einer großen Seite (z. B. 2 MB) zusammen. Linux implementiert diese Funktion in zwei Varianten: Transparente riesige Seiten und explizite große Seiten.

Transparente riesige Seiten

Transparente riesige Seiten (THP) werden vom Kernel transparent verwaltet. Die User Space-Anwendungen haben keine Kontrolle über sie. Der Kernel bemüht sich, große Seiten zuzuweisen, wann immer dies möglich ist, dies ist jedoch nicht garantiert. Darüber hinaus kann THP Overhead einführen, da ein zugrunde liegender "Garbage Collector" -Kernel- Daemon namens khugepaged für das Zusammenführen der physischen Seiten verantwortlich ist, um große Seiten zu erstellen. Dies kann CPU-Zeit in Anspruch nehmen und unerwünschte Auswirkungen auf die Leistung der ausgeführten Anwendungen haben. In Systemen mit zeitkritischen Anwendungen wird generell empfohlen, THP zu deaktivieren.

THP kann in der Boot-Befehlszeile (siehe Ende dieser Antwort) oder in der Shell in sysfs deaktiviert werden :

$ cat /sys/kernel/mm/transparent_hugepage/enabled always [madvise] never $ sudo sh -c "echo never > /sys/kernel/mm/transparent_hugepage/enabled"
$ cat /sys/kernel/mm/transparent_hugepage/enabled
always madvise [never]

NB : Es gibt einige interessante Artikel zur Leistungsbewertung / zu den Themen des THP:

  • Transparente riesige Seiten: Messung der Auswirkungen auf die Leistung ;
  • Den Mythos transparenter riesiger Seiten für Datenbanken regeln .

Explizite riesige Seiten

Wenn die großen Seiten auf Anwendungsebene benötigt werden (dh aus dem Benutzerbereich). Die HUGETLBFS-Kernelkonfiguration muss festgelegt werden, um das Pseudodateisystem hugetlbfs zu aktivieren (das Menü im Kernelkonfigurator lautet ungefähr : "Dateisysteme" -> "Pseudodateisysteme" -> "Unterstützung des HugeTLB-Dateisystems"). Im Kernel- Quellbaum befindet sich dieser Parameter in fs / Kconfig :

config HUGETLBFS
    bool "HugeTLB file system support"
    depends on X86 || IA64 || SPARC64 || (S390 && 64BIT) || \
           SYS_SUPPORTS_HUGETLBFS || BROKEN
    help
      hugetlbfs is a filesystem backing for HugeTLB pages, based on
      ramfs. For architectures that support it, say Y here and read
      <file:Documentation/admin-guide/mm/hugetlbpage.rst> for details.

      If unsure, say N.

Auf einem Ubuntu-System können wir beispielsweise Folgendes überprüfen:

$ cat /boot/config-5.4.0-53-generic | grep HUGETLBFS
CONFIG_HUGETLBFS=y

NB : Auf Raspberry Pi ist es möglich, die Darstellung von /proc/config.gz zu konfigurieren und dasselbe mit zcat zu tun , um den Parameter zu überprüfen. Das Konfigurationsmenü lautet dazu: "Allgemeines Setup" -> "Kernel .config-Unterstützung" + "Zugriff auf .config über /proc/config.gz aktivieren"

Wenn dieser Parameter gesetzt ist, wird das Pseudo-Dateisystem hugetlbfs zum Kernel-Build hinzugefügt (vgl. Fs / Makefile ):

obj-$(CONFIG_HUGETLBFS)     += hugetlbfs/

Der Quellcode von hugetlbfs befindet sich in fs / hugetlbfs / inode.c . Beim Start stellt der Kernel interne hugetlbfs- Dateisysteme bereit, um alle verfügbaren großen Seitengrößen für die Architektur zu unterstützen, auf der er ausgeführt wird:

static int __init init_hugetlbfs_fs(void)
{
    struct vfsmount *mnt;
    struct hstate *h;
    int error;
    int i;

    if (!hugepages_supported()) {
        pr_info("disabling because there are no supported hugepage sizes\n");
        return -ENOTSUPP;
    }

    error = -ENOMEM;
    hugetlbfs_inode_cachep = kmem_cache_create("hugetlbfs_inode_cache",
                    sizeof(struct hugetlbfs_inode_info),
                    0, SLAB_ACCOUNT, init_once);
    if (hugetlbfs_inode_cachep == NULL)
        goto out;

    error = register_filesystem(&hugetlbfs_fs_type);
    if (error)
        goto out_free;

    /* default hstate mount is required */
    mnt = mount_one_hugetlbfs(&hstates[default_hstate_idx]);
    if (IS_ERR(mnt)) {
        error = PTR_ERR(mnt);
        goto out_unreg;
    }
    hugetlbfs_vfsmount[default_hstate_idx] = mnt;

    /* other hstates are optional */
    i = 0;
    for_each_hstate(h) {
        if (i == default_hstate_idx) {
            i++;
            continue;
        }

        mnt = mount_one_hugetlbfs(h);
        if (IS_ERR(mnt))
            hugetlbfs_vfsmount[i] = NULL;
        else
            hugetlbfs_vfsmount[i] = mnt;
        i++;
    }

    return 0;

 out_unreg:
    (void)unregister_filesystem(&hugetlbfs_fs_type);
 out_free:
    kmem_cache_destroy(hugetlbfs_inode_cachep);
 out:
    return error;
}

Ein hugetlbfs- Dateisystem ist eine Art RAM-Dateisystem, in das der Kernel Dateien erstellt, um die von den Anwendungen zugeordneten Speicherbereiche zu sichern .

Die Anzahl der benötigten großen Seiten kann reserviert werden, indem die Anzahl der benötigten großen Seiten in / sys / kernel / mm / riesige Seiten / große Seiten - große Seitengröße / nr_hugepages geschrieben wird .

Dann kann mmap () einen Teil des Anwendungsadressraums auf große Seiten abbilden. Hier ist ein Beispiel, das zeigt, wie es geht:

#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>

#define HP_SIZE  (2 * 1024 * 1024) // <-- Adjust with size of the supported HP size on your system

int main(void)
{
  char *addr, *addr1;

  // Map a Huge page
  addr = mmap(NULL, HP_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED| MAP_HUGETLB, -1, 0);
  if (addr == MAP_FAILED) {
    perror("mmap()");
    return 1;
  }

  printf("Mapping located at address: %p\n", addr);

  pause();

  return 0;
}

Im vorhergehenden Programm basiert der Speicher, auf den addr zeigt, auf riesigen Seiten. Anwendungsbeispiel:

$ gcc alloc_hp.c -o alloc_hp
$ ./alloc_hp mmap(): Cannot allocate memory $ cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
0
$ sudo sh -c "echo 1 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages" $  cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
1
$ ./alloc_hp 
Mapping located at address: 0x7f7ef6c00000

In einem anderen Terminal kann die Prozesszuordnung beobachtet werden, um die Größe der Speicherseite zu überprüfen (sie wird beim Systemaufruf pause () blockiert ):

$ pidof alloc_hp
13009
$ cat /proc/13009/smaps
[...]
7f7ef6c00000-7f7ef6e00000 rw-s 00000000 00:0f 331939     /anon_hugepage (deleted)
Size:               2048 kB
KernelPageSize:     2048 kB   <----- The page size is 2MB
MMUPageSize:        2048 kB
[...]

In der vorhergehenden Zuordnung wird der Dateiname / anon_hugepage für den großen Seitenbereich intern vom Kernel erstellt. Es wird als gelöscht markiert, da der Kernel die zugehörige Speicherdatei entfernt, wodurch die Datei verschwindet, sobald keine Referenzen mehr darauf vorhanden sind (z. B. wenn der aufrufende Prozess endet, wird die zugrunde liegende Datei beim Beenden geschlossen () , der Referenzzähler aktiviert Die Datei fällt auf 0 und der Entfernungsvorgang wird beendet, damit sie verschwindet.

Zuordnung anderer großer Seitengrößen

Auf dem Raspberry Pi 4B beträgt die Standardgröße für große Seiten 2 MB, die Karte unterstützt jedoch mehrere andere große Seitengrößen:

$ ls -l /sys/kernel/mm/hugepages
total 0
drwxr-xr-x 2 root root 0 Nov 23 14:58 hugepages-1048576kB
drwxr-xr-x 2 root root 0 Nov 23 14:58 hugepages-2048kB
drwxr-xr-x 2 root root 0 Nov 23 14:58 hugepages-32768kB
drwxr-xr-x 2 root root 0 Nov 23 14:58 hugepages-64kB

Um sie zu verwenden, muss ein Dateisystem vom Typ hugetlbfs bereitgestellt werden, das der Größe der gewünschten großen Seite entspricht. Die Kerneldokumentation enthält Details zu den verfügbaren Mount-Optionen. Um beispielsweise ein hugetlbfs- Dateisystem auf / mnt / vast mit 8 riesigen Seiten der Größe 64 KB zu mounten, lautet der Befehl:

mount -t hugetlbfs -o pagesize=64K,size=512K,min_size=512K none /mnt/huge

Dann ist es möglich, große Seiten von 64 KB in einem Benutzerprogramm zuzuordnen. Das folgende Programm erstellt das Verzeichnis / tmp / hpfs, in dem ein hugetlbfs- Dateisystem mit einer Größe von 4 großen Seiten von 64 KB bereitgestellt wird . Eine Datei mit dem Namen / memfile_01 wird erstellt und auf die Größe von 2 großen Seiten erweitert. Die Datei wird dank des Systemaufrufs mmap () in den Speicher abgebildet . Das MAP_HUGETLB- Flag wird nicht übergeben, da der angegebene Dateideskriptor für eine Datei gilt, die in einem hugetlbfs- Dateisystem erstellt wurde. Anschließend ruft das Programm pause () auf, um die Ausführung anzuhalten und einige Beobachtungen in einem anderen Terminal vorzunehmen:

#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <fcntl.h>


#define ERR(fmt, ...) do {                            \
    fprintf(stderr,                                   \
            "ERROR@%s#%d: "fmt,                       \
             __FUNCTION__, __LINE__, ## __VA_ARGS__); \
                         } while(0)


#define HP_SIZE   (64 * 1024)
#define HPFS_DIR  "/tmp/hpfs"
#define HPFS_SIZE (4 * HP_SIZE)


int main(void)
{
void *addr;
char  cmd[256];
int   status;
int   rc;
char  mount_opts[256];
int   fd;

  rc = mkdir(HPFS_DIR, 0777);
  if (0 != rc && EEXIST != errno) {
    ERR("mkdir(): %m (%d)\n", errno);
    return 1;
  }

  snprintf(mount_opts, sizeof(mount_opts), "pagesize=%d,size=%d,min_size=%d", HP_SIZE, 2*HP_SIZE, HP_SIZE);

  rc = mount("none", HPFS_DIR, "hugetlbfs", 0, mount_opts);
  if (0 != rc) {
    ERR("mount(): %m (%d)\n", errno);
    return 1;
  }

  fd = open(HPFS_DIR"/memfile_01", O_RDWR|O_CREAT, 0777);
  if (fd < 0) {
    ERR("open(%s): %m (%d)\n", "memfile_01", errno);
    return 1;
  }

  rc = ftruncate(fd, 2 * HP_SIZE);
  if (0 != rc) {
    ERR("ftruncate(): %m (%d)\n", errno);
    return 1;
  }

  addr = mmap(NULL, 2 * HP_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
  if (MAP_FAILED == addr) {
    ERR("mmap(): %m (%d)\n", errno);
    return 1;
  }

  // The file can be closed
  rc = close(fd);
  if (0 != rc) {
    ERR("close(%d): %m (%d)\n", fd, errno);
    return 1;
  }

  pause();

  return 0;

} // main

Das vorhergehende Programm muss als root ausgeführt werden, da es mount () aufruft :

$ gcc mount_tlbfs.c -o mount_tlbfs $ cat /sys/kernel/mm/hugepages/hugepages-64kB/nr_hugepages 
0
$ sudo sh -c "echo 8 > /sys/kernel/mm/hugepages/hugepages-64kB/nr_hugepages" $ cat /sys/kernel/mm/hugepages/hugepages-64kB/nr_hugepages 
8
$ sudo ./mount_tlbfs 

In einem anderen Terminal kann die Datei / proc / [pid] / smaps angezeigt werden, um die große Seitenzuordnung zu überprüfen. Sobald das Programm in die großen Seiten schreibt, löst der Lazy-Zuweisungsmechanismus die effektive Zuordnung der großen Seiten aus.

Vgl. Dieser Artikel für zukünftige Details

Frühzeitige Reservierung

Die riesigen Seiten werden mit aufeinanderfolgenden physischen Speicherseiten erstellt. Die Reservierung sollte früh im Systemstart erfolgen (insbesondere auf stark ausgelasteten Systemen), da der physische Speicher möglicherweise so fragmentiert ist, dass es manchmal unmöglich ist, danach große Seiten zuzuweisen. Um so früh wie möglich zu reservieren, kann dies über die Kernel- Boot-Befehlszeile erfolgen :

hugepages=  
       [HW] Number of HugeTLB pages to allocate at boot.
       If this follows hugepagesz (below), it specifies
       the number of pages of hugepagesz to be allocated.
       If this is the first HugeTLB parameter on the command
       line, it specifies the number of pages to allocate for
       the default huge page size.  See also
       Documentation/admin-guide/mm/hugetlbpage.rst.
       Format: <integer>

hugepagesz=
        [HW] The size of the HugeTLB pages.  This is used in
        conjunction with hugepages (above) to allocate huge
        pages of a specific size at boot.  The pair
        hugepagesz=X hugepages=Y can be specified once for
        each supported huge page size. Huge page sizes are
        architecture dependent.  See also
        Documentation/admin-guide/mm/hugetlbpage.rst.
        Format: size[KMG]

transparent_hugepage=
        [KNL]
        Format: [always|madvise|never]
        Can be used to control the default behavior of the system
        with respect to transparent hugepages.
        See Documentation/admin-guide/mm/transhuge.rst
        for more details.

Auf Raspberry Pi kann die Startbefehlszeile normalerweise in /boot/cmdline.txt aktualisiert werden, und die aktuelle Startbefehlszeile , die vom laufenden Kernel verwendet wird, wird in / proc / cmdline angezeigt .

NB :

  • Dieses Rezept wird hier und hier näher erläutert
  • Es gibt eine User-Space-Bibliothek namens libhugetlbfs, die eine Abstraktionsebene über den hier beschriebenen Hugetlbfs- Mechanismus des Kernels bietet . Es kommt mit Bibliotheksdiensten wie get_huge_pages () und zugehörigen Tools wie vastctl . Das Ziel dieses User Space Service ist es, die Heap- und Text + Datensegmente von STATISCH verknüpften ausführbaren Dateien in große Seiten abzubilden (die Zuordnung von dynamisch verknüpften Programmen wird nicht unterstützt). All dies hängt von den in dieser Antwort beschriebenen Kernelfunktionen ab.