lwip动态内存管理分析

内存管理采用动态链表的形式,如下所示:



/** @file

 *
 * Dynamic memory manager
 *
 */

/*
 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 *
 * Author: Adam Dunkels <adam@sics.se>
 *
 */

/* 包含必要的头文件 */
#include <string.h>
#include "lwip/arch.h"
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/sys.h"
#include "lwip/stats.h"

/* 该宏定义在opt.h中,等于0则使用下面的内存管理,否则使用标准C库的内存管理 */
#if (MEM_LIBC_MALLOC == 0)
/* lwIP replacement for your libc malloc() */

/* 小内存块将来会对应下面的一个内存管理结构体 */
struct mem {
  mem_size_t next, prev; //注意next,prev是一个数字,不是指针,他记录的是内存块在整个可以内存数组中的位置
#if MEM_ALIGNMENT == 1   
  u8_t used;
#elif MEM_ALIGNMENT == 2
  u16_t used;
#elif MEM_ALIGNMENT == 4
  u32_t used;//给内存块的使用情况
#elif MEM_ALIGNMENT == 8
  u64_t used;
#else
#error "unhandled MEM_ALIGNMENT size"
#endif /* MEM_ALIGNMENT */
};

/* 内存缓存的最后一个内存块控制块 */
static struct mem *ram_end;

#if 0
/* Adam original */
static u8_t ram[MEM_SIZE + sizeof(struct mem) + MEM_ALIGNMENT];
#else
/* Christiaan alignment fix */
static u8_t *ram;
static struct mem ram_heap[ 1 + ((MEM_SIZE + sizeof(struct mem)-1)/sizeof(struct mem)) ];
#endif


#define MIN_SIZE 12
#if 0 /* this one does not align correctly for some, resulting in crashes */
#define SIZEOF_STRUCT_MEM (unsigned int)MEM_ALIGN_SIZE(sizeof(struct mem))
#else
#define SIZEOF_STRUCT_MEM (sizeof(struct mem) + \
                          (((sizeof(struct mem) % MEM_ALIGNMENT) == 0)? 0 : \
                          (4 - (sizeof(struct mem) % MEM_ALIGNMENT))))
#endif

static struct mem *lfree;   /* pointer to the lowest free block */

static sys_sem_t mem_sem;

static void plug_holes(struct mem *mem)
{
  struct mem *nmem;
  struct mem *pmem;

  LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram);
  LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end);
  LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0);

  /* plug hole forward */
  LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE", mem->next <= MEM_SIZE);

/* 判断本内存后面一个内存块是否空闲,如果空闲,则合并起来 */
  nmem = (struct mem *)&ram[mem->next];
  if( mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end ) //释放的内存不跟最后一个内存控制块合并,最后一个是一直保留的
  {
    if (lfree == nmem) //调整第一个空闲内存指针
{
      lfree = mem;
    }
    mem->next = nmem->next;//调整本内存块的后指针
    ((struct mem *)&ram[nmem->next])->prev = (u8_t *)mem - ram;//合并后的内存块不存在了,其后一个内存块的前指针要调整到本内存块,使双向链表保持完整
  }

  /* plug hole backward */
  pmem = (struct mem *)&ram[mem->prev];//判断本内存块前面的内存块是否空闲,如果控制,则进行合并,原理同上
  if( pmem != mem && pmem->used == 0 ) 
  {
    if (lfree == mem) 
{
      lfree = pmem;
    }
    pmem->next = mem->next;
    ((struct mem *)&ram[mem->next])->prev = (u8_t *)pmem - ram;
  }
 
}


void mem_init(void)
{
  struct mem *mem;

#if 0
  /* Adam original */
#else
  /* Christiaan alignment fix */
  ram = (u8_t*)ram_heap; //让内存缓存头指针指向内存缓存数组,这里通过指针类型强转将结构体数组变为字符数组
#endif

  memset(ram, 0, MEM_SIZE);//清零内存缓存数组
  mem = (struct mem *)ram;//内存缓存数组的第一个字节开始,构造一个内存管理控制块,并初始化该控制块
  mem->next = MEM_SIZE;
  mem->prev = 0;
  mem->used = 0;
  ram_end = (struct mem *)&ram[MEM_SIZE];
  ram_end->used = 1;
  ram_end->next = MEM_SIZE;
  ram_end->prev = MEM_SIZE;

  mem_sem = sys_sem_new(1);

  lfree = (struct mem *)ram;//第一个空闲内块指针指针内存缓存数组的首地址

#if MEM_STATS
  lwip_stats.mem.avail = MEM_SIZE;
#endif /* MEM_STATS */

}


void mem_free(void *rmem)
{
  struct mem *mem;

/* 参数检查是必须的,对空指针进行操作有可能会引发硬件中断 */
  if (rmem == NULL) 
  {
    LWIP_DEBUGF(MEM_DEBUG | DBG_TRACE | 2, ("mem_free(p == NULL) was called.\n"));
    return;
  }

/* 等待获得互斥访问信号量 */
  sys_sem_wait(mem_sem);

  LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram &&(u8_t *)rmem < (u8_t *)ram_end);

/* 判断该内存地址是否在内存缓存数组之间,否则要停止操作,因为在应用程序中对内存的申请释放是很容易出错的,这样的检测非常有必要 */
  if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) 
  {
  
    LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_free: illegal memory\n"));
#if MEM_STATS
    ++lwip_stats.mem.err;
#endif /* MEM_STATS */

    sys_sem_signal(mem_sem);


    return;
  }
  
  /* 通过该内存地址,获得控制该地址内存的内存控制块,实际用户内存是在该内存控制块的后面,根据这个原理反推即可 */
  mem = (struct mem *) ( (u8_t *)rmem - SIZEOF_STRUCT_MEM );

  LWIP_ASSERT("mem_free: mem->used", mem->used);

/* 将该内存的标志置为“空闲” */
  mem->used = 0;

/* 调整第一个空闲内存块地址指针 */
  if (mem < lfree) 
  {
    lfree = mem;
  }

#if MEM_STATS
  lwip_stats.mem.used -= mem->next - ((u8_t *)mem - ram);
#endif /* MEM_STATS */

/* 调整内存控制块链表,并合并小内存块 */
  plug_holes(mem);
  
  /* 操作完毕,是否互斥访问控制信号量 */
  sys_sem_signal(mem_sem);
  
}


void *mem_realloc(void *rmem, mem_size_t newsize)
{
  mem_size_t size;
  mem_size_t ptr, ptr2;
  struct mem *mem, *mem2;

  /* 字节对齐 */
  if ((newsize % MEM_ALIGNMENT) != 0) 
  {
   newsize += MEM_ALIGNMENT - ((newsize + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT);
  }

 /* 不能超过最大值 */
  if (newsize > MEM_SIZE)
  {
    return NULL;
  }

  /* 获取互斥访问信号量 */
  sys_sem_wait(mem_sem);

  LWIP_ASSERT("mem_realloc: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
   (u8_t *)rmem < (u8_t *)ram_end);
  if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) //内存块必须在内存缓存数组里
  {
    LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_realloc: illegal memory\n"));
    return rmem;
  }
  
  /* 通过内存地址计算出内存控制块地址,这样才能访问该内存的内存控制块 */
  mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);

/* 计算内存控制块在整个内存缓存数组的位置 */
  ptr = (u8_t *)mem - ram;

  size = mem->next - ptr - SIZEOF_STRUCT_MEM;

#if MEM_STATS
  lwip_stats.mem.used -= (size - newsize);
#endif /* MEM_STATS */

  if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size) //这里好像有问题,如果后面的内存块是已经被占用的,也能直接分配出去吗?
  {
    ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
    mem2 = (struct mem *)&ram[ptr2];
    mem2->used = 0;
    mem2->next = mem->next;
    mem2->prev = ptr;
    mem->next = ptr2;
    if (mem2->next != MEM_SIZE) {
      ((struct mem *)&ram[mem2->next])->prev = ptr2;
    }

    plug_holes(mem2);
  }
  sys_sem_signal(mem_sem);
  return rmem;
}


#if 1
/**
 * Adam's mem_malloc(), suffers from bug #17922
 * Set if to 0 for alternative mem_malloc().
 */
void *mem_malloc(mem_size_t size)
{
mem_size_t ptr, ptr2;
struct mem *mem, *mem2;

/* 不能分配长度为0的内存 */
if (size == 0) {
return NULL;
}

/* 如果分配的内存长度不是字节对齐的,则扩展到字节对齐 */
if ((size % MEM_ALIGNMENT) != 0) {
size += MEM_ALIGNMENT - ((size + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT);
}

/* 如果分配的内存长度大于总内存缓存数组长度,则分配失败 */
if (size > MEM_SIZE) {
return NULL;
}

/* 动态内存分配管理器是支持多线程的,所以分配时要用信号量进行互斥,这里等待获得信号量 */
sys_sem_wait(mem_sem);

/* 从第一个空闲的位置开始遍历整个内存缓存数组 */
for( ptr = (u8_t *)lfree - ram; ptr < MEM_SIZE; ptr = ((struct mem *)&ram[ptr])->next ) 
{
mem = (struct mem *)&ram[ptr];//将数组数据转成内存控制块
if( (!mem->used) && (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= size + SIZEOF_STRUCT_MEM) ) //如果该内存位置为空闲,且该内存块的长度大于要分配的内存,则使用该内存块
{
ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
  mem2 = (struct mem *)&ram[ptr2];


mem2->prev = ptr;
mem2->next = mem->next;
mem->next = ptr2;
if (mem2->next != MEM_SIZE) {
((struct mem *)&ram[mem2->next])->prev = ptr2;//如果下个内存块没有指向最后内存控制块,则需要改变后一个内存控制块的前指针,调整双向链表指针
}

mem2->used = 0;
mem->used = 1;

#if MEM_STATS
lwip_stats.mem.used += (size + SIZEOF_STRUCT_MEM);
/*      if (lwip_stats.mem.max < lwip_stats.mem.used) {
lwip_stats.mem.max = lwip_stats.mem.used;
} */
if (lwip_stats.mem.max < ptr2) {
lwip_stats.mem.max = ptr2;
}
#endif /* MEM_STATS */


if (mem == lfree) { //如果本次分配到的内存块刚好是第一个空闲内存块,则要调整空闲内存块指针
/* Find next free block after mem */
while (lfree->used && lfree != ram_end) 
{
lfree = (struct mem *)&ram[lfree->next];
}
LWIP_ASSERT("mem_malloc: !lfree->used", !lfree->used);
}

/* 内存申请完成后,要释放互斥访问信号量 */
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.",
(unsigned long)((u8_t *)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);


/* 特别注意,返回申请到的内存地址是内存控制块后的第一个地址,并非内存控制块地址 */
  return (u8_t *)mem + SIZEOF_STRUCT_MEM;
}
}

LWIP_DEBUGF(MEM_DEBUG | 2, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));

#if MEM_STATS
++lwip_stats.mem.err;
#endif /* MEM_STATS */


sys_sem_signal(mem_sem);

return NULL;
}
#else
/**
 * Adam's mem_malloc() plus solution for bug #17922
 */
void *
mem_malloc(mem_size_t size)
{
  mem_size_t ptr, ptr2;
  struct mem *mem, *mem2;

  if (size == 0) {
    return NULL;
  }

  /* Expand the size of the allocated memory region so that we can
     adjust for alignment. */
  if ((size % MEM_ALIGNMENT) != 0) {
    size += MEM_ALIGNMENT - ((size + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT);
  }

  if (size > MEM_SIZE) {
    return NULL;
  }

  sys_sem_wait(mem_sem);
  for (ptr = (u8_t *)lfree - ram; ptr < MEM_SIZE - size; ptr = ((struct mem *)&ram[ptr])->next) 
  {
    mem = (struct mem *)&ram[ptr];

    if (!mem->used) {

      ptr2 = ptr + SIZEOF_STRUCT_MEM + size;


      if (mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) >= size) {
        /* split large block, create empty remainder */
        mem->next = ptr2;
        mem->used = 1;
        /* create mem2 struct */
        mem2 = (struct mem *)&ram[ptr2];
        mem2->used = 0;
        mem2->next = mem->next;
        mem2->prev = ptr;

        if (mem2->next != MEM_SIZE) {
          ((struct mem *)&ram[mem2->next])->prev = ptr2;
        }
      }
      else if (mem->next - (ptr + SIZEOF_STRUCT_MEM) > size) {
        /* near fit, no split, no mem2 creation,
           round up to mem->next */
        ptr2 = mem->next;
        mem->used = 1;
      }
      else if (mem->next - (ptr + SIZEOF_STRUCT_MEM) == size) {
        /* exact fit, do not split, no mem2 creation */
        mem->next = ptr2;
        mem->used = 1;
      }

      if (mem->used) {
#if MEM_STATS
        lwip_stats.mem.used += (size + SIZEOF_STRUCT_MEM);
        if (lwip_stats.mem.max < ptr2) {
          lwip_stats.mem.max = ptr2;
        }
#endif /* MEM_STATS */
        if (mem == lfree) {
          /* Find next free block after mem */
          while (lfree->used && lfree != ram_end) {
            lfree = (struct mem *)&ram[lfree->next];
          }
          LWIP_ASSERT("mem_malloc: !lfree->used", !lfree->used);
        }
        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.",
         (unsigned long)((u8_t *)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
        return (u8_t *)mem + SIZEOF_STRUCT_MEM;
      }
    }
  }
  LWIP_DEBUGF(MEM_DEBUG | 2, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
#if MEM_STATS
  ++lwip_stats.mem.err;
#endif /* MEM_STATS */
  sys_sem_signal(mem_sem);
  return NULL;
}
#endif


#endif /* MEM_LIBC_MALLOC == 0 */

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值