lwip—mem_init和mem_malloc详解

<pre name="code" class="cpp">#define MEM_ALIGNMENT           4
//对齐方式为4字节对齐
#ifndef LWIP_MEM_ALIGN_SIZE
#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1))
//实现待分配数据空间的内存对齐
#endif

#ifndef LWIP_MEM_ALIGN
//地址对齐,对齐方式也为4字节对齐
#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1)))
#endif

/* MEM_SIZE: the size of the heap memory. If the application will send
a lot of data that needs to be copied, this should be set high. */
#define MEM_SIZE                (8*1024)
//堆的总空间大小,此后在这个基础上划分堆,将在这个空间进行内存分配,内存块结构体和数据都是在这个空间上的

//mem为内存块的结构体,next;,prev都为内存块索引
struct mem {
  /** index (-> ram[next]) of the next struct */	//ram为堆的首地址,相当于数组的首地址,索引基地址
  mem_size_t next;					//next为下一个内存块的索引
  /** index (-> ram[next]) of the next struct */
  mem_size_t prev;					//prev为前一个内存块的索引
  /** 1: this area is used; 0: this area is unused */
  u8_t used;						//标志此内存块已被分配
};

static struct mem *ram_end;

/** All allocated blocks will be MIN_SIZE bytes big, at least!
 * MIN_SIZE can be overridden to suit your needs. Smaller values save space,
 * larger values could prevent too small blocks to fragment the RAM too much. */
#ifndef MIN_SIZE
#define MIN_SIZE             12
//内存块大小的最小限制,不能小于12
#endif /* MIN_SIZE */
/* some alignment macros: we define them here for better source code layout */
#define MIN_SIZE_ALIGNED     LWIP_MEM_ALIGN_SIZE(MIN_SIZE)
//将MIN_SIZE按4字节对齐,即把12按4字节对齐
#define SIZEOF_STRUCT_MEM    LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))
//将mem大小按4字节对齐
#define MEM_SIZE_ALIGNED     LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
//将堆的总空间按4字节对齐,MEM_SIZE在前面,为8*1024
//内存对齐解释看我的博文:http://blog.csdn.net/lg2lh/article/details/34853883

/** the heap. we need one struct mem at the end and some room for alignment */
static u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT];
//实际开的堆内存空间,MEM_SIZE_ALIGNED为对齐后的数据空间为8192
//堆内存的大小为MEM_SIZE_ALIGNED+(2*SIZEOF_STRUCT_MEM)+MEM_ALIGNMENT=8192+2*MEN结构体的大小+4

void
mem_init(void)
{
  struct mem *mem;
//定义一个mem结构体指针变量

  LWIP_ASSERT("Sanity check alignment",
    (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0);

  /* align the heap */
  ram = LWIP_MEM_ALIGN(ram_heap);
//将堆空间首地址ram_heap按4字节地址对齐
  /* initialize the start of the heap */
  mem = (struct mem *)ram;
//将堆空间ram 首地址强制转换成mem结构体类型,作为首个内存块,但这个内存块还未使用
  mem->next = MEM_SIZE_ALIGNED;
//把首个内存块的next指针指向了堆空间的最后一个地址(MEM_SIZE_ALIGNED为8*1024),后面实际在mem_malloc时会动态调整next索引,
//从而得到实际分配内存空间即为 mem->next减去该内存块mem的地址
//待分配内存块的next索引总是指向堆空间最后,好像也不一定,但是按照思路是这样的。
  mem->prev = 0;
//初始化,因为是第一个内存块,所以前一个内存块不存在,故初始化为0
  mem->used = 0;
//该内存块没有被分配,待分配状态
  /* initialize the end of the heap */
  ram_end = (struct mem *)&ram[MEM_SIZE_ALIGNED];
//例化一个堆空间末尾内存块,该内存块指向最后一个地址,标志结尾用的已被分配,不可再分配了
  ram_end->used = 1;
//该内存块已被分配
  ram_end->next = MEM_SIZE_ALIGNED;
//因为后续再无内存块故,next索引指向最后,即自己
  ram_end->prev = MEM_SIZE_ALIGNED;
//这个我也不知道啊
  mem_sem = sys_sem_new(1);

  /* initialize the lowest-free pointer to the start of the heap */
  lfree = (struct mem *)ram;
//初始化空闲对指针,此时首个内存块是空闲的

  MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
}

void *
mem_malloc(mem_size_t size)
{
  mem_size_t ptr, ptr2;
  struct mem *mem, *mem2;
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
  u8_t local_mem_free_count = 0;
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
  LWIP_MEM_ALLOC_DECL_PROTECT();

  if (size == 0) {
    return NULL;
  }
//size为0的话返回null 分配不成功

  /* Expand the size of the allocated memory region so that we can
     adjust for alignment. */
  size = LWIP_MEM_ALIGN_SIZE(size);
//将待分配数据按4字节进行对齐

  if(size < MIN_SIZE_ALIGNED) {
//如果待分配空间小于MIN_SIZE_ALIGNED(12),则返回分配空间也要为12,最小分配空间为12
    /* every data block must be at least MIN_SIZE_ALIGNED long */
    size = MIN_SIZE_ALIGNED;
  }

  if (size > MEM_SIZE_ALIGNED) {
//如果待分配空间大于MEM_SIZE_ALIGNED(8*1024),超出堆空间,则返回NULL,无法分配
    return NULL;
  }

  /* protect the heap from concurrent access */
  sys_arch_sem_wait(mem_sem, 0);
  LWIP_MEM_ALLOC_PROTECT();	//未定义
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
  /* run as long as a mem_free disturbed mem_malloc */
  do {
    local_mem_free_count = 0;
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */

    /* Scan through the heap searching for a free block that is big enough,
     * beginning with the lowest free block.
     */
//ptr初值=空闲内存块地址与堆内存首地址之差,如果ptr+size小于堆空间总大小8*1024,则可实现相应大小
//的内存块分配,其中ptr实际为已分配了的空间大小,size为待分配的空间大小,两个和一定要小于总空间,才可以实现分配.
//判断完成后,将ptr赋值为该内存块next所指地址
    for (ptr = (u8_t *)lfree - ram; ptr < MEM_SIZE_ALIGNED - size;
         ptr = ((struct mem *)&ram[ptr])->next) {
//将待分配的这个内存空间初始化为内存块结构体
	 mem = (struct mem *)&ram[ptr];

#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT //未定义
      mem_free_count = 0;
      LWIP_MEM_ALLOC_UNPROTECT();
      /* allow mem_free to run */
      LWIP_MEM_ALLOC_PROTECT();
      if (mem_free_count != 0) {
        local_mem_free_count = mem_free_count;
      }
      mem_free_count = 0;
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
//ptr为已分配了的内存空间
//后面你会发现,待分配内存块的mem->next始终指向堆空间的最后,即MEM_SIZE_ALIGNED。
//内存块未被使用,此时mem为待分配内存块,故mem->next指向MEM_SIZE_ALIGNED,
//剩余分配空间(MEM_SIZE_ALIGNED-已分配空间-MEM结构体大小)要大于要待分配空间size
      if ((!mem->used) &&
          (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {

        /* mem is not used and at least perfect fit is possible:
         * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */

//剩余分配空间(MEM_SIZE_ALIGNED-已分配空间-2*MEM结构体大小-12)
//要大于要待分配空间size,则才可以进行内存分配。
        if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { 									

          /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing
           * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
           * -> split large block, create empty remainder,
           * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
           * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
           * struct mem would fit in but no data between mem2 and mem2->next
           * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
           *       region that couldn't hold data, but when mem->next gets freed,
           *       the 2 regions would be combined, resulting in more free memory
           */
//ptr2指向新的待分配内存空间
          ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
          /* create mem2 struct */
//mem2为新的待分配内存块结构体
          mem2 = (struct mem *)&ram[ptr2];
 //新的内存块mem2未被使用
          mem2->used = 0;
//新的待分配的内存块mem2的next索引指向堆空间的最后,即MEM_SIZE_ALIGNED
          mem2->next = mem->next;
//而新的内存块的prev索引是我们这次正在分配的模块索引,即ptr
          mem2->prev = ptr;
          /* and insert it between mem and mem->next */
//把本次分配的mem内存块的next索引重新定位,指向新的待分配的模块的索引,不再指向堆空间最后
          mem->next = ptr2;
          mem->used = 1;//本内存块被使用

//我之前分析的都是新的待分配内存块next索引应该始终指向堆空间最后的,这里竟然判断了,可能存在不指向最后的情况
//具体原因还没分析。如果新的待分配内存块mem2的next索引未指向最后,则需要将它所指向的索引内存块的prev索引指向
 //他自己ptr2。
          if (mem2->next != MEM_SIZE_ALIGNED) {
            ((struct mem *)&ram[mem2->next])->prev = ptr2;

          }
          MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM));
        } else {//如果没有满足对应if条件,则直接分配完改内存块即可,也不用指向下一个待分配的内存块,因为没有空间可以再分配了
          /* (a mem2 struct does no fit into the user data space of mem and mem->next will always
           * be used at this point: if not we have 2 unused structs in a row, plug_holes should have
           * take care of this).
           * -> near fit or excact fit: do not split, no mem2 creation
           * also can't move mem->next directly behind mem, since mem->next
           * will always be used at this point!
           */
          mem->used = 1;
          MEM_STATS_INC_USED(used, mem->next - ((u8_t *)mem - ram));
        }

        if (mem == lfree) {//将空闲指针索引指向新的待分配内存块索引ram[lfree->next],即ptr2
          /* Find next free block after mem and update lowest free pointer */
          while (lfree->used && lfree != ram_end) {
            LWIP_MEM_ALLOC_UNPROTECT();
            /* prevent high interrupt latency... */
            LWIP_MEM_ALLOC_PROTECT();
            lfree = (struct mem *)&ram[lfree->next];
          }
          LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used)));
        }
        LWIP_MEM_ALLOC_UNPROTECT();
        sys_sem_signal(mem_sem);
        LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.",
         (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end);
        LWIP_ASSERT("mem_malloc: allocated memory properly aligned.",
         ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
        LWIP_ASSERT("mem_malloc: sanity check alignment",
          (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0);

        return (u8_t *)mem + SIZEOF_STRUCT_MEM;//返回分配结果,即已分配内存块数据空间的首地址。
      }
    }
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
    /* if we got interrupted by a mem_free, try again */
  } while(local_mem_free_count != 0);
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
  LWIP_DEBUGF(MEM_DEBUG | 2, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
  MEM_STATS_INC(err);
  LWIP_MEM_ALLOC_UNPROTECT();
  sys_sem_signal(mem_sem);
  return NULL;
}

lwip—mem_init和mem_malloc详解,布布扣,bubuko.com

时间: 2024-10-05 19:12:46

lwip—mem_init和mem_malloc详解的相关文章

TCP/IP详解学习笔记(一) 概述

生活中有舒适区,借口成为懒惰的护身符,学习也有舒适区,逃避便是阻止进步的最大障碍. 经过半年多嵌入式方面的工作和学习,我提高了很多,但同时我也对自己所面临的问题逐渐清晰: 1. 偏于实践,理论基础不牢固 2. 对算法,数据结构认知太浅 3. 对界面程序以及GUI相关学习比较排斥 我也一直主观上逃避对这些知识的学习,这也是为什么我写的内容都是以实践为主了.从本系列开始,我尝试离开大脑的舒适区,巩固自己理论相关的知识. TCP/IP作为整个现代互联网社会的基础,有着至关重要的作用,因此学习掌握协议栈

Spring事务管理(详解+实例)

写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都

转载:DenseNet算法详解

原文连接:http://blog.csdn.net/u014380165/article/details/75142664 参考连接:http://blog.csdn.net/u012938704/article/details/53468483 本文这里仅当学习笔记使用,具体细节建议前往原文细度. 论文:Densely Connected Convolutional Networks 论文链接:https://arxiv.org/pdf/1608.06993.pdf 代码的github链接:h

MariaDB(MySQL)创建、删除、选择及数据类型使用详解

一.MariaDB简介(MySQL简介略过) MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品.在存储引擎方面,使用XtraDB(英语:XtraDB)来代替MySQL的InnoDB. MariaDB由MySQL的创始人Michael Widenius(英语:Michael Widenius)主导开发,他早前曾以10亿美元的价格,将自己创建的公司MySQL A

HttpServletResponse和HttpServletRequest详解

HttpServletResponse,HttpServletRequest详解 1.相关的接口 HttpServletRequest HttpServletRequest接口最常用的方法就是获得请求中的参数,这些参数一般是客户端表单中的数据.同时,HttpServletRequest接口可以获取由客户端传送的名称,也可以获取产生请求并且接收请求的服务器端主机名及IP地址,还可以获取客户端正在使用的通信协议等信息.下表是接口HttpServletRequest的常用方法. 说明:HttpServ

POSIX 线程详解(经典必看)

总共三部分: 第一部分:POSIX 线程详解                                   Daniel Robbins ([email protected]), 总裁/CEO, Gentoo Technologies, Inc.  2000 年 7 月 01 日 第二部分:通用线程:POSIX 线程详解,第 2部分       Daniel Robbins ([email protected]), 总裁/CEO, Gentoo Technologies, Inc.  20

.NET深入解析LINQ框架(五:IQueryable、IQueryProvider接口详解)

阅读目录: 1.环路执行对象模型.碎片化执行模型(假递归式调用) 2.N层对象执行模型(纵横向对比链式扩展方法) 3.LINQ查询表达式和链式查询方法其实都是空壳子 4.详细的对象结构图(对象的执行原理) 5.IQueryable<T>与IQueryProvider一对一的关系能否改成一对多的关系 6.完整的自定义查询 1]. 环路执行对象模型.碎片化执行模型(假递归式调用) 这个主题扯的可能有点远,但是它关系着整个LINQ框架的设计结构,至少在我还没有搞懂LINQ的本意之前,在我脑海里一直频

netstat状态详解

一.生产服务器netstat tcp连接状态................................................................................ 2 1.1生产服务器某个业务LVS负载均衡上连接状态数量............................................... 2 1.2生产服务器某个业务web上连接状态数量...............................................

详解go语言的array和slice 【二】

上一篇  详解go语言的array和slice [一]已经讲解过,array和slice的一些基本用法,使用array和slice时需要注意的地方,特别是slice需要注意的地方比较多.上一篇的最后讲解到创建新的slice时使用第三个索引来限制slice的容量,在操作新slice时,如果新slice的容量大于长度时,添加新元素依然后使源的相应元素改变.这一篇里我会讲解到如何避免这些问题,以及迭代.和做为方法参数方面的知识点. slice的长度和容量设置为同一个值 如果在创建新的slice时我们把