Memory Slabs

A memory slab is a kernel object that allows memory blocks to be dynamically allocated from a designated memory region. All memory blocks in a memory slab have a single fixed size, allowing them to be allocated and released efficiently and avoiding memory fragmentation concerns.

Concepts

Any number of memory slabs can be defined (limited only by available RAM). Each memory slab is referenced by its memory address.

A memory slab has the following key properties:

  • The block size of each block, measured in bytes. It must be at least 4N bytes long, where N is greater than 0.

  • The number of blocks available for allocation. It must be greater than zero.

  • A buffer that provides the memory for the memory slab’s blocks. It must be at least “block size” times “number of blocks” bytes long.

The memory slab’s buffer must be aligned to an N-byte boundary, where N is a power of 2 larger than 2 (i.e. 4, 8, 16, …). To ensure that all memory blocks in the buffer are similarly aligned to this boundary, the block size must also be a multiple of N.

A memory slab must be initialized before it can be used. This marks all of its blocks as unused.

A thread that needs to use a memory block simply allocates it from a memory slab. When the thread finishes with a memory block, it must release the block back to the memory slab so the block can be reused.

If all the blocks are currently in use, a thread can optionally wait for one to become available. Any number of threads may wait on an empty memory slab simultaneously; when a memory block becomes available, it is given to the highest-priority thread that has waited the longest.

Unlike a heap, more than one memory slab can be defined, if needed. This allows for a memory slab with smaller blocks and others with larger-sized blocks. Alternatively, a memory pool object may be used.

Internal Operation

A memory slab’s buffer is an array of fixed-size blocks, with no wasted space between the blocks.

The memory slab keeps track of unallocated blocks using a linked list; the first 4 bytes of each unused block provide the necessary linkage.

Implementation

Defining a Memory Slab

A memory slab is defined using a variable of type k_mem_slab. It must then be initialized by calling k_mem_slab_init().

The following code defines and initializes a memory slab that has 6 blocks that are 400 bytes long, each of which is aligned to a 4-byte boundary..

struct k_mem_slab my_slab;
char __aligned(4) my_slab_buffer[6 * 400];

k_mem_slab_init(&my_slab, my_slab_buffer, 400, 6);

Alternatively, a memory slab can be defined and initialized at compile time by calling K_MEM_SLAB_DEFINE.

The following code has the same effect as the code segment above. Observe that the macro defines both the memory slab and its buffer.

K_MEM_SLAB_DEFINE(my_slab, 400, 6, 4);

Allocating a Memory Block

A memory block is allocated by calling k_mem_slab_alloc().

The following code builds on the example above, and waits up to 100 milliseconds for a memory block to become available, then fills it with zeroes. A warning is printed if a suitable block is not obtained.

char *block_ptr;

if (k_mem_slab_alloc(&my_slab, &block_ptr, 100) == 0)) {
    memset(block_ptr, 0, 400);
    ...
} else {
    printf("Memory allocation time-out");
}

Releasing a Memory Block

A memory block is released by calling k_mem_slab_free().

The following code builds on the example above, and allocates a memory block, then releases it once it is no longer needed.

char *block_ptr;

k_mem_slab_alloc(&my_slab, &block_ptr, K_FOREVER);
... /* use memory block pointed at by block_ptr */
k_mem_slab_free(&my_slab, &block_ptr);

Suggested Uses

Use a memory slab to allocate and free memory in fixed-size blocks.

Use memory slab blocks when sending large amounts of data from one thread to another, to avoid unnecessary copying of the data.

API Reference

group mem_slab_apis

Defines

K_MEM_SLAB_DEFINE(name, slab_block_size, slab_num_blocks, slab_align)

Statically define and initialize a memory slab.

The memory slab’s buffer contains slab_num_blocks memory blocks that are slab_block_size bytes long. The buffer is aligned to a slab_align -byte boundary. To ensure that each memory block is similarly aligned to this boundary, slab_block_size must also be a multiple of slab_align.

The memory slab can be accessed outside the module where it is defined using:

extern struct k_mem_slab <name>; 

Parameters
  • name: Name of the memory slab.

  • slab_block_size: Size of each memory block (in bytes).

  • slab_num_blocks: Number memory blocks.

  • slab_align: Alignment of the memory slab’s buffer (power of 2).

Functions

int k_mem_slab_init(struct k_mem_slab *slab, void *buffer, size_t block_size, uint32_t num_blocks)

Initialize a memory slab.

Initializes a memory slab, prior to its first use.

The memory slab’s buffer contains slab_num_blocks memory blocks that are slab_block_size bytes long. The buffer must be aligned to an N-byte boundary matching a word boundary, where N is a power of 2 (i.e. 4 on 32-bit systems, 8, 16, …). To ensure that each memory block is similarly aligned to this boundary, slab_block_size must also be a multiple of N.

Parameters
  • slab: Address of the memory slab.

  • buffer: Pointer to buffer used for the memory blocks.

  • block_size: Size of each memory block (in bytes).

  • num_blocks: Number of memory blocks.

Return Value
  • 0: on success

  • -EINVAL: invalid data supplied

int k_mem_slab_alloc(struct k_mem_slab *slab, void **mem, k_timeout_t timeout)

Allocate memory from a memory slab.

This routine allocates a memory block from a memory slab.

Note

Can be called by ISRs, but timeout must be set to K_NO_WAIT.

Parameters
  • slab: Address of the memory slab.

  • mem: Pointer to block address area.

  • timeout: Non-negative waiting period to wait for operation to complete. Use K_NO_WAIT to return without waiting, or K_FOREVER to wait as long as necessary.

Return Value
  • 0: Memory allocated. The block address area pointed at by mem is set to the starting address of the memory block.

  • -ENOMEM: Returned without waiting.

  • -EAGAIN: Waiting period timed out.

  • -EINVAL: Invalid data supplied

void k_mem_slab_free(struct k_mem_slab *slab, void **mem)

Free memory allocated from a memory slab.

This routine releases a previously allocated memory block back to its associated memory slab.

Return

N/A

Parameters
  • slab: Address of the memory slab.

  • mem: Pointer to block address area (as set by k_mem_slab_alloc()).

static inline uint32_t k_mem_slab_num_used_get(struct k_mem_slab *slab)

Get the number of used blocks in a memory slab.

This routine gets the number of memory blocks that are currently allocated in slab.

Return

Number of allocated memory blocks.

Parameters
  • slab: Address of the memory slab.

static inline uint32_t k_mem_slab_max_used_get(struct k_mem_slab *slab)

Get the number of maximum used blocks so far in a memory slab.

This routine gets the maximum number of memory blocks that were allocated in slab.

Return

Maximum number of allocated memory blocks.

Parameters
  • slab: Address of the memory slab.

static inline uint32_t k_mem_slab_num_free_get(struct k_mem_slab *slab)

Get the number of unused blocks in a memory slab.

This routine gets the number of memory blocks that are currently unallocated in slab.

Return

Number of unallocated memory blocks.

Parameters
  • slab: Address of the memory slab.