Что делает posix_memalign / memalign - PullRequest
47 голосов
/ 03 июля 2011

Я пытаюсь понять, что делают функции memalign() и posix_memalign().Чтение доступной документации не помогло.

Может кто-нибудь помочь мне понять, как это работает и для чего оно используется?Или, может быть, привести пример использования?

Я пытаюсь понять, как работает память Linux, мне нужно написать свой собственный простой пул памяти (куча с низкой фрагментацией).

Ответы [ 8 ]

58 голосов
/ 03 июля 2011

Принимая во внимание, что malloc дает вам кусок памяти, который может иметь любое выравнивание (единственное требование - это выравнивание для наибольшего типа примитива, поддерживаемого реализацией), posix_memalign дает вам кусок памяти, который гарантированно имеет запрошенное выравнивание.

Итак, результат, например, posix_memalign(&p, 32, 128) будет 128-байтовым фрагментом памяти, чей начальный адрес гарантированно будет кратен 32.

Это полезно для различных низкоуровневых операций (таких как использование инструкций SSE или DMA), которые требуют памяти, которая подчиняется определенному выравниванию.

10 голосов
/ 03 июля 2011

malloc всегда возвращает память, для которой установлено максимальное выравнивание, требуемое любым из типов примитивов. Это позволяет malloc памяти хранить любой тип, который вам может понадобиться. Насколько я понимаю, описание posix_memalign заключается в том, что он возвращает ячейку памяти, адрес которой будет кратен тому, что вы указали в качестве выравнивания.

Я не уверен, насколько это было бы полезно при записи пользовательского пула памяти, но я попытался привести пример того, как это можно реализовать. Разница в моем примере: все, что выделено с помощью malloc_aligned, должно быть освобождено с помощью free_aligned; однако с posix_memalign вы можете использовать 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;
}
8 голосов
/ 26 июня 2012

В дополнение к ответу Оли я хотел бы указать на еще более важный вопрос.

В современных архитектурах x86 строка кэша, представляющая собой наименьший объем данных, которые можно извлечь из памяти в кэш, составляет 64 байта. Предположим, что размер вашей структуры составляет 56 байтов, у вас есть большой массив из них. Когда вы просматриваете один элемент, ЦП должен будет выполнить 2 запроса памяти (он может выдать 2 запроса, даже если он находится в середине строки кэша). Это плохо сказывается на производительности, так как вам приходится ждать памяти и использовать больше кеша, что в конечном итоге дает более высокий коэффициент пропуска кеша. В этом случае недостаточно просто использовать posix_memalign, но вы должны дополнить или сжать структуру, чтобы она находилась на границе 64 байт.

Наличие 40-байтовой структуры - просто неудача:)

4 голосов
/ 03 июля 2011

Как это работает, зависит от реализации. Цель этой функции - дать выровненный блок памяти размером n байтов (начальный адрес блока кратен n).

3 голосов
/ 02 января 2015

Поскольку memalign устарел (ref: man page), здесь будет описана только разница между malloc () и posix_memalign (). malloc () выравнивается по 8 байтов (например, для 32-битного RHEL), но для posix_memalign () выравнивание определяется пользователем. Чтобы знать, как это использовать, возможно, одним хорошим примером является установка атрибута памяти с помощью mprotect (). Чтобы использовать mprotect (), указатель памяти должен быть выровнен по PAGE. И поэтому, если вы вызываете posix_memalign () с размером страницы в качестве выравнивания, то возвращаемый указатель может легко передать в mprotect () для установки атрибутов read-write-исполняемый. (например, после того, как вы скопируете данные в указатель памяти, вы можете установить для них атрибут только для чтения, чтобы защитить его от изменения). Возвращенный указатель "malloc ()" здесь нельзя использовать.

0 голосов
/ 27 февраля 2019

Облегчает использование THP в подсистеме виртуальной памяти Linux: https://youtu.be/fgC6RUlkQE4?t=4930

0 голосов
/ 15 января 2019

Когда вы используете posix_memalign в GNU C , вы должны быть осторожны, что второй параметр должен быть не только степенью двойки, но и кратным sizeof (void *) , Обратите внимание, что это требование отличается от требования в функции memalign, для которой требуется только степень двойки.

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) 

Когда мы смотрим на первое условие if в реализации posix_memalign, оно проверяет, является ли выравнивание кратным sizeof (void *).

0 голосов
/ 02 августа 2012

По умолчанию указатель возврата malloc кратен 8, это означает, что разделенная память malloc на чанки имеет 8 байтов и проверяет свободную память в начале каждого чанка. Есть 2 проблемы.

Большая порция тратит больше памяти, но большая порция помогает C быстрее находить свободную память порции. memalign может изменить размер этих блоков. Если вы хотите сэкономить память, уменьшите размер чанка до 2 или 4. Если вы хотите ускорить работу приложения, увеличьте размер чанка до 2.

...