当前位置: 首页 > news >正文

newlib库中malloc函数依赖_sbrk函数,该函数使用链接脚本中的_end符号作为堆的初始地址.

问题:

具体是通过什么传递,从而使newlib得到用户设置的堆的起始地址的?在我的链接脚本文件中并没有看见__heap_start = .;和__heap_end = .;,那么newlib中的crt0.o是怎么知道用户设置的堆的起始地址的呢?

答:

在嵌入式系统中,newlib 是一个轻量级的 C 库,用于管理包括动态内存分配在内的标准 C 函数,如 mallocfree。为了使 newlib 能够管理堆(heap),它需要知道堆的起始地址和大小。你提到在链接脚本中没有看到 __heap_start__heap_end 这样的符号,并想知道 newlib 中的 crt0.o 是如何获取用户设置的堆的起始地址的。下面我将详细解答这个问题。


1. newlib 如何获取堆的起始地址?

newlib 获取堆的起始地址并不是通过硬性指定的符号(如 __heap_start),而是通过链接脚本中定义的某个符号(通常是 end_end)来实现的。这个符号表示堆的起始位置,通常由链接脚本在内存布局中设置。

在你的链接脚本中,虽然没有显式定义 __heap_start = .;__heap_end = .;,但有以下部分:

._user_heap_stack :
{. = ALIGN(8);PROVIDE ( end = . );PROVIDE ( _end = . );. = . + _Min_Heap_Size;. = . + _Min_Stack_Size;. = ALIGN(8);
} >RAM
  • end 符号的作用:在这里,end(和 _end)被定义为 ._user_heap_stack 节的起始地址。这个节通常紧接在数据段(如 .bss)之后,用于分配堆和栈的空间。
  • 堆的起始地址end 标记了堆的开始位置,而堆的结束位置是 end + _Min_Heap_Size
  • newlib 的使用方式newlib 的动态内存管理函数依赖于一个叫做 _sbrk 的函数,而 _sbrk 使用 end 符号作为堆的初始地址。

因此,在你的配置中,newlib 通过链接脚本中的 end 符号获取了堆的起始地址。


2. _sbrk 函数如何工作?

_sbrknewlib 中负责管理堆增长的关键函数。它的作用是根据请求的内存大小(incr)扩展堆,并返回新分配的内存块地址。以下是 _sbrk 的典型实现(简化版):

caddr_t _sbrk(int incr)
{extern char end; /* 由链接脚本设置 */static char *heap_end;if (heap_end == 0)heap_end = &end; /* 初始化时将 heap_end 设置为 end */char *prev_heap_end = heap_end;heap_end += incr; /* 扩展堆 */return (caddr_t) prev_heap_end;
}
  • 初始化:当 _sbrk 第一次被调用时,heap_end 被设置为 end,即链接脚本中定义的堆起始地址。
  • 内存分配:之后,每次调用 _sbrk 时,heap_end 会根据请求的大小递增,从而分配新的内存块。
  • 关键点end 是一个外部符号,直接由链接脚本提供,newlib 通过它知道堆的起始位置。

在你的链接脚本中,end 被定义为 ._user_heap_stack 的起始地址,因此 _sbrk 会从这个位置开始管理堆。


3. crt0.o 的角色是什么?

crt0.o 是 C 运行时库的一部分,负责在 main 函数执行之前进行初始化工作,比如设置堆和栈。然而,在 newlib 的标准实现中,crt0.o 并不需要显式地将堆的起始地址传递给 _sbrk,原因如下:

  • end 符号的全局性end 是链接脚本中定义的全局符号,newlib_sbrk 函数可以直接访问它。
  • 初始化过程:通常,crt0.o 会设置一些基本的运行时环境(比如栈指针),但堆的初始化是由 _sbrk 在第一次调用时完成的,而 _sbrk 直接使用 end
  • 可能的汇编代码:在某些系统中,crt0.o 可能包含类似以下的代码(以 ARM 为例):
    ldr r0, =end
    bl _sbrk_init  ; 调用初始化函数(视实现而定)
    
    但在 newlib 的典型实现中,_sbrk 不需要额外的初始化函数调用,因为它直接依赖 end 符号。

在你的情况下,由于 end 已由链接脚本定义,crt0.o 不需要额外操作,newlib_sbrk 就能通过 end 知道堆的起始地址。


4. 为什么没有 __heap_start__heap_end 也能工作?

  • 符号名称的灵活性newlib 并不强制要求使用 __heap_start__heap_end 这样的符号。不同的系统和链接脚本可能使用不同的命名约定,而 newlib 默认使用 end_end
  • 你的配置:在你的链接脚本中,end 替代了 __heap_start 的作用,提供了堆的起始地址。堆的大小由 _Min_Heap_Size 定义,但 _sbrk 通常只关心起始地址,结束地址的检查可能由额外的逻辑完成(比如确保 heap_end 不超过 RAM 范围)。
  • 结果:即使没有 __heap_start__heap_endnewlib 仍然能通过 end 正常工作。

5. 具体传递机制总结

  • 通过什么传递:堆的起始地址通过链接脚本中的 end 符号传递给 newlib
  • 如何传递end 是在链接时确定的全局符号,newlib_sbrk 函数直接引用它,无需额外的运行时传递。
  • crt0.o 的作用crt0.o 本身并不直接告诉 newlib 堆的起始地址,而是依赖链接脚本的静态定义(end)。_sbrk 在第一次调用时自动以 end 为基础初始化堆。

因此,在你的系统中,newlib 通过以下方式得知用户设置的堆的起始地址:

  1. 链接脚本定义了 end = .,标记 ._user_heap_stack 的起始地址。
  2. newlib_sbrk 函数使用 extern char end 访问这个地址。
  3. 堆从 end 开始管理,crt0.o 不需要显式干预。

结论

在你的链接脚本中,虽然没有 __heap_start__heap_end,但 newlib 通过 end 符号获取了堆的起始地址。这个符号由链接脚本提供,crt0.o 不需要额外传递信息,_sbrk 函数直接使用 end 来初始化和管理堆。这种机制简单高效,非常适合嵌入式系统的内存管理需求。

http://www.dtcms.com/a/319988.html

相关文章:

  • 古法笔记 | 通过查表进行ASCII字符编码转换
  • change和watch
  • Event Stream输出优化:Vue3节流函数的正确实现
  • Flink的运行模式
  • 【算法训练营Day22】回溯算法part4
  • Linux中进程地址空间
  • Godot ------ 中级人物血条制作01
  • 【LLM】扩散模型与自回归模型:文本生成的未来对决
  • GPT-5今夜亮相?OpenAI神秘直播预告,暗示新模型将至
  • 无人机未来的通信脉络:深度解析远距离无线通信模块的革新
  • 【源码】AndroidPlayer
  • 为何毫米波需要采用不同的DPD方法?如何量化其值?
  • pma_init reset_pb
  • 服务器Docker安装教程
  • openGauss3.10企业版单机部署(openEuler20.03 SP3)
  • 嵌入式学习硬件(一)ARM体系架构
  • 【数字图像处理系列笔记】Ch05:傅里叶变换与频率域滤波
  • 哈勃网络计划大规模升级卫星以创建全球蓝牙层
  • AI代码审查大文档处理技术实践
  • mysql如何实现备份某个数据库并还原备份
  • RHCA - CL260 | Day04:对象存储、存储池、用户认证
  • Mysql自定义顺序查询
  • 预约时间组件
  • 能源材料顶刊AEM:钛酸锂“双模储能”设计范式——兼得高能量与高功率密度
  • 智慧能源设备巡检准确率↑32%:陌讯多模态融合算法实战解析
  • Redis模块-RedisJson
  • Q-Learning详解:从理论到实践的全面解析
  • 数据合规——解读跨境电商隐私合规白皮书(2024)【附全文阅读】
  • React(三):脚手架、组件化、生命周期、父子组件通信、插槽、Context
  • AR技术:制造业质量控制的“智能革新”