Contiki提供了三种内存分配机制:memb机制、mmem机制和标准C语言库中的堆内存分配机制。memb最常使用,mmem极少使用,而使用堆内存分配是一件危险的事情。这篇日志讲解memb
memb库提供了一系列内存块管理函数。内存块在静态内存区中被分配为具有固定尺寸的数组。
先把里面的例子搬出来
#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);
}
先来看看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;
//num表示可以存放的结构体个数,由于用short声明,表明最多只能控制127个结构体
unsigned short num;
char *count;
void *mem;
};
先借用上例中的代码把这个宏展开来看看:
struct connection {
int socket;
};
MEMB(connections, struct connection, 16);
展开后的代码:
//char数组,估计用于指向实际内存地址
static char connections_memb_count[16];
//结构体数组,结构体实际存放的地方
static struct connection connections_memb_mem[16];
static struct memb connections =
{
sizeof(struct connection), //这里可以假设为2,一个int的大小
16, //结构体总个数
connections_memb_count, //指向指针数组
(void *)connections_memb_mem //指向结构体实际存放的地方
}
这个东西讲出来挺拗口,来张图就一目了然了:
小结一下:有一个结构体connection,我们需要分配一段内存存放16个connection。使用MEMB实际上已经分配了这16个空间了,需要注意的是static关键字表明,它们被分配到了静态内存区。那么现在还有一个疑问,connections_memb_count[16]里面存放什么东西?有何用,得继续往下研究。
下面来看下memb_init原型:
void memb_init(struct memb *m)
{
//把char数组里面的元素全部初始化为0
memset(m->count, 0, m->num);
//把结构体所占内存全部初始化为0
memset(m->mem, 0, m->size * m->num);
}
memset()函数是C语言标准库函数,memb_init函数就是把所占内存全部清0。
memb_alloc原型:
void *memb_alloc(struct memb *m)
{
int i;
for(i = 0; i < m->num; ++i)
{
if(m->count[i] == 0) //为0表示此空间并未使用
{
++(m->count[i]); //等同于m->count[i]=1;
//返回分配的结构体空间首地址
return (void *)((char *)m->mem + (i * m->size));
}
}
/* 能走到这里说明没有多余空间,分配失败了,标志为返回NULL */
return NULL;
}
现在终于明白connections_memb_count[16]的作用了,可以把它理解为标志位。某段结构体空间已用则标上1,某段结构体空间空闲则标上0。如果象C#那样有BitArray这样的数据结构,完全可以用一个位来存放这样的标志,现在用8个位存放一个标志,有点浪费空间了。不过如果用位运算,访问的时候需要另外声明函数返回标志,有些麻烦。
char[16]实际上和struct[16]一一对应,在char[16]中只要找到一个为0的元素,就说明它所对应的struct空间空闲,这时返回struct空间首地址作为memb_alloc函数的返回值。
memb_free原型:
char memb_free(struct memb *m, void *ptr)
{
int i;
char *ptr2;
ptr2 = (char *)m->mem;
for(i = 0; i < m->num; ++i)
{
if(ptr2 == (char *)ptr) //找到释放的空间
{
if(m->count[i] > 0)
{
--(m->count[i]); //设标志位为0
}
return m->count[i]; //返回0
}
ptr2 += m->size; //指向下一块空间
}
return -1; //没找到相应空间返回-1
}
memb_free用于释放空间,实际上只是把相应的标志位设为0,结构体空间原来的数据并未清除,这是不是有些危险?所以在使用memb的时候一定要注意,里面的结构体每个成员在使用前必须要全部赋值,否则会有出错的可能。
**int memb_inmemb(struct memb m, void ptr)
求证一个指针是否在memb之中
*int memb_numfree(struct memb m)
返回memb的空余空间的个数
最后来个总结吧,memb这东西我个人感觉更象是一个线程池之类的东西。例如我们在写程序时,需要多次同时用到一个结构体类型,每个结构体每次用的时间都不确定,这篇文章开头用的socket就是个很典型的例子。这时就可以使用memb进行统一管理。memb的作用可以作个比喻:学校有个体育馆,里面有6个羽毛球场地,学生要打羽毛球就必须到体育馆去,打完球后,空出来的场地可以让别的同学继续打,如果没有场地了则必须等到有人打完后才能轮到你。