浅析lwip中mem_malloc()内存动态申请函数的具体实现
默认MEM_USE_POOLS为0,所以默认将使用如下mem_init初始化内存管理单元,当然lwip还有快速的类似linux中slab的缓冲块,但是这里mem_init初始化的内存管理单元是最通用的内存管理单元,它可以通过函数mem_malloc()申请任意大小的内存,而slab只能申请固定大小的特定内存块[luther.gliethttp],下面就针对mem_init的具体实现做进一步深入讨论[luther.gliethttp]
浅析lwip_init==>mem_init()默认的内存初始化/**
* 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); // 我设的是4字节对齐
if(size < MIN_SIZE_ALIGNED) {
/* every data block must be at least MIN_SIZE_ALIGNED long */
size = MIN_SIZE_ALIGNED; // 一次申请最小不能低于MIN_SIZE_ALIGNED,同样我设的为4字节
}
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.
*/
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 */
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 */
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
*/
// 分配出去size大小的内存之后,是否还能剩下一个能够分配的最小内存所需空闲空间[luther.gliethttp]
// 如果剩下的内存足够,那么为剩下的空闲内存构造mem结构体
ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
/* create mem2 struct */
mem2 = (struct mem *)&ram[ptr2]; // ptr第一次执行为0,所以ptr2递增指向更高空闲内存偏移处,然后为mem2填充结构体数据
mem2->used = 0; // 本段内存空闲
mem2->next = mem->next; // mem2链接被分配出去的mem的next单元,第一次执行的话,mem->next就是MEM_SIZE_ALIGNED
mem2->prev = ptr; // prev指向mem对应的ptr索引偏移[luther.gliethttp]
/* and insert it between mem and mem->next */
mem->next = ptr2; // mem的next指向mem2,这样构成如下链表:mem<->mem2->MEM_SIZE_ALIGNED
mem->used = 1; // 本mem被分配出去了,但是仍在链表上[luther.gliethttp]
if (mem2->next != MEM_SIZE_ALIGNED) { // mem2指向MEM_SIZE_ALIGNED的最后一个空闲内存
((struct mem *)&ram[mem2->next])->prev = ptr2; // 因为上面mem被摘下来了,mem->next也被替换,这里为mem->next的prev赋值
}
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!
*/
// 本mem不能拆分成更小的内存了,所以将mem全部分配出去[luther.gliethttp]
mem->used = 1;
MEM_STATS_INC_USED(used, mem->next - ((u8_t *)mem - ram));
}
if (mem == lfree) {
// 如果分配出去的mem是lfree空闲链表头,那么需要重新计算lfree[luther.gliethttp]
/* Find next free block after mem and update lowest free pointer */
while (lfree->used && lfree != ram_end) { // 将第1个used=0的没有被使用的mem作为lfree.
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;
}