gpt4 book ai didi

c - 我应该使堆栈段变大还是堆段变大?

转载 作者:太空宇宙 更新时间:2023-11-04 00:17:10 26 4
gpt4 key购买 nike

我正在为内存非常有限的微处理器设计程序,我必须在不同的功能中使用“大量”内存。我不能有大的栈段,堆段,数据段,我必须选择哪个变大哪个变小。我总共有大约 32KB,

我为文本段使用了大约 20KB,其余部分为 12KB。我需要一个 4KB 的缓冲区来传递给不同的函数(SPI Flash 扇区大小)。应该在哪里初始化那个大缓冲区?

所以我的选择是:

1) 如果我在函数的开头声明缓冲区,则堆栈需要变大

spiflash_read(...)
{
u8 buffer[4096]; // allocated on stack
syscall_read_spi(buffer,...)
}

2) 动态分配,heap需要变大

spiflash_read(...)
{
u8 *buffer = (u8*) malloc(4096); // allocated in heap
syscall_read_spi(buffer,...)
}

3) 静态分配,巨大的缺点是它不能在“SPI 库”之外使用。

static u8 buffer[4096]; // allocated in data section.

spiflash_read(...)
{
syscall_read_spi(buffer,...)
}

我的问题是哪种方法是实现此设计的最佳方式?有人可以解释一下原因吗?

最佳答案

静态分配始终是运行时安全的,因为如果内存不足,链接器会在构建时告诉您,而不是代码在运行时崩溃。但是,除非在执行期间永久需要内存,否则它可能会造成浪费,因为分配的内存不能重复用于多种用途,除非您明确地以这种方式进行编码。

动态内存分配是运行时可检查的——如果您用完了堆,malloc() 将返回一个空指针。然而,您有责任测试返回值,并在必要时释放内存。动态内存块通常是 4 或 8 字节对齐的,并带有堆管理数据开销,这使得它们对于非常小的分配来说效率低下。此外,频繁分配和释放不同大小的 block 会导致堆碎片和内存浪费——这对于“永远在线”的应用程序来说可能是灾难性的。如果您从不打算释放内存,它总是会被分配,并且您先验知道您需要多少内存,那么使用静态分配可能会更好。如果您有库源代码,您可以修改 malloc 以在内存分配失败时立即停止,以避免必须检查每个分配。如果分配大小通常是一些常见大小,则固定 block 分配器而不是标准 malloc() 可能更可取。这将更具确定性,您可以实现使用情况监控以帮助优化 block 大小和每个大小的数量。

堆栈分配是最有效的,因为它会根据需要自动获取和返回内存。然而,它也很少或没有运行时检查支持。通常,当发生堆栈溢出时,代码将不确定地失败 - 并且不一定接近根本原因。一些链接器可以生成堆栈分析输出,通过调用树计算最坏情况下的堆栈使用情况;如果你有那种设施,你应该使用它,但请记住,如果你有一个多线程系统,就会有多个堆栈,你需要检查每个入口点的最坏情况。另外lonker不会分析中断栈的使用情况,你的系统可能有单独的中断栈,也可能共享系统栈。

我解决这个问题的方法当然不是将大型数组或对象放在堆栈上,而是遵循以下过程:

  1. 使用链接器堆栈分析来计算最坏情况下的堆栈使用情况,必要时允许 ISR 使用额外的堆栈。分配那么多堆栈。

  2. 静态分配执行期间所需的所有对象。

  3. 使用链接映射来确定剩余的内存量,将几乎所有内存分配给堆(您的链接器或链接器脚本可能会自动执行此操作,但如果您必须明确设置堆大小,请保留一些未使用的空间,否则每次添加新的静态对象或扩展堆栈时,您都必须调整堆的大小)。从堆中分配所有大型临时对象,并注意释放分配的内存。

如果您的库包含堆诊断函数,您可以在代码中使用它们来监控堆使用情况,以检查您离耗尽有多近。

链接器分析“最坏情况”很可能比您在实践中看到的要大——我永远不会执行最坏情况路径。您可以使用特定字节(比如 0xEE)或模式预填充堆栈,然后在大量测试和操作之后,检查“高潮”标记并以这种方式优化堆栈。谨慎使用此技术;您的测试可能无法涵盖所有​​可预见的情况。

关于c - 我应该使堆栈段变大还是堆段变大?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9709099/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com