代码段,Map文件,boot

程序内存通常分为代码段、数据段和BSS段。代码段存放指令,数据段存储已初始化的全局变量,BSS段用于未初始化的全局变量和静态变量。编译后的可执行文件包含这些静态数据和指令,加载到内存时,数据会被复制到RAM。静态变量和初始化的全局变量在程序运行前已分配空间,动态变量在栈中分配。理解内存布局有助于优化程序和内存管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个程序包含的部分
bss段、data段、text段三个组成的
一般文章的说明如下:
1.bss段 Block Started by Symbol( ZI data)
未初始化的全局变量和静态变量

2.data段(RW data)
数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

3.text(code)
代码段,存放指令

本文起始
一段代码为何会分为各种段,CPU会根据指令执行对应的操作
例如 MOV a,b
ADD a,b
CPU的运行实际上是对数据进行操作所以包含两个对象
1.如何操作 加减乘除,位移等等
如何操作等一系列的内容我们称为指令

2.对谁操作 数据:常量,变量
操作对象 就是数据。这个数据也就是逻辑数据,变量常量都有我们指定的意义。

一段代码由编译器完成编译(Build)后生成可执行文件。可执行文件就是指令和数据的存在形式。
可执行文件只要编译完成,文件本身是不会变动的,处于只读状态,顺理成章把它写入到只读存储器中。
所以文件本身是不会变动的,很好理解就是静态,不管你怎么样我就安安静静在那里。所以称为 静态区域。
静态区域的有时也叫做image(镜像)文件,镜像就是镜子里面的成像,其特点是和原像一模一样。
原像是只在目标机器中的可执行文件。所以我们编译好的文件,是和目标机器里面存在的文件相同。

程序执行的源头就是可执行文件,所以可执行文件所在的地址空间被称为加载域 Load Region。CPU运行的时候把指令从加载域中取出,加载到CPU中(哈佛结构CPU)。

静态文件,现在包含所有的静态指令和数据。静态数据 就是 全局变量和静态定义的变量,还有那些无论怎样都安安静静存在都的常量。
在这里插入图片描述

显然 一个可执行文件占用的空间(FLASH 空间) 为所有静态数据 = code +静态数据

RW-Data 初始化了的可读写变量,值得是静态初始化的静态变量。
如果是一个动态变量,也就是程序运行中才会出现的变量。
代码如下

void task1_task(void *pvParameters)
{

	for(;;)
	{
		GPIO_ToggleBits(GPIOF,GPIO_Pin_8);
		vTaskDelay(1000);
	}

}
      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name

       296         14          0          0          0       1839   app_can_train.o
       176         38          0         14          0       2041   app_pwm_in_mes.o
        56         14          0          1         20       1323   app_spi_train.o
         0          0          0          0          0      15428   event_groups.o
       756         74          0         32       8072       7599   heap_4.o
        24         10          0          0        604       1332   idle_task_static_memory.o
       148          0          0          0          0       3790   list.o
       396        132          0         16       3072      30954   main.o

void task1_task(void *pvParameters)
{
  uint8_t a;
	for(;;)
	{
		GPIO_ToggleBits(GPIOF,GPIO_Pin_8);
		vTaskDelay(1000);
	}

}

变量a对RW-data没有影响。
如果加上static 静态;

void task1_task(void *pvParameters)
{
  static uint8_t a;
	for(;;)
	{
		GPIO_ToggleBits(GPIOF,GPIO_Pin_8);
		vTaskDelay(1000);
	}

}
      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name

       296         14          0          0          0       1839   app_can_train.o
       176         38          0         14          0       2041   app_pwm_in_mes.o
        56         14          0          1         20       1323   app_spi_train.o
         0          0          0          0          0      15428   event_groups.o
       756         74          0         32       8072       7599   heap_4.o
        24         10          0          0        604       1332   idle_task_static_memory.o
       148          0          0          0          0       3790   list.o
       396        132          0         17       3072      30966   main.o

RW-data 增加了一个数据宽度。

理解FLASH空间的关键在于 静态

在函数中的非静态变量是在运行过程中出现的变量,所以为动态变量。
动态变量的存在时间有局限,存在于运行时的栈中。栈的消耗是动态的。

静态空间的分配状况称为 Memory Map of the image ,一般在编译器的map 文件中

在这里插入图片描述
Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00003ad8, Max: 0x00100000, ABSOLUTE)
Execution Region ER_IROM1 (Exec base: 0x08000000, Load base: 0x08000000, Size: 0x00003a14, Max: 0x00100000, ABSOLUTE)

加载域  Base: 0x08000000, Size: 0x00003ad8, Max: 0x00100000, ABSOLUTE
执行域  Exec base: 0x08000000, Load base: 0x08000000, Size: 0x00003a14, Max: 0x00100000, ABSOLUTE

加载和执行都是在 0x08000000 的FLASH 存储空间内。
执行域大于加载域说明,加载的时候多出一部分空间 0x3ad8 - 0x3a14 = 0xc4 (196)
196 为 RW Data 段的大小。 RW Data 已经拷贝到RAM空间
RW Data为变量,在编译阶段就已经初始化,所以开建立C环境的时候,会拷贝到RAM中。
然后建立ZI区域,统一初始化为0.
RAM空间
0x08003a14 开始映射RW Data 到RAM 0x20000000
RAM 空间还包括未被静态区映射的ZI段。
在这里插入图片描述
到此 对于代码段应该有深刻的理解。

text 段简单说就是代码
data段就是编译阶段已经确定的静态数据。在编译阶段就确认初值的静态变量。这些数据会在建立C环境的时候,从加载域搬运到运行域上。
bss 段 非静态数据。在建立C环境的时候 统一在RAM中建立,并且初始化为0.

在这里插入图片描述
Load Region 也就是FLASH 上的可执行文件
在这里插入图片描述
如果理解了 载入域和执行域
那么 MAP文件就是 载入域和执行域 地址簿。
无效的指令和数据会被编译器优化掉,不会出现在 载入域和执行域 ,这个过程也被记录在MAP文件中。

无效的指令和数据会被编译器优化 :Removing Unused input sections from the image.
其他部分都是各种变量函数 的地址,所占用的空间。

cortex m3的存储空间映射

在这里插入图片描述
0x00000000~0x000FFFFF 是代码的执行空间,这里是虚拟空间,没有物理器件。
0x0800 0000 ~ 0x080F FFFF是FLASH 存储器的物理地址,地址对应器件的存储单元。

代码的执行空间和存储器可以存在映射关系。
0x00000000~0x000FFFFF 是代码的执行空间 这里5个F可以映射到0x0800 0000 ~ 0x080F FFFFFLASH 存储器的物理地址
这种映射关系是可以配置的。
为何设计这种映射关系
1.上电后CPU各种寄存器的默认值0x00000000 开始执行取32bit数据为栈指针,在取32bit数据复位函数入口开始执行指令。
2,如果地址 值0x00000000 是固定对应物理器件(FLASH,SRAM…)那么程序必须从指定的物理存储器上取指令。方式单一
3.设计一个空的地址空间,和物理实体产生映射。映射到不同的物理实体就会有不同的引导方式,也就是第一条指令的来源
这个物理实体指的是主FLASH,系统存储器,SRAM,or other。这样第一条代码的从哪来就多了很多选择。
在这里插入图片描述
不仅仅是引导使用了地址的关系。
我们发现CPU 能访问的设备或者数据 都是通过地址来进行的,所以对于CPU来说一些皆地址。
对于CPU来说并不知道什么是gpio,什么是uart外设。它们都只是地址,把地址上的数据读出来,把数据写入地址。
在这里插入图片描述
所以都是地址。
地址 和 物理器件的关系就是所谓的存储器地址映射。
CPU第一条指令从哪来,就是BOOT 选择。

镜像文件的包含了哪些内容
1.异常向量
2.用户程序

异常向量是什么,CUP发生异常的时候,异常处理函数的入口列表。ARM cortex M3/M4系列CPU的架构规定好了。每次复位的时候,可以调转到一个可以配置的地址。从这个位置开始执行第一条取指令。这个视乎和boot一样?boot硬件引脚选择是物理器件。这个选择是物理器件中的位置,也就是说可以在主FLASH,系统存储器,SRAM,or other任意的位置取第一条指令

所以一个完整的一份“程序” 需要包含异常向量和执行的代码。
CPU做的事情是取指令执行,还有异常调转执行。
在这里插入图片描述
在这里插入图片描述
所以MCU上的代码很可能是上面的两种结构。
特殊代码 慢慢演变成所谓的boot代码用来引出应用程序。
如果boot代码包含程序的更新,演变出BootLoader,还有其专门的流程以确保逻辑严密。

显然这里会有一个问题,就是不同的向量和不同的应用程序,该存放在哪里。
编译器会按照顺序进行编排,当然还可以通过用户设置来指定它们的存放位置。
叫做分散加载文件 (*.sct) 。加载域在什么位置,执行域在什么位置。

分散加载(scatter)文件是一个文本文件,它可以用来描述连接器生成映像文件时需要的信息。通过编写一个分散加载文件来指定 ARM 连接器在生成映像文件时如何分配 Code、RO-Data, RW-Data, ZI-Data 等数据的存放地址。如果不用分散加载文件指定,那么 ARM连接器会按照默认的方式来生成映像文件。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值