Was macht posix_memalign/memalign?

Lesezeit: 6 Minuten

Benutzeravatar von artyomboyko
Artjomboyko

Ich versuche zu verstehen, welche Funktionen memalign() und posix_memalign() tun. Das Lesen der verfügbaren Dokumentation hat nicht geholfen.

Kann mir jemand helfen zu verstehen, wie es funktioniert und wofür es verwendet wird? Oder vielleicht ein Anwendungsbeispiel?

Ich versuche zu verstehen, wie Linux-Speicher funktioniert, ich muss meinen eigenen einfachen Speicherpool (Heap mit geringer Fragmentierung) schreiben.

Benutzeravatar von Oliver Charlesworth
Oliver Charlesworth

Wohingegen malloc gibt Ihnen einen Teil des Speichers, der eine beliebige Ausrichtung haben könnte (die einzige Anforderung ist, dass er für den größten primitiven Typ ausgerichtet sein muss, den die Implementierung unterstützt), posix_memalign gibt Ihnen einen Teil des Speichers, der garantiert die angeforderte Ausrichtung hat.

Also das Ergebnis von zB posix_memalign(&p, 32, 128) wird ein 128-Byte-Speicherstück sein, dessen Startadresse garantiert ein Vielfaches von 32 ist.

Dies ist nützlich für verschiedene Low-Level-Operationen (z. B. die Verwendung von SSE-Anweisungen oder DMA), die Speicher erfordern, der einer bestimmten Ausrichtung folgt.

  • Das Ergebnis von malloc hat nicht nur “irgendeine” Ausrichtung. Es ist garantiert für jeden nativen Typ auf dem System geeignet ausgerichtet (int, long, double, structs usw.). Sie haben Recht, dass größere Ausrichtungen für spezielle Zwecke verwendet werden können, aber für die überwiegende Mehrheit der Anwendungen ist das Ergebnis von malloc sicherlich gut ausgerichtet.

    – Johannes Zwinck

    3. Juli 2011 um 13:52 Uhr

  • Gibt es einen guten Grund zu bevorzugen posix_memalign Über mmapdie ausgerichteten Speicher zuweist?

    – Eli Bendersky

    5. November 2013 um 13:52 Uhr

  • @EliBendersky posix_memalign wird normalerweise einen Block vom Heap zuweisen, wohingegen mmap wird immer zum Betriebssystem gehen. Wenn Sie beispielsweise viele Cache-Line-ausgerichtete Speicherblöcke zuweisen möchten, dann posix_memalign ist sehr bevorzugt. Es ist der gleiche Grund zu bevorzugen malloc Über mmap.

    – jleahy

    6. November 2013 um 17:13 Uhr

Benutzeravatar des Knotens
Knoten

malloc gibt immer Speicher zurück, der auf die maximale Ausrichtung eingestellt ist, die von einem der primitiven Typen benötigt wird. Dies erlaubt malloc‘d Speicher, um jeden Typ zu speichern, den Sie benötigen. Mein Verständnis der Beschreibung von posix_memalignist, dass es einen Speicherort zurückgibt, dessen Adresse ein Vielfaches dessen ist, was Sie als Ausrichtung angeben.

Ich bin mir nicht sicher, wie nützlich dies beim Schreiben eines benutzerdefinierten Speicherpools wäre, aber ich habe versucht, ein Beispiel dafür bereitzustellen, wie dies implementiert werden könnte. Der Unterschied ist bei meinem Beispiel, irgendetwas mit zugewiesen malloc_aligned mit befreit werden muss free_aligned; jedoch mit posix_memalign Sie können verwenden free.

#include <stdlib.h>
#include <stdio.h>

void *malloc_aligned(size_t alignment, size_t bytes)
{
    // we need to allocate enough storage for the requested bytes, some 
    // book-keeping (to store the location returned by malloc) and some extra
    // padding to allow us to find an aligned byte.  im not entirely sure if 
    // 2 * alignment is enough here, its just a guess.
    const size_t total_size = bytes + (2 * alignment) + sizeof(size_t);

    // use malloc to allocate the memory.
    char *data = malloc(sizeof(char) * total_size);

    if (data)
    {
        // store the original start of the malloc'd data.
        const void * const data_start = data;

        // dedicate enough space to the book-keeping.
        data += sizeof(size_t);

        // find a memory location with correct alignment.  the alignment minus 
        // the remainder of this mod operation is how many bytes forward we need 
        // to move to find an aligned byte.
        const size_t offset = alignment - (((size_t)data) % alignment);

        // set data to the aligned memory.
        data += offset;

        // write the book-keeping.
        size_t *book_keeping = (size_t*)(data - sizeof(size_t));
        *book_keeping = (size_t)data_start;
    }

    return data;
}

void free_aligned(void *raw_data)
{
    if (raw_data)
    {
        char *data = raw_data;

        // we have to assume this memory was allocated with malloc_aligned.  
        // this means the sizeof(size_t) bytes before data are the book-keeping 
        // which points to the location we need to pass to free.
        data -= sizeof(size_t);

        // set data to the location stored in book-keeping.
        data = (char*)(*((size_t*)data));

        // free the memory.
        free(data);
    }
}

int main()
{
    char *ptr = malloc_aligned(7, 100);

    printf("is 5 byte aligned = %s\n", (((size_t)ptr) % 5) ? "no" : "yes");
    printf("is 7 byte aligned = %s\n", (((size_t)ptr) % 7) ? "no" : "yes");

    free_aligned(ptr);

    return 0;
}

  • Danke dafür! Sehr nützliches Stück Code als Ersatz für memalign, das irgendwie immer noch in mingw fehlt.

    – Bart

    7. Februar 2012 um 16:12 Uhr

Benutzeravatar von tothphu
tothphu

Zusätzlich zu Olis Antwort möchte ich Sie auf einen noch wichtigeren Punkt hinweisen.

Bei neueren x86-Architekturen beträgt eine Cache-Zeile, die die kleinste Datenmenge ist, die vom Speicher in den Cache abgerufen werden kann, 64 Byte. Angenommen, Ihre Strukturgröße beträgt 56 Bytes, Sie haben ein großes Array davon. Wenn Sie ein Element nachschlagen, muss die CPU 2 Speicheranforderungen ausgeben (sie kann 2 Anforderungen ausgeben, selbst wenn sie sich in der Mitte der Cacheline befindet). Das ist schlecht für die Leistung, da Sie auf Speicher warten müssen und mehr Cache verwenden, was letztendlich zu einem höheren Cache-Miss-Verhältnis führt. In diesem Fall reicht es nicht aus, nur posix_memalign zu verwenden, aber Sie sollten Ihre Struktur so auffüllen oder komprimieren, dass sie auf 64-Byte-Grenzen liegt.

Eine 40-Byte-Struktur zu haben, ist einfach Pech 🙂

Wie es funktioniert, hängt von der Implementierung ab. Der Zweck der Funktion besteht darin, Ihnen einen n-Byte-ausgerichteten Speicherblock zu geben (die Startadresse des Blocks ist ein Vielfaches von n).

Da memalign veraltet ist (siehe Manpage), wird hier nur der Unterschied zwischen malloc() und posix_memalign() beschrieben. malloc() ist 8-Byte ausgerichtet (z. B. für RHEL 32-Bit), aber für posix_memalign() ist die Ausrichtung benutzerdefinierbar. Um die Verwendung zu kennen, ist vielleicht ein gutes Beispiel das Setzen von Speicherattributen mit mprotect(). Um mprotect() verwenden zu können, muss der Speicherzeiger PAGE-ausgerichtet sein. Wenn Sie also posix_memalign() mit pagesize als Ausrichtung aufrufen, kann der zurückgegebene Zeiger einfach an mprotect() übergeben werden, um die read-write-executable-Attribute festzulegen. (z. B. nachdem Sie die Daten in den Speicherzeiger kopiert haben, können Sie ihn auf ein schreibgeschütztes Attribut setzen, um ihn vor Änderungen zu schützen). Der zurückgegebene Zeiger von “malloc()” kann hier nicht verwendet werden.

Benutzeravatar von ruach
Ruach

Wenn Sie verwenden posix_memalign in GNU-C, sollten Sie darauf achten, dass der zweite Parameter nicht nur eine Zweierpotenz, sondern auch ein Vielfaches von sizeof (void*) sein sollte. Beachten Sie, dass sich diese Anforderung von der in der Memalign-Funktion unterscheidet, die nur eine Zweierpotenz erfordert.

int
__posix_memalign (void **memptr, size_t alignment, size_t size)
{
  void *mem;

  /* Test whether the SIZE argument is valid.  It must be a power of
     two multiple of sizeof (void *).  */
  if (alignment % sizeof (void *) != 0
      || !powerof2 (alignment / sizeof (void *))
      || alignment == 0)
    return EINVAL;


  void *address = RETURN_ADDRESS (0);
  mem = _mid_memalign (alignment, size, address);

  if (mem != NULL)
    {
      *memptr = mem;
      return 0;
    }

  return ENOMEM;
}
weak_alias (__posix_memalign, posix_memalign) 

Wenn wir uns die erste if-Bedingung in der posix_memalign-Implementierung ansehen, prüft sie, ob die Ausrichtung ein Vielfaches von sizeof(void*) ist.

1412950cookie-checkWas macht posix_memalign/memalign?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy