Исправлен менеджер памяти в C

Я решил не использовать кучу в своей программе и создать собственный диспетчер памяти для извлечения фрагментов памяти из большого глобального массива uint8_t (u8). Я выбрал u8, так как 8 бит — это байт. Очень большой размер MEMORY_BLOCK_SIZE не является проблемой, поскольку в сборках Release компилятор удаляет неиспользуемые байты. Я отслеживаю свободные участки памяти с помощью того, что я называю «поиском».

#include <assert.h>

// NOTE: null is a redefinition of NULL
// NOTE: u8 = uint8_t
// NOTE: u64 = uint64_t

#define MEMORY_BLOCK_SIZE 8388608
#define MEMORY_LOOKUPS 8

static u8 memoryBlock[ MEMORY_BLOCK_SIZE ];
static u8* memoryLookups[ MEMORY_LOOKUPS ];
static u64 memoryLookupsSizes[ MEMORY_LOOKUPS ];

void memoryInit( void )
{
     memoryLookups[ 0 ] = memoryBlock;
     memoryLookupsSizes[ 0 ] = MEMORY_BLOCK_SIZE;
}

u8* requestMemoryChunk( u64 size )
{
     assert( size );

     u8* usablePtr = null;
     u64 ptrPosition;

     for( u64 i = MEMORY_LOOKUPS; i >= 0; --i )
     {
          if( ( !( memoryLookups[ i ] ) ) &&
                ( memoryLookupsSizes[ i ] >= size ) )
          {
                usablePtr = memoryLookups[ i ];
                ptrPosition = i;
                break;
          }
     }

     assert( usablePtr );

     u8* block = usablePtr;
     
     memoryLookupsSizes[ ptrPosition ] -= size;

     if( !( memoryLookupsSizes[ ptrPosition ] ) )
          memoryLookups[ ptrPosition ] = null;
     else 
          memoryLookups[ ptrPosition ] = usablePtr + size;

     return block;
}

void freeMemoryChunk( u64 size, u8* block )
{
     assert( block && size );

     for( u64 i = 0; i < MEMORY_LOOKUPS; ++i )
     {
          if( ! ( memoryLookups[ i ] ) )
          {
                memoryLookups[ i ] = block;
                memoryLookupsSizes[ i ] = size;
                
                break;
          }
     }
}
``` 

2 ответа
2

Пожалуйста, не переименовывайте NULL, uint8_t и uint64_t. Это делает ваш код очень трудным для чтения. Если вы действительно не хотите вводить все это, выполните поиск и замену после того, как закончите кодирование.

Хорошо известно, что проверка if (! X) такая же, как if (x == NULL) и if (x == 0). Но в конечном итоге я предпочитаю явные версии для удобства чтения.

Также

for(int x=0; x< MEMORY_LOOKUPS; x++) {
    uint8_t* x = requestMemoryChunk(MEMORY_BLOCK_SIZE - 1 - x);
    freeMemoryChunk(x, MEMORY_BLOCK_SIZE - 1 - x);
}
uint8_t* y = requestMemoryChunk(2);
// you just lost 99.99% of your memory

Если этот пример слишком экстремальный, рассмотрите

for(int x=0; x< MEMORY_LOOKUPS; x++) {
    uint8_t* x = requestMemoryChunk(10000 - x);
    freeMemoryChunk(x, 10000 - x);
}
uint8_t* y = requestMemoryChunk(2);
// you just lost ~10000 bytes of memory

Рассмотрите возможность привязки запросов к размерам блоков (например, степени двойки), чтобы избежать подобных патологических запросов. Далее рассмотрите возможность сортировки поисковых запросов по размеру.

Стилистически все в порядке. В условиях if есть ненужные круглые скобки, но (… вы знаете, есть лисп)

В конечном итоге люди пишут на c для эффективности и контроля. Кто-то ожидает потерять читабельность, делая умные вещи. Но нет причин не объяснять умные вещи в комментариях.

  • Спасибо за обзор, я согласен со всеми пунктами, которые вы упомянули. Я думал, что u64 / u8 / null нетрудно читать, так как это довольно распространенная практика программирования на переменном языке. Чтобы решить проблему нерационального использования памяти, вам следует изменить MEMORY_LOOKUPS в соответствии с потребностями вашей программы. (то же, что и MEMORY_BLOCK_SIZE). Я стараюсь делать это как можно меньше, потому что чем больше поисков программа проверяет, тем больше итераций она должна сделать.

    — рано


  • Вы не можете реализовать такую ​​систему диспетчера памяти в стандарте C. Он должен полагаться на стандартные расширения и определенные параметры компилятора. Основная причина этого — «строгое правило алиасинга» … (Что такое строгое правило псевдонима?)

  • … но также и выравнивание, вам не следует писать библиотеку, которая распределяет неверно выровненные фрагменты памяти. Если вы посмотрите на malloc и друзей, они работают только потому, что у них есть требование (из стандарта C 7.22.3):

    Указатель, возвращаемый в случае успешного выделения, выравнивается соответствующим образом, чтобы его можно было назначить указателю на любой тип объекта с фундаментальным требованием выравнивания, а затем использовать для доступа к такому объекту или массиву таких объектов в выделенном пространстве (до пространство явно освобождено).

  • Как отмечалось в другом обзоре, вам не следует придумывать самодельные секретные псевдонимы для стандартных терминов. Использовать stdint.h с участием uint8_t и т. д. Использование NULL. В частности, не переопределяйте NULL к чему-то нестандартному, поскольку это может вызвать всевозможные тонкие ошибки. Видеть В чем разница между нулевыми указателями и NULL? для подробностей.

  • Ошибка здесь: for( u64 i = MEMORY_LOOKUPS; i >= 0; --i ) An u64 всегда> = 0. Приличные компиляторы должны вас тут предупредить. В общем, подобных ошибок можно избежать, всегда выполняя итерацию от 0 и выше, а затем вместо этого меняя индексацию: arr[MAX - i] и т.п.

  • Как правило, код качества библиотеки не проверяет, являются ли переданные параметры NULL, 0 и т. Д. Такие вещи обрабатываются документацией, и вызывающая сторона несет ответственность за то, чтобы не передавать параметры мусора.

  • Ваши подпрограммы должны отслеживать последнее использованное место в памяти, а не искать его во время выполнения. Я полагаю, что некоторые из них могут быть освобождены, поэтому в справочной таблице будут пробелы, но тогда вы должны спросить себя, действительно ли массив является правильным классом контейнера для использования. Вы можете, например, использовать вместо этого BST, отсортированный по адресу памяти.

  • Ваш код, похоже, вообще не решает проблему фрагментации.

  • Очень большой размер MEMORY_BLOCK_SIZE не является проблемой, поскольку в сборках Release компилятор удаляет неиспользуемые байты.

    Я бы не был так уверен в этом, это во многом зависит от системы и контекста.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *