wake-up-neo.com

Was macht posix_memalign/memalign?

Ich versuche zu verstehen, was die 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 wird es verwendet? Oder vielleicht ein Anwendungsbeispiel?

Ich versuche zu verstehen, wie Linux-Speicher funktioniert. Ich muss meinen eigenen einfachen Speicherpool schreiben (Low-Fragmentation-Heap).

42
twoface88

Während malloc Ihnen einen Speicherplatz bietet, der eine beliebige Ausrichtung haben könnte (die einzige Voraussetzung ist, dass er für den größten primitiven Typ, der von der Implementierung unterstützt wird, abgeglichen werden muss), gibt posix_memalign Ihnen einen Speicherplatz, der garantiert die angeforderte Ausrichtung aufweist.

Das Ergebnis von z.B. posix_memalign(&p, 32, 128) ist ein 128-Byte-Speicherblock, 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.

55

malloc gibt immer Speicher zurück, der auf die maximale Ausrichtung eingestellt ist, die für einen der Grundtypen erforderlich ist. Dadurch kann malloc'd Speicher jeden beliebigen Typ speichern. Ich verstehe die Beschreibung von posix_memalign so, dass sie einen Speicherort zurückgibt, dessen Adresse ein Vielfaches dessen ist, was Sie als Ausrichtung angeben.

Ich bin nicht sicher, wie nützlich dies beim Schreiben eines benutzerdefinierten Speicherpools sein könnte, aber ich habe ein Beispiel dafür gegeben, wie dies implementiert werden könnte. Der Unterschied besteht in meinem Beispiel: Alles, was mit malloc_aligned zugewiesen wurde, muss mit free_aligned freigegeben werden. Mit posix_memalign können Sie jedoch free verwenden.

#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;
}
10
Node

Neben Oli's Antwort möchte ich Sie auf ein noch wichtigeres Thema hinweisen.

Bei aktuellen x86-Architekturen beträgt eine Cachezeile, 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 verfügen über ein großes Array. Wenn Sie nach einem Element suchen, muss die CPU 2 Speicheranforderungen ausgeben (es können 2 Anforderungen ausgegeben werden, auch wenn sie sich in der Mitte der Cache-Zeile befinden). 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, sondern Sie sollten Ihre Struktur auf 64-Byte-Grenzen auffüllen oder komprimieren. 

40 Bytes struct ist einfach Pech :)

8
tothphu

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

3
MByD

Da memalign veraltet ist (ref: man page), wird hier nur der Unterschied zwischen malloc () und posix_memalign () beschrieben. malloc () ist 8-Byte-ausgerichtet (z. B. für RHEL 32-Bit), für posix_memalign () ist die Ausrichtung jedoch benutzerdefinierbar. Um zu wissen, wie man das verwendet, ist es vielleicht ein gutes Beispiel, das Speicherattribut mit mprotect () zu setzen. Um mprotect () zu verwenden, muss der Speicherzeiger auf PAGE ausgerichtet sein. Wenn Sie also posix_memalign () mit pagesize als Ausrichtung aufrufen, kann der zurückgegebene Zeiger problemlos an mprotect () übergeben werden, um die Read-Write-Executable-Attribute festzulegen. (Nachdem Sie die Daten beispielsweise in den Speicherzeiger kopiert haben, können Sie sie als schreibgeschütztes Attribut festlegen, um sie vor Änderungen zu schützen.) "malloc ()" 's zurückgegebener Zeiger kann hier nicht verwendet werden.

3
Peter Teoh

Wenn Sie posix_memalign in GNU C verwenden, sollten Sie darauf achten, dass der zweite Parameter nicht nur eine Zweierpotenz ist, sondern auch ein Vielfaches von sizeof (void *) ist. Beachten Sie, dass sich diese Anforderung von der in der Funktion memalign unterscheidet, die nur zwei Potenzen 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 die erste if-Bedingung in der posix_memalign-Implementierung betrachten, wird geprüft, ob die Ausrichtung ein Vielfaches von sizeof (void *) ist. 

0
Jaehyuk Lee

Erleichtert die Verwendung von THP im virtuellen Subsystem von Linux: https://youtu.be/fgC6RUlkQE4?t=4930

0
htmldrum