Skip to content

文档:内存管理

冯基魁 edited this page Jun 13, 2020 · 2 revisions

Contiki-NG支持静态和动态内存分配。 在嵌入式系统中,传统上,内存分配仅限于静态大小,因为静态内存没有泄漏和碎片。 但是,当运行时内存需求发生变化时,静态内存将很麻烦。 此类更改可能发生在跟踪连接的Web服务器或支持动态编程语言的虚拟机中。 当限制为静态内存时,程序员必须猜测资源的最大使用量,并过度分配内存块以防止内存耗尽。 为了缓解此类问题,我们在静态内存之外还提供了两种不同类型的内存分配器:半动态MEMB模块和动态HeapMem模块。

MEMB:内存块

os/lib/memb.h中声明的MEMB库提供了一组内存块管理功能。 内存块被分配为大小恒定的对象数组,并放置在静态内存中。 API如下所示:

Function Purpose
MEMB(name, structure, num) 声明一个内存块
void memb_init(struct memb *m) 初始化一个内存块
void *memb_alloc(struct memb *m) 分配一个内存块
char memb_free(struct memb *m, void *ptr) 释放一个内存块
int memb_inmemb(struct memb *m, void *ptr) 检查一个地址是否是内存块

MEMB()宏声明一个内存块,其类型为 struct memb。 由于该块已放入静态内存中,因此通常将其放置在使用内存块的C源文件的顶部。 name 标识存储块,以后用作其他存储块功能的参数。 structure 参数指定内存块的类型,num 表示内存块可容纳的对象数量。 struct memb 的定义如下:

struct memb {
  unsigned short size;
  unsigned short num;
  char *count;
  void *mem;
};

MEMB宏的扩展产生了三个定义静态内存的语句。 一个语句存储该内存块可以容纳的对象数量。 由于数量存储在unsigned short类型的变量中,因此存储块最多可以容纳USHRT_MAX个对象。 第二条语句分配由structure参数引用的类型为num的结构数组。

一旦使用MEMB()声明了内存块,就必须通过调用memb_init()对其进行初始化。 此函数采用struct memb的参数来标识内存块。

初始化struct memb之后,我们准备开始使用memb_alloc()从中分配对象。 通过相同的struct memb分配的所有对象都具有相同的大小,这由MEMB()structure参数的大小确定。 如果操作成功,则memb_alloc()返回指向已分配对象的指针;如果内存块中没有空闲对象,则返回NULL。

memb_free()释放以前使用memb_alloc()分配的对象。 需要两个参数来释放对象:m指向内存块,而ptr指向内存块中的对象。 可以检查任何指针以确定其是否在存储块的数据区域内。 如果ptr在内存块m内,则memb_inmemb()返回1;如果指向未知内存,则返回0。

我们展示了如何使用MEMB模块的示例。open_connection()函数为套接字标识的每个新连接分配一个新的结构连接变量。 关闭连接后,我们将释放内存块用于struct连接变量。

#include "contiki.h"
#include "lib/memb.h"

struct connection {
  int socket;
};
MEMB(connections, struct connection, 16);

struct connection *
open_connection(int socket)
{
  struct connection *conn;

  conn = memb_alloc(&connections);
  if(conn == NULL) {
    return NULL;
  }
  conn->socket = socket;
  return conn;
}

void
close_connection(struct connection *conn)
{
  memb_free(&connections, conn);
}

堆内存(HeapMem)

标准C库提供了一组用于分配和释放堆内存空间中的内存的函数。 对于不同的编译器工具链,目前尚不清楚默认堆内存模块在资源受限的执行环境中的性能如何。 在某些malloc实现中,大小不同的对象上的分配和释放模式可能更成问题。 因此,Contiki-NG包括一个堆内存模块,该模块已在各种硬件平台和不同的应用程序上使用。 HeapMem模块的API与标准C相似。为避免名称冲突,HeapMem中的函数名称为heapmem_alloc()heapmem_realloc()heapmem_free()而不是malloc()realloc()free()。 该API如下表所示:

Function Purpose
void *heapmem_alloc(size_t size) 分配未初始化的内存
void *heapmem_realloc(void *ptr, size_t size) 重新分配内存块的大小
void heapmem_free(void *ptr) 释放内存

本节中列出的所有函数都在C头文件os/lib/heapmem.h中声明。 heapmem_alloc()函数在堆上分配大小的内存字节。 如果成功分配了内存,heapmem_alloc()将返回一个指向它的指针。 如果没有足够的连续可用内存,heapmem_alloc()将返回NULL。

heamem_realloc()函数以新的大小重新分配先前分配的块ptr。 如果新块较小,则将旧块中数据的大小字节复制到新块中。 如果新块较大,则将复制完整的旧块,而新块的其余部分将包含未指定的数据。 一旦分配了新块并填充了其内容,便会释放旧块。 如果无法分配该块,heapmem_realloc()将返回NULL。 如果重新分配成功,heapmem_realloc()将返回一个指向新块的指针。

heapmem_free()会释放先前通过heapmem_alloc()heapmem_realloc()分配的块。 参数ptr必须指向已分配块的开始。