OSLIB Memory Allocators

Both RT and NIL kernels are fully static, no memory is allocated or freed at runtime, all objects positions are defined at link time. While dynamic allocation is not required OSLIB supports 3 different allocators in case the application requires reliable and thread-aware memory management.

Global Settings

CH_CFG_USE_MEMCORE This switch enables the core allocator module, this module is required by all the other memory management modules.
CH_CFG_MEMCORE_SIZE Size of the memory block to use as system heap. If set to zero then the whole available RAM is used as system heap.
CH_CFG_USE_HEAP This switch enables the heap allocator module.
CH_CFG_USE_MEMPOOLS This switch enables the memory pool allocator module.

Allocator Types

The 4 kinds of allocators are designed to work together:

  • Core Allocator, for one-way memory allocation, memory cannot be freed.
  • Pool Allocator, for allocating and freeing fixed-size objects.
  • Guarded Pool Allocator, it is like the pool allocator but guarded by a semaphore. Trying to allocate an object causes the caller thread to wait if an object is not immediately available.
  • Heap Allocator, for allocating and freeing arbitrary-size memory blocks.

The Core Allocator

This allocator is able to efficiently return memory blocks from a memory area called the system heap. This allocator features:

  • Very fast memory allocation.
  • Memory blocks can be allocated from bottom to top or from top to bottom.
  • It is possible to allocate memory blocks with arbitrary alignment and offsets.
  • Allocated memory blocks cannot be returned to the heap, the working model is strictly one-way.

The System Heap

The System Heap is a contiguous RAM memory area where the Core Allocator operates, it is organized as follow:

 The System Heap

The parameter CH_CFG_MEMCORE_SIZE specifies how much RAM has to be used for the system heap, the system area is created as a plain array and takes space from the compiler BSS section.

If CH_CFG_MEMCORE_SIZE is set to zero then linker is expected to export two symbols:

  • __heap_base__, base address of the system heap.
  • __heap_end__, end address of the system heap.

Notes

Conventionally the bottom-to-top allocation is used as an _sbrk() implementation in order to support the C runtime. The top-to-bottom allocation is used by ChibiOS application and/or to feed blocks to the two other OSLIB allocators (pool allocator, heap allocator).

This is done in order to let the C runtime allocate contiguous blocks, several C library implementations make this assumption.

Alignments and Offsets

When allocating a block it is possible to specify the following parameters:

  • Size is the size of the area starting at the returned pointer address called Main Area.
  • Offset is the size of the area below the returned pointer address called Header Area.
  • Alignment is a power-of-two alignment enforcement for the returned pointer.

The returned block looks as follow:

Memory block

Note that the total allocated size is the sum of Size, Offset and lost space caused by alignment enforcement.

Core Allocator API

chCoreAllocFromBase() Allocates a memory block bottom-to-top.
chCoreAllocFromBaseI() Allocates a memory block bottom-to-top (I-Class variant).
chCoreAllocFromTop() Allocates a memory block top-to-bottom.
chCoreAllocFromTopI() Allocates a memory block top-to-bottom (I-Class variant).
chCoreGetStatusX() Returns the remaining space in the system heap.

The Pool Allocator

This allocator is able to efficiently return memory blocks from a pool of fixed-size objects. This allocator features:

  • Very fast fixes-size objects allocation.
  • Pools can have fixed number of objects or be able to be feed by the Core Allocator when more objects are needed.
  • Allocated objects can be returned to the pool.
  • No fragmentation is possible so constant time allocation is guaranteed.
  • It is possible to define multiple pools for objects with different sizes.

Diagram

A pool is a linked list of objects of fixed size, allocating an object means removing the first object in the list, freeing an objects means inserting it on top of the list, both operations are fixed-time.

 Pool

Note that a freed object is not guaranteed to keep its content intact when it is allocated again because it is overwritten with the linked list pointer. The provider is a function implementing the same prototype of the Core Allocator “alloc” functions. The Core Allocator itself can be, and usually is, used as provider.

Pool Allocator API

memory_pool_t Type of a memory pool object.
MEMORYPOOL_DECL() Memory Pool static initializer.
chPoolObjectInit() Initializes a memory_pool_t object with default alignment restrictions.
chPoolObjectInitAligned() Initializes a memory_pool_t object with specific alignment restrictions.
chPoolLoadArray() Loads an array of objects into a pool.
chPoolAlloc() Allocates an object from the pool.
chPoolAllocI() Allocates an object from the pool (I-Class variant).
chPoolFree() Returns an object into the pool.
chPoolFreeI() Returns an object into the pool (I-Class variant).
chPoolAdd() Synonym of chPoolFree().
chPoolAddI() Synonym of chPoolFreeI().

The Guarded Pool Allocator

This allocator is able to efficiently return memory blocks from a pool of fixed-size objects. It is a variation of the Pool Allocator where the allocation operations are protected with a semaphore. This allocator features:

  • Very fast fixes-size objects allocation.
  • Pools a have fixed number of objects.
  • Allocated objects can be returned to the pool.
  • No fragmentation is possible so constant time allocation is guaranteed.
  • It is possible to define multiple pools for objects with different sizes.

Diagram

A guarded pool is a linked list of objects of fixed size, allocating an object means removing the first object in the list, freeing an objects means inserting it on top of the list, only freeing and object is a fixed-time operation.

The semaphore counter represents the number of objects available in the pool, a thread trying to allocate an object on an empty pool would wait on the semaphore. When an object becomes available, the first waiting thread, if any, is resumed and gets the object.

 Guarded Pool

Note that a freed object is not guaranteed to keep its content intact when it is allocated again because it is overwritten with the linked list pointer.

Guarded Pool Allocator API

guarded_memory_pool_t Type of a guarded memory pool object.
GUARDEDMEMORYPOOL_DECL() Guarded Memory Pool static initializer.
chGuardedPoolObjectInit() Initializes a guarded_memory_pool_t object with default alignment restrictions.
chGuardedPoolObjectInitAligned() Initializes a guarded_memory_pool_t object with specific alignment restrictions.
chGuardedPoolLoadArray() Loads an array of objects into a guarded pool.
chGuardedPoolAlloc() Allocates an object from the guarded pool.
chGuardedPoolAllocI() Allocates an object from the pool (I-Class variant).
chGuardedPoolAllocTimeout() Allocates an object from the guarded pool with timeout.
chGuardedPoolAllocTimeoutS() Allocates an object from the guarded pool with timeout (S-Class variant).
chGuardedPoolFree() Returns an object into the guarded pool.
chGuardedPoolFreeI() Returns an object into the guarded pool (I-Class variant).
chGuardedPoolFreeS() Returns an object into the guarded pool (S-Class variant).
chGuardedPoolAdd() Synonym of chGuardedPoolFree().
chGuardedPoolAddI() Synonym of chGuardedPoolFreeI().
chGuardedPoolAddS() Synonym of chGuardedPoolFreeS().

The Heap Allocator

This allocator implements the traditional malloc() and free() paradigm but with some peculiarities:

  • Multiple heaps are possible, both static and dynamic.
    • Static Heaps have a fixed size.
    • Dynamic Heaps can obtain more memory from a “provider”, the core allocator for example.
  • Allocated blocks come from the specified heap or from the default one if NULL is specified as heap.
  • Freed blocks return to the heap from which they come.
  • It is possible to allocate blocks with arbitrary alignment, this is important in order to have memory aligned with cache lines for example.
  • First-fit algorithm.
  • Thread safe, multiple threads can access the same heap concurrently.
  • A default heap is always defined that feeds from the core allocator, no space assigned initially.

Heap Allocator API

memory_heap_t Type of a memory heap object.
chHeapObjectInit() Initializes an heap object of type memory_heap_t. .
chHeapAlloc() Allocates a memory block.
chHeapAllocAligned() Allocates an aligned memory block.
chHeapFree Returns a memory block to its heap.
chHeapStatus() Returns status information about an heap like remaining space and fragmentation.
chHeapGetSize() Returns the size of an allocated memory block.

Allocators Comparison

All the allocators can be used together but some are best suited in specific scenarios, this is a brief comparison:

Allocator Can Free Constant Time Variable Size From ISR Creates Fragments Thread Safe
Core Allocator no yes yes yes - yes
Pool Allocator yes yes no yes no yes
Guarded Pool Allocator yes no no yes1 no yes
Heap Allocator yes no yes no yes yes
C Library Allocator yes no yes no yes no2
  1. Without waiting.
  2. Depends on library implementation and integration with the RTOS.
  • Can Free indicates the ability of the allocator to return blocks to the available memory.
  • Constant Time is the ability to allocate/free blocks in a constant time, this is important for system determinism.
  • Variable Size is the ability to allocate/free blocks of variable size.
  • From ISR indicates that the allocator services can also be called from ISR context.
  • Creates Fragments indicates if allocating and freeing memory can cause internal fragmentation.
  • Thread Safe indicates that the allocator can be used in a multi-threaded environment.

The selection of the appropriate allocator depends on requirements of your application, there is not an universal best solution.