zoukankan      html  css  js  c++  java
  • contiki系统分析四:内存分配


    contiki有三种分配内存的方式。


    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系统中通用的做法是静态分配内存.好的方法应该是动态去分配.


  • 相关阅读:
    线段树优化dp(elect选择)
    gdb调试
    无参装饰器
    3.23作业
    3.22周末作业
    函数对象与闭包函数
    3.20作业
    3.19作业
    名称空间与作用域
    函数参数的使用
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/2981337.html
Copyright © 2011-2022 走看看