stm32h743_player/README.md
2024-03-03 22:46:44 +08:00

385 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 内存规划
## 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`,即[可重定向锁机制](https://sourceware.org/legacy-ml/newlib/2016/msg01165.html)。该机制避免了使用标准C语言库中基于保存的newlib上下文实现的锁`__malloc_lock(struct _reent *)`取而代之的则是由用户提供如下的一系列的mutex与锁函数分别为newlib中各个部分添加更轻量级的锁。
```c
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]>);
```
[22](https://sourceware.org/newlib/libc.html#g_t_005f_005fretarget_005flock_005finit)
{{< admonition type=info title="其他newlib中的函数" open=true >}}
即使`_RETARGETABLE_LOCKING`为newlib实现了一种更加轻量的锁机制但当使用到newlib中需要使用到除锁以外的上下文的函数例如
{{< /admonition >}}
[11](https://sourceware.org/git/?p=newlib-cygwin.git;a=blob_plain;f=newlib/libc/stdlib/_mallocr.c)
在不使用`_RETARGETABLE_LOCKING`时malloc的调用的流程如下
- [`malloc`](https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=newlib/libc/stdlib/malloc.c#l162)调用可重入版本的[`_malloc_r`](https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=newlib/libc/stdlib/_mallocr.c#l162)函数;
- `_malloc_r`函数调用[`__malloc_lock`](https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=newlib/libc/stdlib/mlock.c#l40)函数为内存分配器上锁;
-
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`目标文件。
```cmake
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`中有
```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`的原型为
```c
#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,`,因此完整的函数原型为
```c
void* mALLOc(struct _reent *reent_ptr, size_t bytes);
```
其中,第一个参数为使函数可重入的锁,第二个参数则为常规`malloc`中要申请的内存大小。