在学韦东山老师的freertos,卡在了汇编中cpu的寄存器和task的栈的交互流程,为了避免以后忘记所著。
 

一、堆和栈

堆(了解不全):向上生长

在程序中通过malloc或者vector,调用堆空间。堆会导致内存泄漏,内存碎片化。原因有3:

1.外部碎片

堆用于分配不定长空间,且在不再使用的时候需要释放,这导致了原先紧凑分配的空间,在不同长度的空间被释放后,变得碎片化。

2.内部碎片

为了硬件的访问效率,内存分配器遵循*对齐*分配的规则。不足4/8byte的空间也会被分配4/8byte空间,这样的对齐能便于硬件的数据访问---每次加一个4/8个byte就可以访问到下一个数据这导致了空而无用。

补:4/8字节对齐

3.管理开销

要管理堆,不仅需要知道地址,还需要长度,以及管理状态。

这就需要额外的空间去堆的信息,以便于后续释放堆的时候知道哪些空间属于这个堆,有没有被占用/释放,但这也就导致了实际分配的堆空间大于预期的堆空间。

这个额外的空间称为控制块,用于释放堆时由堆管理器进行访问,来检查应该收回空间,而状态位则用于在空间被收回时,检索它周围的空间是否也可以被收回。

补:内存分配器的工作

栈:向下生长

栈的LIFO(后进先出)机制导致了它不会有碎片化的空间。

举例来说就是中断嵌套,最高等级的中断函数完成了,次一级的才会继续运行,可以知道最高等级的中断一定是最后面触发的,但他是最先被完成释放了空间的。     

栈只是一个内存块,不存在寄存器,寄存器只存在于cpu,只是在分配空间的时候,内核自动划分了用于存储寄存器值的空间。

 二、栈和CPU的交互:汇编为例

1.在单片机中,以keil5做例。keil5的魔法棒就相当于界面化的命令行,在特定位置给出指令就可以实现指定操作。

以**fromelf --bin --output=test.bin test.axf**这条反汇编指令来说,在编译和重新编译前

- --text :指定输出格式为 纯文本 (如果不加这个,默认输出可能是二进制)。
- -c :(code) 执行反汇编 。将机器码(0101)翻译回汇编代码(LDR/STR)。
- -a :(address) 显示地址 。在每一行汇编前面加上它在 Flash 中的物理地址。
- --output=test.dis :指定生成的 结果文件名 (.dis 是常用后缀,意为 Disassembly)。
- test.axf : 输入源文件 。它是编译器生成的“大包”,包含机器码、符号表、调试信息等。
- test.axf : 含金量最高 。它是 ELF 格式,包含了代码地址、函数名、甚至对应的 C 语言行号。 fromelf 必须读取它才能还原出有意义的信息。
- test.bin : 最纯净 。只有原始的机器码,没有地址和函数名信息。它是直接“拍”进 Flash 运行的固件。

主要用途是生成反汇编码输出到文本文件中:

fromelf --text -a -c --output=./Output/工程名.dis ./Objects/工程名.axf

fromelf:

2.前置:栈只是一个内存块,不存在寄存器,寄存器只存在于cpu,只是在分配空间的时候,内核自动划分了用于存储寄存器值的空间。

3.PUSH和POP的操作顺序并不同:

4.寄存器:

- PC (Program Counter) : 执行坐标 。指向下一条要执行的指令地址。
- SP (Stack Pointer) : 栈指针 。指向 RAM 中栈顶的位置,随 PUSH/POP 自动加减。
- LR (Link Register) :记录函数跳转前的断点, return 本质就是把 LR 给 PC。
- xPSR : 记录计算结果的状态(如:是否为0、是否为负、是否有进位)。

5.汇编指令:

6.实例:加法运算后return,这里传参是(cnt,1)

三、栈和中断的交互:

xPSR:状态寄存器,用于记录压栈时的计算、判断语句等的结果或者数据,以便恢复现场的时候直接知道结果。

SP在触发中断时的工作:直接走到当前的栈顶进行push操作,然后在push最后一个寄存器r0后,pc开始执行中断服务函数

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐