memb 内存块分配器,在contiki中使用最频繁
mmem 管理内存分配器,很少去使用
标准C库的malloc的栈内存分配器,在contiki这种受限系统中使有最危险。
memb的内存分配器有下列的API:
MEMB(name, structure, num) : 定义memory block.
void memb_init(struct memb *m) : 初始化memory block.
void *memb_alloc(struct memb *m) : 分配memory block.
int memb_free(struct memb *m, void *ptr) : 释放memory block.
int memb_inmemb(struct memb *m, void *ptr) : 判断ptr传入的地址是否是memory block.
MEMB做了哪些事情
#define MEMB(name, structure, num) \ static char CC_CONCAT(name,_memb_count)[num]; \ static structure CC_CONCAT(name,_memb_mem)[num]; \ static struct memb name = {sizeof(structure), num, \ CC_CONCAT(name,_memb_count), \ (void *)CC_CONCAT(name,_memb_mem)} struct memb { unsigned short size; unsigned short num; char *count; void *mem; };
主要是定义了mem block.
其中CC_CONCAT这个宏把两个参数连接成为一个字符串.
name表示要定义的这个memblock的名称.
num表示memblock的数量,但是在struct memb中把num存储的大小定义为unsigned short型,也就是说,最多存隼128个memblock的对象
strructure参数表示需要扩展内存的结构体.
先定义出记录mem block对象的指针,和内容.然后再把信息记录在memb的结构体中.记录内存的状态跟分配内存是在同一时间完成的.
其中定义了两个数组
count:表示这段内存空间有没有被使用(0表示没使用,1表示已经使用),首地址memb->count
mem:实际的结构体指针存放的数组,首地址memb->mem,其它元素访问memb->mem+i*memb->size
memb->size是存放的sizeof(structure)的值.
num就表示分配数量,实际上mem的总大小为memb->size * memb->num
另外要注意的是,MEMB开辟的内存空间都是用static定义的,count和mem这两个数组由于没赋初值,放在.bss段里,但是memb name赋了初值,放在.data段里.众所周知,传统的C库中的malloc一系列的函数,是在heap中存放开辟的空间的.
由于内存受限系统不能去开辟一段自由使用的空间,而且contiki中的轻量级线程也没有自己的堆和栈.所以放在data或bss段中是比较好的办法.
memb_init
把已经开辟的空间里的所有值都设为0.用memset实现.
memb_alloc
从memb->count[0]开始往后找大小为mem->size的内存块,
如果memb->count[i]标记为1,则这个区块已经使用,继续向后找.
如果标记为0,则把区块分配出去.返回memb->mem +i*memb->size
如果返回NULL,则标记为0.
memb_free
有两个参数,一个是memb结构体的参数,另一个是要free掉的指针.
函数的实现流程是,在memb->mem数组中去掉传入的指针.如果找到.则把memb->count[i]标记为0.然后返回0
没找到这个指针,则返回为-1.
memb_inmemb
去查找传入参数是否是memb分配的地址.也就是看传入参数是否落在memb->mem的区间中.若是返回1,不是返回0.
下面是一个调用API的示例
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); }mmem管理内存分配器
模型:
mmem(managed memory allocator)
实现的文件是core/lib/mmem.c及对应的头文件.
中间涉及一个结构体:
struct mmem { struct mmem *next; unsigned int size; void *ptr; };
大体的思路,还是用static类型的全局变量.是字符型的数组.数组大小默认4096,也就是4096字节.这个参数可用MMEM_CONF_SIZE进行配置.
然后mmem_init()初始化上图所示的链表.
mmem_alloc()则是分配一段连续的内存.然后在链表中记录下来.
其中用了一个技巧.
/* Check if we have enough memory left for this allocation. */ if(avail_memory < size) { return 0; } /* We had enough memory so we add this memory block to the end of the list of allocated memory blocks. */ list_add(mmemlist, m); /* Set up the pointer so that it points to the first available byte in the memory block. */ m->ptr = &memory[MMEM_SIZE - avail_memory]; /* Remember the size of this memory block. */ m->size = size; /* Decrease the amount of available memory. */ avail_memory -= size;
avail_memory是一个全局变量.MMEM_SIZE是分配的内存空间的总大小.则MMEM_SIZE-avail_memory就是还没有分配的内存的起始的数组索引.
这也意味着,在这段内存空间里,已分配的内存始终在前面,未分配的内存始终在后面.这样做的话,也有合理性.在内存分配时,不存在内存碎片.
mmem->size是当前分配内存的大小. mmem->ptr是当前分配内存的起始地址.
mmem_free(),有一个参数,即当前分配内存的mmem结构体.实现是,利用memmove,把后面已分配内存的空间的数据覆盖掉刚才要free掉的内存.然后再从链表中删除要free的mmem的记录.
static struct mmem mmem; static void test_mmem(void) { struct my_struct { int a; } my_data, *my_data_ptr; if(mmem_alloc(&mmem, sizeof(my_data)) == 0) { printf("memory allocation failed\n"); } else { printf("memory allocation succeeded\n"); my_data.a = 0xaa; memcpy(MMEM_PTR(&mmem), &my_data, sizeof(my_data)); printf("Value a equals 0x%x\n", ((struct my_struct *)MMEM_PTR(&mmem))->a); mmem_free(&mmem); } }
示例代码,没有分析的必要了.
这种 *** 作在中断,多线程及抢占式的代码存在的情况下,极不安全.所以一般只是在contiki的进程中使用.如果获取分配的内存地址,只能用MMEM_PTR()这个宏.
在contiki系统中,可以在platform下面的初始化代码时提供mmem_init.
platform/iris/contiki-iris-main.c有对应的例子.可以看一下.
第三种分配内存的方法即C库里的malloc, calloc等函数.但是在contiki内存受限.不会分配到连续的内存,而且也要开足heap区间.对contiki来说,不是一个好的选择.
memb的话,属于静态的分配内存.mmem属于动态分配内存.
但是现在的contiki系统中通用的做法是静态分配内存.好的方法应该是动态去分配.
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)