ualloc implements a traits-based allocator, described below.
Memory in mbed OS
mbed OS provides memory allocation services that are based on a standard memory organization, described below. The memory allocation services provide for most use-cases in memory allocation, including heap allocation, pool allocation, and extendable pools.
Memory organization in mbed OS
In a conventional embedded system, there are four areas of memory: Code, Global Data, the heap, and the stack. Frequently, the heap and the stack are organized so that they occupy the same block of memory. In mbed OS, we add two additional areas of memory: uVisor memory and the never free heap. Memory is organized as below. Note that code generally lives in ROM, so it is not included in this diagram.
Cortex-M3/M4 Cortex-M0/M+ Largest RAM address Largest RAM address +-----------------+ +-----------------+ | Never Free Heap | | Never Free Heap | | | | | | Heap | | Heap | +-----------------+ +-----------------+ | Global Data | | Global Data | +-----------------+ +-----------------+ | Stack | | Stack | +-----------------+ +-----------------+ | uVisor Memory | Smallest RAM address +-----------------+ Smallest RAM address
On Cortex-M3/M4, the uVisor reserves a small portion of memory at the beginning of RAM for itself and for secured features (boxes). The uVisor secures this area using the MPU. For more information on the uVisor, see our main site
The stack is placed at the bottom of the memory, and it grows downwards. This location is selected explicitly because it allows stack overflows to be easily caught. In a Cortex-M3/M4 system, where the uVisor is in use, the first access below the bottom of the stack will trigger a MemManage exception, handled by the uVisor. In a Cortex-M0/M0+ system, it will trigger a HardFault. This allows applications to recover from stack overflows, generally through a reset.
This organization does mean that a stack must be sized to match the application. Currently, this is a value set in the target, but a future version will expose stack configuration through yotta config.
Global Data is the typical
.data sections generated by the compiler. This section's size depends on the application and requires no configuration.
There are two kinds of heap in mbed OS. The heap grows upwards from the bottom of the heap section, while the never free heap grows downwards from the top.
The standard heap
The standard heap is a typical dlmalloc heap, which grows upwards from the bottom of the heap section using the
sbrk function provided by the core-util module.
The never free heap
The never free heap grows downwards from the end of memory. The never free heap is intended for use with data that need not be freed, such as memory pools. Memory is allocated from the never free heap using the reverse sbrk function (
krbs) provided by the core-util module.
Memory Allocation in mbed OS
mbed OS provides several memory allocation services, including a traits allocator, a typical dlmalloc heap, a never free heap, a pool allocator and an extendable pool allocator. These facilities are built on top of two trivial, but lock-free, allocators:
krbs() (a reverse
In general, you should use
mbed_ualloc() where malloc-type allocation is necessary. Standard library functions, such as
calloc can also be used, but they are not as flexible as
mbed_ualloc(). You should avoid using
krbs() where possible; these are low-level functions, intended for use by other memory allocators.
Where memory is allocated in interrupt context, or similarly sized memory is used frequently, it can be advantageous to use
calloc should not be called from an interrupt context.
mbed_ualloc() should only be called from an interrupt context with the
UALLOC_TRAITS_NEVER_FREE trait set.
ualloc traits allocator
In mbed OS, all memory allocation is done through a traits allocator, ualloc. ualloc provides features that support both the dlmalloc heap and the never free heap. Currently, only two traits are supported in ualloc:
UALLOC_TRAITS_NEVER_FREE, which allocates from the never-free region, and
UALLOC_TRAITS_ZERO_FILL, which zeros the allocated space prior to returning it, much like
ualloc hooks several of the standard library functions:
This is to ensure that all allocations are done via ualloc, which in turn ensures that all compilers with all libc's will produce the same memory behavior. Using ualloc as a the channel for all memory allocations also provides a common point for memory allocation analysis. By analyzing calls to ualloc functions, it is possible to monitor all memory allocation behavior.
For allocations with the
UALLOC_TRAITS_NEVER_FREE trait, ualloc calls
krbs() directly. Without the never-free trait, ualloc calls dlmalloc functions.
The standard heap
The standard heap is managed by dlmalloc. For standard heap operations, ualloc forwards operations to dlmalloc. dlmalloc, in turn, uses
sbrk() to obtain more memory.
The pool allocator
mbed OS provides a pool allocator in the core-util module, called
PoolAllocator. This allocator divides its block of memory into 4-byte aligned regions of fixed size. The pool allocator is lock-free and very fast, so it is suitable for use in interrupt context.
PoolAllocator can run out of memory, however, so for some operations,
ExtendablePoolAllocator is more useful.
The extendable pool allocator
mbed OS also provides an extendable pool allocator in the core-util module, called
ExtendablePoolAllocator. It is built on top of PoolAllocator, with the sole difference that, when
ExtendablePoolAllocator runs out of pool elements to allocate, it can request more from the never free heap, using
ualloc(), with the
UALLOC_TRAITS_NEVER_FREE flag set.
sbrk is a trivial allocator implemented in the core-util module. It supports linear allocation and deallocation. It is completely lock-free.
krbs is effectively the reverse of
sbrk(), allocating memory from the end of a section, rather than the beginning. The one exception in behavior is that
krbs() does not support deallocation.