Slabs是一个内核对象,它允许从指定的内存区域动态分配内存块。内存板中的所有内存块都具有单一的固定大小,允许有效地分配和释放它们,并避免内存碎片问题。

概念

可以定义任意数量的内存板(仅受可用 RAM 的限制)。每个内存板都由其内存地址引用。
内存板具有以下关键属性:

  • 每个块的块大小,以字节为单位。它必须至少为4N字节长,其中N大于0。
  • 可用于分配的块数。它必须大于零。
  • 为内存板块提供内存的缓冲区。它必须至少是块大小乘以块数字节的长度。

内存板的缓冲区必须与N字节边界对齐,其中N是大于2的2次幂(即 4、8、16、…)。要确保缓冲区中的所有内存块都与此边界对齐,块大小也必须是N的倍数。
必须先初始化内存板,然后才能使用它。这会将其所有块标记为未使用。
需要使用内存块的线程只需从内存板中分配它。当线程使用内存块完成时,它必须将块释放回内存板,以便可以重用该块。
如果所有块当前都在使用中,则线程可以选择等待一个块变为可用。任意数量的线程可以同时在空内存板上等待;当内存块变为可用时,它将被提供给等待时间最长的优先级最高的线程。
与堆不同,如果需要,可以定义多个内存板。这允许内存板具有较小的块,而其他内存板具有较大的块。或者,可以使用内存池对象。

内部操作

内存板的缓冲区是固定大小的块的数组,块之间没有浪费空间。
内存板使用链表跟踪未分配的块;每个未使用块的前4个字节提供必要的链接。

定义内存板

内存板是使用k_mem_slab类型定义的变量。然后必须通过调用k_mem_slab_init()对其进行初始化。
下面的代码定义并初始化一个内存板,该内存板具有6个块,这些块的长度为400字节,每个块都与4字节边界对齐。

  1. struct k_mem_slab my_slab;
  2. char __aligned(4) my_slab_buffer[6 * 400];
  3. k_mem_slab_init(&my_slab, my_slab_buffer, 400, 6);

也可以通过调用K_MEM_SLAB_DEFINE在编译时定义和初始化内存板。

  1. K_MEM_SLAB_DEFINE(my_slab, 400, 6, 4);

也可以将缓冲区放在静态区域里,通过K_MEM_SLAB_DEFINE_STATIC宏来定义内存板。

  1. K_MEM_SLAB_DEFINE_STATIC(my_slab, 400, 6, 4);

分配内存块

通过调用k_mem_slab_alloc()来分配内存块。
以下的代码最多等待 100 毫秒,以便内存块变为可用,然后用零填充该内存块。如果未获得合适的块,则会打印警告。

  1. char *block_ptr;
  2. if (k_mem_slab_alloc(&my_slab, &block_ptr, 100) == 0)) {
  3. memset(block_ptr, 0, 400);
  4. ...
  5. } else {
  6. printf("Memory allocation time-out");
  7. }

释放内存块

通过调用k_mem_slab_free()释放内存块。

  1. char *block_ptr;
  2. k_mem_slab_alloc(&my_slab, &block_ptr, K_FOREVER);
  3. ... /* use memory block pointed at by block_ptr */
  4. k_mem_slab_free(&my_slab, &block_ptr);

内存板的Kconfig

Kconfig 描述
CONFIG_MEM_SLAB_TRACE_MAX_UTILIZATION 这为k_mem_slab结构增加了可变性,以使内存版获得最大利用率。