因为lwIP主要用于嵌入式系统,内存要求比较高,所以要对那些小对象进行池化之类的处理来加快分配速度,减少内存碎片产生。
lwIP中主要有memp.h, memp_std.h, memp.c, mem.h, mem.c几个类组成了内存管理模块。
memp.c
动态内存池管理器, lwip拥有各种不同的内存池来为各个模块的小对象分配内存。
一个内存池主要有name,description,number(内存池里的内存节点的数目)和size(内存池里的内存节点的大小)
内存节点的struct描述:
struct memp {
struct memp *next; // 指向下一个内存节点的指针
#if MEMP_OVERFLOW_CHECK // 如果进行内存溢出检测,
const char *file; // 发生溢出时调用函数的文件名
int line; // 发生溢出时调用函数的行号
#endif /* MEMP_OVERFLOW_CHECK */
};
lwip中各类内存池的描述在memp_std.h文件中:
该文件主要由三种内存池组成: 1. LWIP_MEMPOOL(标准的内存池) 2. LWIP_MALLOC_MEMPOOL(被mem.c中mem_malloc使用的内存池,需要在lwippools.h自定义不同大小的内存池,稍后会讲到) 3. LWIP_PBUF_MEMPOOL(PBUF的内存池)。其中2,3两种内存池均是通过调用第一种内存池来实现的,所以我们来看下第一种内存池,也就是lwip的标准内存池。
我们来看一个TCP_PCB的内存池定义:
LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB")
其中TCP_PCB是内存池名称,MEMP_NUM_TCP_PCB是节点的数目,sizeof(struct tcp_pcb)是每个节点大小, "TCP_PCB"是内存池的描述。
而在memp.c中通过不断定义这些描述(宏)来保存内存池中各种不同的信息到相应的结构中去:
1. 通过各种内存池的唯一的名称定义一个enum,并且在最后插入MEMP_MAX来得到内存池的总数。
typedef enum {
#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
#include "lwip/memp_std.h"
MEMP_MAX
} memp_t;
2.定义数组memp_size[MEMP_MAX]存放每个内存池的节点大小。
/** This array holds the element sizes of each pool. */
#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC
static
#endif
const u16_t memp_sizes[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size),
#include "lwip/memp_std.h"
};
3. 定义数组memp_num[MEMP_MAX]存放每个内存池的节点数目。
/** This array holds the number of elements in each pool. */
static const u16_t memp_num[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc) (num),
#include "lwip/memp_std.h"
};
4. 定义数组memp_num[MEMP_MAX]存放每个内存池的描述。
/** This array holds a textual description of each pool. */
#ifdef LWIP_DEBUG
static const char *memp_desc[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc) (desc),
#include "lwip/memp_std.h"
};
#endif /* LWIP_DEBUG */
/** This array holds a textual description of each pool. */
#ifdef LWIP_DEBUG
static const char *memp_desc[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc) (desc),
#include "lwip/memp_std.h"
};
#endif /* LWIP_DEBUG */
5. 定义数组memp_memory,这个数组所占有的内存就是所有内存池用的实际内存块。
数组长度为各个池的需要的内存总和,即每个内存池+ ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) ),
其中num为节点数目,MEMP_SIZE为每个节点结构所需要的额外内存,size为内存池提供给上层用户的内存大小。
这些大小都是要对齐的,还有个MEM_ALIGNMENT - 1是为了对齐数组memp_memory,因为定义的memp_memory是不
一定对齐的,最坏的情况就要MEM_ALIGNMENT - 1。
/** This is the actual memory used by the pools. */
static u8_t memp_memory[MEM_ALIGNMENT - 1
#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) )
#include "lwip/memp_std.h"
];
内存对齐的概念我想大家都懂,这儿就不多做解释了,在lwip的mem.h头文件定义了下面两个宏来进行size和内存地址的对齐。
#ifndef LWIP_MEM_ALIGN_SIZE
#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1))
#endif
#ifndef LWIP_MEM_ALIGN
#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1)))
#endif
mem.c:
如果使用pool的话就是运用内存池管理器进行内存管理。
否则的话主要实现了一个实现了一个类似于C malloc的heap的轻量级内存管理器。
下面开始讲述每个函数的功能,工作原理和要点:
memp.h memp.c
memp_init(): 初始化每个内存池,通过struct memp作为节点以单链表的形式串联起来。
memp_malloc(): 从相应的内存池中取出内存。
memp_free(): 归还内存到相应的内存池,需要检测溢出。
memp_overflow_init(): 溢出检测的初始化,主要是在用户申请的内存区域前后插入一些填充符0xcd。
memp_overflow_check_element(): 节点溢出检测,在用户申请的内存区域前后检查填充符0xcd是否被覆盖,是则溢出。
memp_overflow_check_all(): 所有节点溢出检测,安全,但是相对慢。
一个内存池的一个结点的内存分配情况:
------------------------------------------------------------------------------------------------------------------------------------
struct memp所占用的内存 + check_overflow_before_region + 用户申请的内存 +check_overflow_after_region
------------------------------------------------------------------------------------------------------------------------------------
memp_memory的内存分配情况:
------------------------------------------------------------------------------------------------------------------------------------
对齐地址的内存空间(0->MEM_ALIGNMENT - 1) + 各个内存池所占内存
------------------------------------------------------------------------------------------------------------------------------------
memp_std.h:
防止多次定义错误,因为memp.c使用#include memp_std.h并且定义以下的宏的技巧,所以每次需要undef这些宏.
mem.h mem.c
#if MEM_USE_POOLS:
mem_malloc: 从相应的提供给malloc用的不同size的pool中取一个合适的一个,并且将这个pool的size保存到相应的结构memp_malloc_helper中。
可以在lwippools.h自定义pool,详情请见代码memp_std.h和memp.h文件的注释:
LWIP_MALLOC_MEMPOOL_START
LWIP_MALLOC_MEMPOOL(20, 256)
LWIP_MALLOC_MEMPOOL(10, 512)
LWIP_MALLOC_MEMPOOL(5, 1512)
LWIP_MALLOC_MEMPOOL_END
mem_free: 释放到相应的内存池中, 取出memp_malloc_helper中的size放入相应的内存池中去。
#else /* MEM_USE_POOLS: */
实现了一个类似于C malloc的heap的轻量级内存管理器。
/* 采用索引双链表的形式来管理heap */
/* 节点结构 */
struct mem {
/** index (-> ram[next]) of the next struct */
mem_size_t next; // 下一个struct的索引
/** index (-> ram[next]) of the next struct */
mem_size_t prev; // 上一个struct的索引
/** 1: this area is used; 0: this area is unused */
u8_t used; // 是否被使用
};
mem_init: 初始化heap,并且设置lfree为最低地址的空闲节点,以加快搜索速度。
mem_malloc: 从heap上分配用户所需size的内存。从lfree节点开始搜索空闲并且可以容纳SIZEOF_STRUCT_MEM + size的空间,
如果找到这样的节点mem,则分为两种情况,如果可容纳的空间可以容纳SIZEOF_STRUCT_MEM + size + (SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED),则说明可以新建一个空闲节点,于是将建立新的空闲节点并且插入到mem和mem->next之间,将mem的used标志为1,否则的话不建立节点,只是设置mem的used标志为1。
mem_free: 释放的时候会改used标志为0,表示空闲,并且调用plug_holes()函数来进行前后合并空闲空间。
mem_calloc: 使用mem_malloc申请size×count大小的地址,并且初始化清0所分配的内存。
mem_realloc: 重新分配new_size的内存,lwip目前不支持扩大size的情况。
如果当前节点后面的那个节点也是free的话,那么可以合并多余剩下的节点和后面的那个free节点。
如果不是空闲的则看看剩余的空间是否足够新建一个空闲节点,能则建之,并将其插入到链表中去。
相关文件的代码注释:
memp.h
/* * 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 * */ #ifndef __LWIP_MEMP_H__ #define __LWIP_MEMP_H__ #include "lwip/opt.h" #ifdef __cplusplus extern "C" { #endif /* 通过各种内存池的唯一的名称定义一个enum,并且在最后插入MEMP_MAX来得到内存池的总数。*/ /* Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */ typedef enum { #define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name, #include "lwip/memp_std.h" MEMP_MAX } memp_t; #if MEM_USE_POOLS /* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */ typedef enum { /* Get the first (via: MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/ /* 获得第一个内存池,enum中mem_malloc使用的pool中的第一个 */ MEMP_POOL_HELPER_FIRST = ((u8_t) #define LWIP_MEMPOOL(name,num,size,desc) #define LWIP_MALLOC_MEMPOOL_START 1 #define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0 #define LWIP_MALLOC_MEMPOOL_END #include "lwip/memp_std.h" ) , /* Get the last (via: MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */ /* 获得最后一个内存池,enum中mem_malloc使用的pool中的最后一个 */ MEMP_POOL_HELPER_LAST = ((u8_t) #define LWIP_MEMPOOL(name,num,size,desc) #define LWIP_MALLOC_MEMPOOL_START #define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size * #define LWIP_MALLOC_MEMPOOL_END 1 #include "lwip/memp_std.h" ) } memp_pool_helper_t; /* The actual start and stop values are here (cast them over) We use this helper type and these defines so we can avoid using const memp_t values */ #define MEMP_POOL_FIRST ((memp_t) MEMP_POOL_HELPER_FIRST) // mem_malloc用的第一个内存池 #define MEMP_POOL_LAST ((memp_t) MEMP_POOL_HELPER_LAST) // mem_malloc用的最后一个内存池 #endif /* MEM_USE_POOLS */ #if MEMP_MEM_MALLOC || MEM_USE_POOLS extern const u16_t memp_sizes[MEMP_MAX]; #endif /* MEMP_MEM_MALLOC || MEM_USE_POOLS */ #if MEMP_MEM_MALLOC #include "mem.h" #define memp_init() #define memp_malloc(type) mem_malloc(memp_sizes[type]) #define memp_free(type, mem) mem_free(mem) #else /* MEMP_MEM_MALLOC */ #if MEM_USE_POOLS /** This structure is used to save the pool one element came from. */ struct memp_malloc_helper { memp_t poolnr; // enum memp_t }; #endif /* MEM_USE_POOLS */ void memp_init(void); #if MEMP_OVERFLOW_CHECK void *memp_malloc_fn(memp_t type, const char* file, const int line); #define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__) #else void *memp_malloc(memp_t type); #endif void memp_free(memp_t type, void *mem); #endif /* MEMP_MEM_MALLOC */ #ifdef __cplusplus } #endif #endif /* __LWIP_MEMP_H__ */
memp.c
/** * @file * Dynamic pool memory manager * * lwIP has dedicated pools for many structures (netconn, protocol control blocks, * packet buffers, ...). All these pools are managed here. */ /* * 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 * */ #include "lwip/opt.h" #include "lwip/memp.h" #include "lwip/pbuf.h" #include "lwip/udp.h" #include "lwip/raw.h" #include "lwip/tcp.h" #include "lwip/igmp.h" #include "lwip/api.h" #include "lwip/api_msg.h" #include "lwip/tcpip.h" #include "lwip/sys.h" #include "lwip/stats.h" #include "netif/etharp.h" #include "lwip/ip_frag.h" #include #if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ struct memp { struct memp *next; // 指向下一个节点 #if MEMP_OVERFLOW_CHECK const char *file; // mem_malloc调用者的文件 int line; // mem_malloc调用者的行数 #endif /* MEMP_OVERFLOW_CHECK */ }; #if MEMP_OVERFLOW_CHECK /* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning * and at the end of each element, initialize them as 0xcd and check * them later. */ /* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free, * every single element in each pool is checked! * This is VERY SLOW but also very helpful. */ /* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in * lwipopts.h to change the amount reserved for checking. */ #ifndef MEMP_SANITY_REGION_BEFORE #define MEMP_SANITY_REGION_BEFORE 16 #endif /* MEMP_SANITY_REGION_BEFORE*/ #if MEMP_SANITY_REGION_BEFORE > 0 #define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE) /* 对齐 */ #else #define MEMP_SANITY_REGION_BEFORE_ALIGNED 0 #endif /* MEMP_SANITY_REGION_BEFORE*/ #ifndef MEMP_SANITY_REGION_AFTER #define MEMP_SANITY_REGION_AFTER 16 #endif /* MEMP_SANITY_REGION_AFTER*/ #if MEMP_SANITY_REGION_AFTER > 0 #define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER) /* 对齐 */ #else #define MEMP_SANITY_REGION_AFTER_ALIGNED 0 #endif /* MEMP_SANITY_REGION_AFTER*/ /* MEMP_SIZE: save space for struct memp and for sanity check */ #define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED)/* struct memp的结构大小 + 前方溢出检测填充区域,并对齐 */ #define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED) /* size + 后方溢出检测填充区域,并对齐 */ #else /* MEMP_OVERFLOW_CHECK */ /* No sanity checks * We don't need to preserve the struct memp while not allocated, so we * can save a little space and set MEMP_SIZE to 0. */ #define MEMP_SIZE 0 /* 这儿有一个space优化,可以将MEMP_SIZE设置为0,因为用户申请的内存大小至少为1字节, next指针在出链表之后没意义,可以被覆盖 */ #define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) /* 对齐的size*/ #endif /* MEMP_OVERFLOW_CHECK */ /** This array holds the first free element of each pool. * Elements form a linked list. */ static struct memp *memp_tab[MEMP_MAX]; // 每个pool头指针组成的数组 #else /* MEMP_MEM_MALLOC */ #define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) #endif /* MEMP_MEM_MALLOC */ /* 每个pool的节点大小组成的数组 */ /** This array holds the element sizes of each pool. */ #if !MEM_USE_POOLS && !MEMP_MEM_MALLOC static #endif const u16_t memp_sizes[MEMP_MAX] = { #define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size), #include "lwip/memp_std.h" }; #if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ /* 每个pool的节点数目组成的数组 */ /** This array holds the number of elements in each pool. */ static const u16_t memp_num[MEMP_MAX] = { #define LWIP_MEMPOOL(name,num,size,desc) (num), #include "lwip/memp_std.h" }; /* 每个pool的description组成的数组 */ /** This array holds a textual description of each pool. */ #ifdef LWIP_DEBUG static const char *memp_desc[MEMP_MAX] = { #define LWIP_MEMPOOL(name,num,size,desc) (desc), #include "lwip/memp_std.h" }; #endif /* LWIP_DEBUG */ /* 所有pool的内存源头数组 */ /** This is the actual memory used by the pools. */ static u8_t memp_memory[MEM_ALIGNMENT - 1 /* 是为了对齐数组memp_memory,因为定义的memp_memory是不一定对齐的,最坏的情况就要MEM_ALIGNMENT - 1 */ #define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) ) #include "lwip/memp_std.h" ]; #if MEMP_SANITY_CHECK /** * Check that memp-lists don't form a circle 检测链表是否有环,可以改进算法 */ static int memp_sanity(void) { s16_t i, c; struct memp *m, *n; for (i = 0; i < MEMP_MAX; i++) { for (m = memp_tab[i]; m != NULL; m = m->next) { c = 1; for (n = memp_tab[i]; n != NULL; n = n->next) { if (n == m && --c < 0) { return 0; } } } } return 1; } #endif /* MEMP_SANITY_CHECK*/ #if MEMP_OVERFLOW_CHECK /** * Check if a memp element was victim of an overflow * (e.g. the restricted area after it has been altered) * * @param p the memp element to check * @param memp_size the element size of the pool p comes from */ static void memp_overflow_check_element(struct memp *p, u16_t memp_size) { u16_t k; u8_t *m; #if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 // 在前方区域查找是否有填充符0xcd被更改,如果有则溢出 m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) { if (m[k] != 0xcd) { LWIP_ASSERT("detected memp underflow!", 0); } } #endif #if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 // 在后方区域查找是否有填充符0xcd被更改,如果有则溢出 m = (u8_t*)p + MEMP_SIZE + memp_size; for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) { if (m[k] != 0xcd) { LWIP_ASSERT("detected memp overflow!", 0); } } #endif } /** * Do an overflow check for all elements in every pool. * * @see memp_overflow_check_element for a description of the check */ static void memp_overflow_check_all(void) { u16_t i, j; struct memp *p; //遍历每个pool的每个节点去查找溢出 p = LWIP_MEM_ALIGN(memp_memory); for (i = 0; i < MEMP_MAX; ++i) { p = p; for (j = 0; j < memp_num[i]; ++j) { memp_overflow_check_element(p, memp_sizes[i]); p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); } } } /** * Initialize the restricted areas of all memp elements in every pool. */ static void memp_overflow_init(void) { u16_t i, j; struct memp *p; u8_t *m; // 遍历每个pool中的每个节点,在给上层分配的内存前后区域填充0xcd p = LWIP_MEM_ALIGN(memp_memory); for (i = 0; i < MEMP_MAX; ++i) { p = p; for (j = 0; j < memp_num[i]; ++j) { #if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 // 在前方区域填充填充符0xcd。 m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED); #endif #if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 // 在后方区域填充填充符0xcd。 m = (u8_t*)p + MEMP_SIZE + memp_sizes[i]; memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED); #endif p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); // p指向下一个节点 } } } #endif /* MEMP_OVERFLOW_CHECK */ /** * Initialize this module. * * Carves out memp_memory into linked lists for each pool-type. */ void memp_init(void) { struct memp *memp; u16_t i, j; for (i = 0; i < MEMP_MAX; ++i) { MEMP_STATS_AVAIL(used, i, 0); MEMP_STATS_AVAIL(max, i, 0); MEMP_STATS_AVAIL(err, i, 0); MEMP_STATS_AVAIL(avail, i, memp_num[i]); } memp = LWIP_MEM_ALIGN(memp_memory); // 对齐内存数组memp_memory,memp指向对齐了的地址 /* for every pool: */ for (i = 0; i < MEMP_MAX; ++i) { memp_tab[i] = NULL; // 初始化节点头指针为NULL /* create a linked list of memp elements */ for (j = 0; j < memp_num[i]; ++j) { memp->next = memp_tab[i]; // 往链表头部插入新的节点 memp_tab[i] = memp; // 更新头指针 memp = (struct memp *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i] #if MEMP_OVERFLOW_CHECK + MEMP_SANITY_REGION_AFTER_ALIGNED // 判断overflow时使用的内存区域之后的填充符的对齐大小 #endif ); // memp指向下一个节点(MEMP_SIZE为struct所需要的size,可以优化,详见其定义。memp_sizes[i]为当前内存池的size) } } #if MEMP_OVERFLOW_CHECK memp_overflow_init(); // 初始化判断overflow所需要的工作 /* check everything a first time to see if it worked */ memp_overflow_check_all(); // 第一次的时候检查一下所有pool的每个节点是否有溢出 #endif /* MEMP_OVERFLOW_CHECK */ } /** * Get an element from a specific pool. * * @param type the pool to get an element from * * the debug version has two more parameters: * @param file file name calling this function * @param line number of line where this function is called * * @return a pointer to the allocated memory or a NULL pointer on error */ void * #if !MEMP_OVERFLOW_CHECK memp_malloc(memp_t type) #else memp_malloc_fn(memp_t type, const char* file, const int line) #endif { struct memp *memp; SYS_ARCH_DECL_PROTECT(old_level); // 声明锁 LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;); SYS_ARCH_PROTECT(old_level); // 获得锁 #if MEMP_OVERFLOW_CHECK >= 2 memp_overflow_check_all(); #endif /* MEMP_OVERFLOW_CHECK >= 2 */ memp = memp_tab[type]; // 得到头结点 if (memp != NULL) { memp_tab[type] = memp->next; // 更新头指针 #if MEMP_OVERFLOW_CHECK memp->next = NULL; memp->file = file; memp->line = line; #endif /* MEMP_OVERFLOW_CHECK */ MEMP_STATS_INC_USED(used, type); LWIP_ASSERT("memp_malloc: memp properly aligned", ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0); memp = (struct memp*)((u8_t*)memp + MEMP_SIZE); // memp指向用户需要的内存区域 } else { LWIP_DEBUGF(MEMP_DEBUG | 2, ("memp_malloc: out of memory in pool %s/n", memp_desc[type])); MEMP_STATS_INC(err, type); } SYS_ARCH_UNPROTECT(old_level); // 释放锁 return memp; } /** * Put an element back into its pool. * * @param type the pool where to put mem * @param mem the memp element to free */ void memp_free(memp_t type, void *mem) { struct memp *memp; SYS_ARCH_DECL_PROTECT(old_level); // 声明锁 if (mem == NULL) { return; } LWIP_ASSERT("memp_free: mem properly aligned", ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0); memp = (struct memp *)((u8_t*)mem - MEMP_SIZE); // memp指向节点地址 SYS_ARCH_PROTECT(old_level); // 获得锁 #if MEMP_OVERFLOW_CHECK #if MEMP_OVERFLOW_CHECK >= 2 memp_overflow_check_all(); // 检查所有元素溢出 #else memp_overflow_check_element(memp, memp_sizes[type]); // 检查释放节点元素溢出 #endif /* MEMP_OVERFLOW_CHECK >= 2 */ #endif /* MEMP_OVERFLOW_CHECK */ MEMP_STATS_DEC(used, type); //重新插入到链表头部 memp->next = memp_tab[type]; memp_tab[type] = memp; #if MEMP_SANITY_CHECK LWIP_ASSERT("memp sanity", memp_sanity()); // 循环检测 #endif /* MEMP_SANITY_CHECK */ SYS_ARCH_UNPROTECT(old_level); // 释放锁 } #endif /* MEMP_MEM_MALLOC */
memp_std.h
/* * SETUP: Make sure we define everything we will need. * * We have create three types of pools: * 1) MEMPOOL - standard pools 标准内存池 * 2) MALLOC_MEMPOOL - to be used by mem_malloc in mem.c 给mem.c里的mem_malloc用的内存池 * 3) PBUF_MEMPOOL - a mempool of pbuf's, so include space for the pbuf struct pbuf使用的内存池 * * If the include'r doesn't require any special treatment of each of the types * above, then will declare #2 & #3 to be just standard mempools. */ #ifndef LWIP_MALLOC_MEMPOOL /* This treats "malloc pools" just like any other pool. The pools are a little bigger to provide 'size' as the amount of user data. */ /* malloc定义的不同size的内存池,具体使用方法在mem.c中有介绍: * Define three pools with sizes 256, 512, and 1512 bytes * LWIP_MALLOC_MEMPOOL_START * LWIP_MALLOC_MEMPOOL(20, 256) * LWIP_MALLOC_MEMPOOL(10, 512) * LWIP_MALLOC_MEMPOOL(5, 1512) * LWIP_MALLOC_MEMPOOL_END 具体工作原理请结合以上的几个宏的定义一起看,在memp.h中 */ #define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, (size + sizeof(struct memp_malloc_helper)), "MALLOC_"#size) #define LWIP_MALLOC_MEMPOOL_START #define LWIP_MALLOC_MEMPOOL_END #endif /* LWIP_MALLOC_MEMPOOL */ #ifndef LWIP_PBUF_MEMPOOL /* This treats "pbuf pools" just like any other pool. * Allocates buffers for a pbuf struct AND a payload size */ /* 因为已知pbuf用的内存池,所以第三个参数换成payload,表示除了pbuf的payload的大小*/ #define LWIP_PBUF_MEMPOOL(name, num, payload, desc) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc) #endif /* LWIP_PBUF_MEMPOOL */ /* lwip中各种内存池的描述 * A list of internal pools used by LWIP. * * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description) * creates a pool name MEMP_pool_name. description is used in stats.c */ #if LWIP_RAW LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB") #endif /* LWIP_RAW */ #if LWIP_UDP LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB") #endif /* LWIP_UDP */ #if LWIP_TCP LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB") LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN") LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG") #endif /* LWIP_TCP */ #if IP_REASSEMBLY LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA") #endif /* IP_REASSEMBLY */ #if LWIP_NETCONN LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF") LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN") #endif /* LWIP_NETCONN */ #if NO_SYS==0 LWIP_MEMPOOL(TCPIP_MSG_API, MEMP_NUM_TCPIP_MSG_API, sizeof(struct tcpip_msg), "TCPIP_MSG_API") LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), "TCPIP_MSG_INPKT") #endif /* NO_SYS==0 */ #if ARP_QUEUEING LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE") #endif /* ARP_QUEUEING */ #if LWIP_IGMP LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP") #endif /* LWIP_IGMP */ #if NO_SYS==0 LWIP_MEMPOOL(SYS_TIMEOUT, MEMP_NUM_SYS_TIMEOUT, sizeof(struct sys_timeo), "SYS_TIMEOUT") #endif /* NO_SYS==0 */ /* 给pbuf使用的内存池 * A list of pools of pbuf's used by LWIP. * * LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description) * creates a pool name MEMP_pool_name. description is used in stats.c * This allocates enough space for the pbuf struct and a payload. * (Example: pbuf_payload_size=0 allocates only size for the struct) */ LWIP_PBUF_MEMPOOL(PBUF, MEMP_NUM_PBUF, 0, "PBUF_REF/ROM") LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL") /* 用户可以定义custom的内存池 * Allow for user-defined pools; this must be explicitly set in lwipopts.h * since the default is to NOT look for lwippools.h */ #if MEMP_USE_CUSTOM_POOLS #include "lwippools.h" #endif /* MEMP_USE_CUSTOM_POOLS */ /* 防止多次定义错误,因为memp.c使用#include memp_std.h并且定义以下的宏的技巧,所以每次需要undef这些宏 * REQUIRED CLEANUP: Clear up so we don't get "multiply defined" error later * (#undef is ignored for something that is not defined) */ #undef LWIP_MEMPOOL #undef LWIP_MALLOC_MEMPOOL #undef LWIP_MALLOC_MEMPOOL_START #undef LWIP_MALLOC_MEMPOOL_END #undef LWIP_PBUF_MEMPOOL
mem.h
/* * 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 * */ #ifndef __LWIP_MEM_H__ #define __LWIP_MEM_H__ #include "lwip/opt.h" #ifdef __cplusplus extern "C" { #endif #if MEM_LIBC_MALLOC #include /* for size_t */ typedef size_t mem_size_t; /* aliases for C library malloc() */ #define mem_init() /* in case C library malloc() needs extra protection, * allow these defines to be overridden. */ #ifndef mem_free #define mem_free free #endif #ifndef mem_malloc #define mem_malloc malloc #endif #ifndef mem_calloc #define mem_calloc calloc #endif #ifndef mem_realloc static void *mem_realloc(void *mem, mem_size_t size) { LWIP_UNUSED_ARG(size); return mem; } #endif #else /* MEM_LIBC_MALLOC */ /* MEM_SIZE would have to be aligned, but using 64000 here instead of * 65535 leaves some room for alignment... */ #if MEM_SIZE > 64000l typedef u32_t mem_size_t; #else typedef u16_t mem_size_t; #endif /* MEM_SIZE > 64000 */ #if MEM_USE_POOLS /** mem_init is not used when using pools instead of a heap */ #define mem_init() /** mem_realloc is not used when using pools instead of a heap: we can't free part of a pool element and don't want to copy the rest */ #define mem_realloc(mem, size) (mem) #else /* MEM_USE_POOLS */ /* lwIP alternative malloc */ void mem_init(void); void *mem_realloc(void *mem, mem_size_t size); #endif /* MEM_USE_POOLS */ void *mem_malloc(mem_size_t size); void *mem_calloc(mem_size_t count, mem_size_t size); void mem_free(void *mem); #endif /* MEM_LIBC_MALLOC */ #ifndef LWIP_MEM_ALIGN_SIZE #define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1)) #endif #ifndef LWIP_MEM_ALIGN #define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1))) #endif #ifdef __cplusplus } #endif #endif /* __LWIP_MEM_H__ */
mem.c
/** * @file * Dynamic memory manager * * This is a lightweight replacement for the standard C library malloc(). * * If you want to use the standard C library malloc() instead, define * MEM_LIBC_MALLOC to 1 in your lwipopts.h * * To let mem_malloc() use pools (prevents fragmentation and is much faster than * a heap but might waste some memory), define MEM_USE_POOLS to 1, define * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list * of pools like this (more pools can be added between _START and _END): * * Define three pools with sizes 256, 512, and 1512 bytes * LWIP_MALLOC_MEMPOOL_START * LWIP_MALLOC_MEMPOOL(20, 256) * LWIP_MALLOC_MEMPOOL(10, 512) * LWIP_MALLOC_MEMPOOL(5, 1512) * LWIP_MALLOC_MEMPOOL_END */ /* * 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 * Simon Goldschmidt * */ #include "lwip/opt.h" #if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */ #include "lwip/def.h" #include "lwip/mem.h" #include "lwip/sys.h" #include "lwip/stats.h" #include #if MEM_USE_POOLS /* lwIP head implemented with different sized pools */ /** * Allocate memory: determine the smallest pool that is big enough * to contain an element of 'size' and get an element from that pool. * * @param size the size in bytes of the memory needed * @return a pointer to the allocated memory or NULL if the pool is empty */ void * mem_malloc(mem_size_t size) { struct memp_malloc_helper *element; memp_t poolnr; mem_size_t required_size = size + sizeof(struct memp_malloc_helper); // 所需要的size // 遍历每一个不同size的pool,查找可以容纳required_size的pool for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr++) { #if MEM_USE_POOLS_TRY_BIGGER_POOL again: #endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ /* is this pool big enough to hold an element of the required size plus a struct memp_malloc_helper that saves the pool this element came from? */ if (required_size <= memp_sizes[poolnr]) { //找到了可以容纳required_size的pool break; } } if (poolnr > MEMP_POOL_LAST) { LWIP_ASSERT("mem_malloc(): no pool is that big!", 0); return NULL; } // 从pool里面申请一块内存 element = (struct memp_malloc_helper*)memp_malloc(poolnr); if (element == NULL) { /* No need to DEBUGF or ASSERT: This error is already taken care of in memp.c */ #if MEM_USE_POOLS_TRY_BIGGER_POOL /** Try a bigger pool if this one is empty! */ if (poolnr < MEMP_POOL_LAST) { poolnr++; goto again; } #endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ return NULL; } /* save the pool number this element came from */ element->poolnr = poolnr; // 保存pool的number /* and return a pointer to the memory directly after the struct memp_malloc_helper */ element++; // 移动element指针,越过struct memp_malloc_helper return element; } /** * Free memory previously allocated by mem_malloc. Loads the pool number * and calls memp_free with that pool number to put the element back into * its pool * * @param rmem the memory element to free */ void mem_free(void *rmem) { struct memp_malloc_helper *hmem = (struct memp_malloc_helper*)rmem; LWIP_ASSERT("rmem != NULL", (rmem != NULL)); LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); /* get the original struct memp_malloc_helper */ /* 回退sizeof(memp_malloc_helper),指向struct memp_malloc_helper */ hmem--; LWIP_ASSERT("hmem != NULL", (hmem != NULL)); LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem))); LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX)); /* and put it in the pool we saved earlier */ /* 释放回相应的pool中去 */ memp_free(hmem->poolnr, hmem); } #else /* MEM_USE_POOLS */ /* lwIP replacement for your libc malloc() */ /* lwip实现了一个轻量级的heap的内存管理器*/ /** * The heap is made up as a list of structs of this type. * This does not have to be aligned since for getting its size, * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes. */ /* 采用索引双链表的形式来管理heap */ /* 节点结构 */ struct mem { /** index (-> ram[next]) of the next struct */ mem_size_t next; // 下一个struct的索引 /** index (-> ram[next]) of the next struct */ mem_size_t prev; // 上一个struct的索引 /** 1: this area is used; 0: this area is unused */ u8_t used; // 是否被使用 }; /** 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 #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) #define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem)) #define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE) /** the heap. we need one struct mem at the end and some room for alignment */ /* 其中MEM_SIZE_ALIGNED -1用来对齐ram_heap的地址, MEM_ALIGNMENT用来表示大小, ram_end指向的最后一个节点需要SIZEOF_STRUCT_MEM, +1需要 */ static u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT]; /** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */ static u8_t *ram; // ram_heap对齐后的地址 /** the last entry, always unused! */ static struct mem *ram_end; // 指向ram_heap最后一个节点 /** pointer to the lowest free block, this is used for faster search */ static struct mem *lfree; // 指向最低的空闲节点块,被用来加快搜索速度 /** concurrent access protection */ static sys_sem_t mem_sem; #if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT static volatile u8_t mem_free_count; /* Allow mem_free from other (e.g. interrupt) context */ #define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free) #define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free) #define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free) #define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc) #define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc) #define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc) #else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ /* Protect the heap only by using a semaphore */ #define LWIP_MEM_FREE_DECL_PROTECT() #define LWIP_MEM_FREE_PROTECT() sys_arch_sem_wait(mem_sem, 0) #define LWIP_MEM_FREE_UNPROTECT() sys_sem_signal(mem_sem) /* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */ #define LWIP_MEM_ALLOC_DECL_PROTECT() #define LWIP_MEM_ALLOC_PROTECT() #define LWIP_MEM_ALLOC_UNPROTECT() #endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ /** * "Plug holes" by combining adjacent empty struct mems. * After this function is through, there should not exist * one empty struct mem pointing to another empty struct mem. * * @param mem this points to a struct mem which just has been freed * @internal this function is only called by mem_free() and mem_realloc() * * This assumes access to the heap is protected by the calling function * already. */ /* 合并前面后面的空间节点,当节点free的时候 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_ALIGNED", mem->next <= MEM_SIZE_ALIGNED); nmem = (struct mem *)&ram[mem->next]; // 合并后面的空闲节点 if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) { /* if mem->next is unused and not end of ram, combine mem and mem->next */ 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 mem->prev is unused, combine mem and mem->prev */ if (lfree == mem) { lfree = pmem; } pmem->next = mem->next; ((struct mem *)&ram[mem->next])->prev = (u8_t *)pmem - ram; } } /** * Zero the heap and initialize start, end and lowest-free */ void mem_init(void) { struct mem *mem; LWIP_ASSERT("Sanity check alignment", (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0); /* align the heap */ ram = LWIP_MEM_ALIGN(ram_heap); // ram为对齐后的地址 /* initialize the start of the heap */ mem = (struct mem *)ram; mem->next = MEM_SIZE_ALIGNED; // 设置后置索引为MEM_SIZE_ALIGNED 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; // 设置后置索引为MEM_SIZE_ALIGNED ram_end->prev = MEM_SIZE_ALIGNED; // 设置前置索引为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); } /** * Put a struct mem back on the heap * * @param rmem is the data portion of a struct mem as returned by a previous * call to mem_malloc() */ void mem_free(void *rmem) { struct mem *mem; LWIP_MEM_FREE_DECL_PROTECT(); if (rmem == NULL) { LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | 2, ("mem_free(p == NULL) was called./n")); return; } LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0); 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) { SYS_ARCH_DECL_PROTECT(lev); LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_free: illegal memory/n")); /* protect mem stats from concurrent access */ SYS_ARCH_PROTECT(lev); MEM_STATS_INC(illegal); SYS_ARCH_UNPROTECT(lev); return; } /* protect the heap from concurrent access */ LWIP_MEM_FREE_PROTECT(); /* 得到相关的节点地址 */ /* Get the corresponding struct mem ... */ mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); /* ... which has to be in a used state ... */ LWIP_ASSERT("mem_free: mem->used", mem->used); /* ... and is now unused. */ // 设置为空闲 mem->used = 0; if (mem < lfree) { /* the newly freed struct is now the lowest */ lfree = mem; } MEM_STATS_DEC_USED(used, mem->next - ((u8_t *)mem - ram)); /* 将前面和后面的空闲节点整合 */ /* finally, see if prev or next are free also */ plug_holes(mem); #if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT mem_free_count = 1; #endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ LWIP_MEM_FREE_UNPROTECT(); } /** * In contrast to its name, mem_realloc can only shrink memory, not expand it. * Since the only use (for now) is in pbuf_realloc (which also can only shrink), * this shouldn't be a problem! * * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked * @param newsize required size after shrinking (needs to be smaller than or * equal to the previous size) * @return for compatibility reasons: is always == rmem, at the moment * or NULL if newsize is > old size, in which case rmem is NOT touched * or freed! */ void * mem_realloc(void *rmem, mem_size_t newsize) { mem_size_t size; mem_size_t ptr, ptr2; struct mem *mem, *mem2; /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */ LWIP_MEM_FREE_DECL_PROTECT(); /* Expand the size of the allocated memory region so that we can adjust for alignment. */ newsize = LWIP_MEM_ALIGN_SIZE(newsize); if(newsize < MIN_SIZE_ALIGNED) { /* every data block must be at least MIN_SIZE_ALIGNED long */ newsize = MIN_SIZE_ALIGNED; } if (newsize > MEM_SIZE_ALIGNED) { return NULL; } 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) { SYS_ARCH_DECL_PROTECT(lev); LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_realloc: illegal memory/n")); /* protect mem stats from concurrent access */ SYS_ARCH_PROTECT(lev); MEM_STATS_INC(illegal); SYS_ARCH_UNPROTECT(lev); return rmem; } /* Get the corresponding struct mem ... */ mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); /* ... and its offset pointer */ ptr = (u8_t *)mem - ram; size = mem->next - ptr - SIZEOF_STRUCT_MEM; LWIP_ASSERT("mem_realloc can only shrink memory", newsize <= size); // 不支持扩大 if (newsize > size) { /* not supported */ return NULL; } // 如果不变,则无需改变 if (newsize == size) { /* No change in size, simply return */ return rmem; } /* protect the heap from concurrent access */ LWIP_MEM_FREE_PROTECT(); MEM_STATS_DEC_USED(used, (size - newsize)); // mem2现在指向mem的下一个节点 mem2 = (struct mem *)&ram[mem->next]; // 如果mem2空闲,则切割mem到SIZEOF_STRUCT_MEM + newsize,然后合并多余的和mem2 if(mem2->used == 0) { /* The next struct is unused, we can simply move it at little */ mem_size_t next; /* remember the old next pointer */ next = mem2->next; /* create new struct mem which is moved directly after the shrinked mem */ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; if (lfree == mem2) { lfree = (struct mem *)&ram[ptr2]; } mem2 = (struct mem *)&ram[ptr2]; mem2->used = 0; /* restore the next pointer */ mem2->next = next; /* link it back to mem */ mem2->prev = ptr; /* link mem to it */ mem->next = ptr2; /* last thing to restore linked list: as we have moved mem2, * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not * the end of the heap */ if (mem2->next != MEM_SIZE_ALIGNED) { ((struct mem *)&ram[mem2->next])->prev = ptr2; } /* no need to plug holes, we've already done that */ } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) { // 如果mem2不空闲,并且有空间容纳一个新的空闲节点,则建立空闲节点 /* Next struct is used but there's room for another struct mem with * at least MIN_SIZE_ALIGNED of data. * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED'). * @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 = ptr + SIZEOF_STRUCT_MEM + newsize; mem2 = (struct mem *)&ram[ptr2]; if (mem2 < lfree) { lfree = mem2; } mem2->used = 0; mem2->next = mem->next; mem2->prev = ptr; mem->next = ptr2; if (mem2->next != MEM_SIZE_ALIGNED) { ((struct mem *)&ram[mem2->next])->prev = ptr2; } /* the original mem->next is used, so no need to plug holes! */ } /* else { // 没空间建新的空闲节点 next struct mem is used but size between mem and mem2 is not big enough to create another struct mem -> don't do anyhting. -> the remaining space stays unused since it is too small } */ #if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT mem_free_count = 1; #endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ LWIP_MEM_FREE_UNPROTECT(); return rmem; } /** * Adam's mem_malloc() plus solution for bug #17922 * Allocate a block of memory with a minimum of 'size' bytes. * * @param size is the minimum size of the requested block in bytes. * @return pointer to allocated memory or NULL if no free memory was found. * * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT). */ 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; } /* Expand the size of the allocated memory region so that we can adjust for alignment. */ size = LWIP_MEM_ALIGN_SIZE(size); if(size < MIN_SIZE_ALIGNED) { /* every data block must be at least MIN_SIZE_ALIGNED long */ size = MIN_SIZE_ALIGNED; } if (size > MEM_SIZE_ALIGNED) { 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. */ /* 从最低的空闲节点块开始查找heap上的每个可以容纳size的节点 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 */ // 如果节点还未被使用,并且节点的空间可容纳size + SIZEOF_STRUCT_MEM 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 */ // 如果节点的空间可容纳size + 2 * SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED, 这样表示有可以把剩余的空间新开一个空闲节点的空间 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 */ // 利用节点的剩余空间创建一个新的空闲节点mem2, 插入在mem和mem->next之间 ptr2 = ptr + SIZEOF_STRUCT_MEM + size; /* create mem2 struct */ mem2 = (struct mem *)&ram[ptr2]; mem2->used = 0; mem2->next = mem->next; mem2->prev = ptr; /* and insert it between mem and mem->next */ mem->next = ptr2; mem->used = 1; if (mem2->next != MEM_SIZE_ALIGNED) { ((struct mem *)&ram[mem2->next])->prev = ptr2; } MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM)); } else { /* (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! */ // 节点剩余空间不够建一个新的free节点,就把不切割了,直接把mem节点全部占用了 mem->used = 1; MEM_STATS_INC_USED(used, mem->next - ((u8_t *)mem - ram)); } // 如果lfree已经用掉,即mem == lfree,则更新lfree if (mem == lfree) { /* 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; } #endif /* MEM_USE_POOLS */ /** * Contiguously allocates enough space for count objects that are size bytes * of memory each and returns a pointer to the allocated memory. * * The allocated memory is filled with bytes of value zero. * * @param count number of objects to allocate * @param size size of the objects to allocate * @return pointer to allocated memory / NULL pointer if there is an error */ void *mem_calloc(mem_size_t count, mem_size_t size) { void *p; /* allocate 'count' objects of size 'size' */ p = mem_malloc(count * size); if (p) { /* zero the memory */ memset(p, 0, count * size); } return p; } #endif /* !MEM_LIBC_MALLOC */
终于讲完了,希望有兴趣的同学来联系我,[email protected],下篇文章会讲链路层模块,敬请期待。