Contiki memb

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 //指向结构体实际存放的地方
 }

这个东西讲出来挺拗口,来张图就一目了然了:

Contiki memb_第1张图片
内存分配---memb

小结一下:有一个结构体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个羽毛球场地,学生要打羽毛球就必须到体育馆去,打完球后,空出来的场地可以让别的同学继续打,如果没有场地了则必须等到有人打完后才能轮到你。

你可能感兴趣的:(Contiki memb)