内存管理采用动态链表的形式,如下所示:
/** @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);
}
/* 通过该内存地址,获得控制该地址内存的内存控制块,实际用户内存是在该内存控制块的后面,根据这个原理反推即可 */
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 */