| CMake | ||
| Core | ||
| Drivers | ||
| Middlewares | ||
| User | ||
| .cproject | ||
| .gitignore | ||
| .gitmodules | ||
| .mxproject | ||
| .project | ||
| CMakeLists.txt | ||
| README.md | ||
| stm32h743_player.ioc | ||
| STM32H743.svd | ||
| STM32H743IITX_FLASH.ld | ||
| STM32H743IITX_RAM.ld | ||
内存规划
CCMRAM
- 用于系统默认的bss与data区
- 用于系统默认的栈区
- 用于FreeRTOS的动态内存分配与LVGL的动态内存分配
- 用于解码器等算法的缓存区
AXIRAM
- 用做用户程序的堆区
- 用于newlib的堆区
APBRAM
- 用于低速外设的DMA缓冲区,如USART、SAI等
内存管理函数移植
为了保证C语言运行库的线程安全性,标准的libc由MMU通过为每个进程分配独立的内存空间实现进程安全性,而线程安全性则通过在每一个线程上下文中分别保存该线程内libc所使用的上下文来实现,方法为在全局范围维护一个指向当前线程所使用的libc上下文的指针,并在调度器切换线程时将该指针指向正在运行任务的libc上下文。arm-none-eabi-gcc等目标为裸机的编译器,通常使用精简的newlib替代标准的libc作为其默认的C语言运行库,从而适应MCU等资源受限的应用。裸机环境中因不存在MMU而不存在进程的概念,而线程则退化为RTOS的任务,因此仍需保证C运行库在RTOS任务间的线程安全性。arm-none-eabi-gcc的编译选项中,默认启用了newlib 4引入的一种特殊的锁机制_RETARGETABLE_LOCKING,即可重定向锁机制。该机制避免了使用标准C语言库中基于保存的newlib上下文实现的锁__malloc_lock(struct _reent *),取而代之的则是由用户提供如下的一系列的mutex与锁函数,分别为newlib中各个部分添加更轻量级的锁。
struct __lock __lock___sfp_recursive_mutex;
struct __lock __lock___atexit_recursive_mutex;
struct __lock __lock___at_quick_exit_mutex;
struct __lock __lock___malloc_recursive_mutex;
struct __lock __lock___env_recursive_mutex;
struct __lock __lock___tz_mutex;
struct __lock __lock___dd_hash_mutex;
struct __lock __lock___arc4random_mutex;
void __retarget_lock_init (_LOCK_T * <[lock_ptr]>);
void __retarget_lock_init_recursive (_LOCK_T * <[lock_ptr]>);
void __retarget_lock_close (_LOCK_T <[lock]>);
void __retarget_lock_close_recursive (_LOCK_T <[lock]>);
void __retarget_lock_acquire (_LOCK_T <[lock]>);
void __retarget_lock_acquire_recursive (_LOCK_T <[lock]>);
int __retarget_lock_try_acquire (_LOCK_T <[lock]>);
int __retarget_lock_try_acquire_recursive (_LOCK_T <[lock]>);
void __retarget_lock_release (_LOCK_T <[lock]>);
void __retarget_lock_release_recursive (_LOCK_T <[lock]>);
{{< admonition type=info title="其他newlib中的函数" open=true >}}
即使_RETARGETABLE_LOCKING为newlib实现了一种更加轻量的锁机制,但当使用到newlib中需要使用到除锁以外的上下文的函数,例如
{{< /admonition >}}
在不使用_RETARGETABLE_LOCKING时,malloc的调用的流程如下:
malloc调用可重入版本的_malloc_r函数;_malloc_r函数调用__malloc_lock函数为内存分配器上锁;
newlib-cygwin\newlib\libc\stdlib\malloc.c
https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=winsup/cygwin/passwd.cc#l190
使用newlib内置的内存分配函数编译工程,可以从连接器输出的map文件中看到
.text._malloc_r
0x000000000801c524 0x474 D:/Programs/STM32CubeCLT/GNU-tools-for-STM32/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/lib/thumb/v7e-m+dp/hard\libg_nano.a(libc_a-mallocr.o)
0x000000000801c524 _malloc_r
_malloc_r函数是来自newlib_nano中的mallocr.o目标文件。
set(TARGET_BUILD_NEWLIB_OPTIONS
-Wl,-wrap,_malloc_r -Wl,-wrap,_free_r -Wl,-wrap,_realloc_r # wrap newlib memory allocator functions
)
newlib源码中newlib-cygwin\newlib\libc\stdlib\malloc.c中有
void *
malloc (size_t nbytes) /* get a block */
{
return _malloc_r (_REENT, nbytes);
}
void
free (void *aptr)
{
_free_r (_REENT, aptr);
}
因此实际是调用的_malloc_r与_free_r函数。这两个函数的原型在newlib-cygwin\newlib\libc\stdlib\_mallocr.c中。例如,_malloc_r的原型为
#if __STD_C
Void_t* mALLOc(RARG size_t bytes)
#else
Void_t* mALLOc(RARG bytes) RDECL size_t bytes;
#endif
{
#ifdef MALLOC_PROVIDED
return malloc (bytes); // Make sure that the pointer returned by malloc is returned back.
#else
mchunkptr victim; /* inspected/selected chunk */
INTERNAL_SIZE_T victim_size; /* its size */
int idx; /* index for bin traversal */
mbinptr bin; /* associated bin */
mchunkptr remainder; /* remainder from a split */
long remainder_size; /* its size */
int remainder_index; /* its bin index */
unsigned long block; /* block traverser bit */
int startidx; /* first bin of a traversed block */
mchunkptr fwd; /* misc temp for linking */
mchunkptr bck; /* misc temp for linking */
mbinptr q; /* misc temp */
INTERNAL_SIZE_T nb = request2size(bytes); /* padded request size; */
/* Check for overflow and just fail, if so. */
if (nb > INT_MAX || nb < bytes)
{
RERRNO = ENOMEM;
return 0;
}
MALLOC_LOCK;
/* Check for exact match in a bin */
if (is_small_request(nb)) /* Faster version for small requests */
{
idx = smallbin_index(nb);
/* No traversal or size check necessary for small bins. */
q = bin_at(idx);
victim = last(q);
#if MALLOC_ALIGN != 16
/* Also scan the next one, since it would have a remainder < MINSIZE */
if (victim == q)
{
q = next_bin(q);
victim = last(q);
}
#endif
if (victim != q)
{
victim_size = chunksize(victim);
unlink(victim, bck, fwd);
set_inuse_bit_at_offset(victim, victim_size);
check_malloced_chunk(victim, nb);
MALLOC_UNLOCK;
return chunk2mem(victim);
}
idx += 2; /* Set for bin scan below. We've already scanned 2 bins. */
}
else
{
idx = bin_index(nb);
bin = bin_at(idx);
for (victim = last(bin); victim != bin; victim = victim->bk)
{
victim_size = chunksize(victim);
remainder_size = long_sub_size_t(victim_size, nb);
if (remainder_size >= (long)MINSIZE) /* too big */
{
--idx; /* adjust to rescan below after checking last remainder */
break;
}
else if (remainder_size >= 0) /* exact fit */
{
unlink(victim, bck, fwd);
set_inuse_bit_at_offset(victim, victim_size);
check_malloced_chunk(victim, nb);
MALLOC_UNLOCK;
return chunk2mem(victim);
}
}
++idx;
}
/* Try to use the last split-off remainder */
if ( (victim = last_remainder->fd) != last_remainder)
{
victim_size = chunksize(victim);
remainder_size = long_sub_size_t(victim_size, nb);
if (remainder_size >= (long)MINSIZE) /* re-split */
{
remainder = chunk_at_offset(victim, nb);
set_head(victim, nb | PREV_INUSE);
link_last_remainder(remainder);
set_head(remainder, remainder_size | PREV_INUSE);
set_foot(remainder, remainder_size);
check_malloced_chunk(victim, nb);
MALLOC_UNLOCK;
return chunk2mem(victim);
}
clear_last_remainder;
if (remainder_size >= 0) /* exhaust */
{
set_inuse_bit_at_offset(victim, victim_size);
check_malloced_chunk(victim, nb);
MALLOC_UNLOCK;
return chunk2mem(victim);
}
/* Else place in bin */
frontlink(victim, victim_size, remainder_index, bck, fwd);
}
/*
If there are any possibly nonempty big-enough blocks,
search for best fitting chunk by scanning bins in blockwidth units.
*/
if ( (block = idx2binblock(idx)) <= binblocks)
{
/* Get to the first marked block */
if ( (block & binblocks) == 0)
{
/* force to an even block boundary */
idx = (idx & ~(BINBLOCKWIDTH - 1)) + BINBLOCKWIDTH;
block <<= 1;
while ((block & binblocks) == 0)
{
idx += BINBLOCKWIDTH;
block <<= 1;
}
}
/* For each possibly nonempty block ... */
for (;;)
{
startidx = idx; /* (track incomplete blocks) */
q = bin = bin_at(idx);
/* For each bin in this block ... */
do
{
/* Find and use first big enough chunk ... */
for (victim = last(bin); victim != bin; victim = victim->bk)
{
victim_size = chunksize(victim);
remainder_size = long_sub_size_t(victim_size, nb);
if (remainder_size >= (long)MINSIZE) /* split */
{
remainder = chunk_at_offset(victim, nb);
set_head(victim, nb | PREV_INUSE);
unlink(victim, bck, fwd);
link_last_remainder(remainder);
set_head(remainder, remainder_size | PREV_INUSE);
set_foot(remainder, remainder_size);
check_malloced_chunk(victim, nb);
MALLOC_UNLOCK;
return chunk2mem(victim);
}
else if (remainder_size >= 0) /* take */
{
set_inuse_bit_at_offset(victim, victim_size);
unlink(victim, bck, fwd);
check_malloced_chunk(victim, nb);
MALLOC_UNLOCK;
return chunk2mem(victim);
}
}
bin = next_bin(bin);
#if MALLOC_ALIGN == 16
if (idx < MAX_SMALLBIN)
{
bin = next_bin(bin);
++idx;
}
#endif
} while ((++idx & (BINBLOCKWIDTH - 1)) != 0);
/* Clear out the block bit. */
do /* Possibly backtrack to try to clear a partial block */
{
if ((startidx & (BINBLOCKWIDTH - 1)) == 0)
{
binblocks &= ~block;
break;
}
--startidx;
q = prev_bin(q);
} while (first(q) == q);
/* Get to the next possibly nonempty block */
if ( (block <<= 1) <= binblocks && (block != 0) )
{
while ((block & binblocks) == 0)
{
idx += BINBLOCKWIDTH;
block <<= 1;
}
}
else
break;
}
}
/* Try to use top chunk */
/* Require that there be a remainder, ensuring top always exists */
remainder_size = long_sub_size_t(chunksize(top), nb);
if (chunksize(top) < nb || remainder_size < (long)MINSIZE)
{
#if HAVE_MMAP
/* If big and would otherwise need to extend, try to use mmap instead */
if ((unsigned long)nb >= (unsigned long)mmap_threshold &&
(victim = mmap_chunk(nb)) != 0)
{
MALLOC_UNLOCK;
return chunk2mem(victim);
}
#endif
/* Try to extend */
malloc_extend_top(RCALL nb);
remainder_size = long_sub_size_t(chunksize(top), nb);
if (chunksize(top) < nb || remainder_size < (long)MINSIZE)
{
MALLOC_UNLOCK;
return 0; /* propagate failure */
}
}
victim = top;
set_head(victim, nb | PREV_INUSE);
top = chunk_at_offset(victim, nb);
set_head(top, remainder_size | PREV_INUSE);
check_malloced_chunk(victim, nb);
MALLOC_UNLOCK;
return chunk2mem(victim);
#endif /* MALLOC_PROVIDED */
}
#endif /* DEFINE_MALLOC */
其中,在已定义__STD_C时,函数原型中的RARG展开为struct _reent *reent_ptr,,因此完整的函数原型为
void* mALLOc(struct _reent *reent_ptr, size_t bytes);
其中,第一个参数为使函数可重入的锁,第二个参数则为常规malloc中要申请的内存大小。