Multiple memory pools

In some systems, it is useful to keep separate sections as dynamic memory heaps, each dedicated to a specific function in the system. Heap allocation mechanisms using separate pools may be implemented for different reasons, such as ensuring that specific modules or subsystems do not use more memory than the amount that is assigned to them at compile time, or ensuring that allocations with the same size can reuse the same physical space in memory, reducing the impact of fragmentation, or even assigning a pre-defined, fixed area in memory for DMA operations with peripherals or network devices. It is possible to delimit the sections for the different pools as usual, by exporting symbols in the linker script. The example below pre-allocates the space in memory for two pools, of 8 KB and 4 KB respectively, located at the end of the .bss section in RAM:

PROVIDE(_start_pool0 = _end_bss);
PROVIDE(_end_pool0 = _start_pool0 + 8KB);
PROVIDE(_start_pool1 = _end_pool0);
PROVIDE(_end_pool1 = _start_pool1 + 4KB);

A custom allocation function must be defined, since the malloc interface does not support the selector of the pool, but the functions can be made generic for both pools. A global structure can be populated with the values exported by the linker:

struct memory_pool {
void *start;
void *end;
void *cur;
};
static struct memory_pool mem_pool[2] = {
{
.start = &_start_pool0;
.end = &_end_pool0;
},
{
.start = &_start_pool1;
.end = &_end_pool1;
},
};

The function must take an extra argument to specify the pool. Then, the allocation is performed with the same algorithm, only changing the current pointer and the boundaries of the selected pool. In this version, the out-of-memory errors are detected before moving the current heap value forward, returning NULL to notify the caller:

void *mempool_alloc(int pool, unsigned int size)
{
struct malloc_block *blk;
struct memory_pool *mp;
char *ret = NULL;
if (pool != 0 && pool != 1)
return NULL;
mp = mem_pool[pool];
if (!mp->cur)
mp->cur = mp->start;
if (((size >>2) << 2) != size)
size = ((size >> 2) + 1) << 2;
blk = (struct malloc_block *)mp->start;
while (blk < mp->cur) {
if ((blk->signature == SIGNATURE_FREED) &&
(blk->size <= size)) {
blk->signature = SIGNATURE_IN_USE;
ret = ((char *)blk) + sizeof(struct malloc_block);
return ret;
}
blk = ((char *)blk) + sizeof(struct malloc_block) + blk->size;
}
blk = (struct malloc_block *)mp->cur;
if (mp->cur + size >= mp->end)
return NULL;
blk->signature = SIGNATURE_IN_USE;
blk->size = size;
ret = ((char *)mp->cur) + sizeof(struct malloc_block);
mp->cur = ret + size;
return ret;
}

Once again, this mechanism does not account for memory fragmentation, so the mempool_free function can have the same implementation as the free for the simplified malloc, as the only necessary thing to do is to mark the blocks being freed as unused.

In more complete cases, where free or a separate garbage collector routine takes care of merging contiguous freed blocks, it might be required to keep track of the freed blocks in each pool, in a list or another data structure that can be visited to check if merging is possible.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.249.105