Подтвердить что ты не робот

Что делает posix_memalign/memalign

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

malloc всегда возвращает память, для которой задано максимальное выравнивание, требуемое для любого из примитивных типов. Это позволяет хранить malloc 'd память любого типа, который вам может понадобиться. Мое понимание описания 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;
}

Ответ 3

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

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

Когда вы используете 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 *).

Ответ 7

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

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