ARM技术论坛
直播中

emitter

4年用户 1883经验值
擅长:可编程逻辑 存储技术
私信 关注
[经验]

浅谈ARM处理器内存分配

1,
ARM的体系结构有很多很多介绍的地方,从其7种模式到CPSR状态寄存器,这些都是属于最底层的硬件直接相关的ARM属性,再向上一点,考虑一下在ARM上运行的嵌入式应用,一般称在ARM上直接运行的嵌入式应用为Image就是直接烧进flash运行的可执行文件,这种可执行文件是由IAR,ADS,ARM-EABI-GCC等编译器经过编译所直接生成的。由于没有操作系统的支持,这种镜像文件是需要自己管理底层存储空间的分配的。这里我主要说一下关于Image镜像与ROM,RAM的关系。首先在ARM上电启动时Image是在ROM中的,这就有问题了,我们知道,程序是由指令与数据共同组成的,指令可以放在ROM中,但数据变量必须放在RAM中才能够运行啊,可ARM上电时RAM空间是清空的啊,这就产生一个问题,变量数据是怎么进到RAM中的,由谁来做的。介绍一下ARM程序中各种标志,在ARM中有CODE,READONLY DATA, READWRITE DATA, ZI,他们分别表示

CODE:ARM 指令,
READONLY DATA指只读数据,如const int tst = 1;
READWRITE DATA可读写数据,就是我们常说的变量,已经被初始的变量才是READWRITE DATA。如 int tst = 2;
ZI:Zero Init数据,就是不初始化,或者用0初始化的变量。
在IAR,ADS等开发工具中,其配置文件中需要为编译后的镜像指定各段所在的地址空间,由可读写属性上可以主要分为
READONLY 包括CODE,READONLY DATA
READWRITE 包括READWRITE DATA,ZI。
了解了这些基础知识后,回到最初的问题,ARM的Image就是由上面所说的各个段组成的,那在只读的ROM空间,如何将READWRITE的数据放入RAM中的那,在代码中我们并没有作这些事啊。其实,这部分工作是由Image自动进行的不需要认为干预,在启动时,Image自动将数据拷贝到RAM中,这里还要强调一下,拷贝的只是READWRITE变量,对ZI的变量,直接将所在内存段清0即可,无需拷贝。
在编写嵌入式程序过程中,需要重点将存储空间中各段位置考虑好,在进行Image的编译,Image起始地址必须是0x00,或者CPU指定的运行地址,重点是分配各个特殊状态的STACK空间,以及用于动态分配的HEAP空间。在IAR中有关于 initialize by copy/manule的指令,就是用来将RW数据段拷入RAM中的,这里一定要将RW拷入,否则会导致运行错误。

2
ARM映像文件的组成
所谓ARM映像文件就是指烧录到ROM中的bin文件,也称为image文件。以下用Image文件来称呼它。
Image文件包含了RO和RW数据。
之所以Image文件不包含ZI数据,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。包含进去反而浪费存储空间。

Q:为什么Image中必须包含RO和RW?
A:因为RO中的指令和常量以及RW中初始化过的变量是不能像ZI那样“无中生有”的。

ARM程序的执行过程
从以上两点可以知道,烧录到ROM中的image文件与实际运行时的ARM程序之间并不是完全一样的。因此就有必要了解ARM程序是如何从ROM中的image到达实际运行状态的。
实际上,RO中的指令至少应该有这样的功能:
1. 将RW从ROM中搬到RAM中,因为RW是变量,变量不能存在ROM中。
2. 将ZI所在的RAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI地址及大小来将相应得RAM区域清零。ZI中也是变量,同理:变量不能存在ROM中
在程序运行的最初阶段,RO中的指令完成了这两项工作后C程序才能正常访问变量。否则只能运行不含变量的代码。
说了上面的可能还是有些迷糊,RO,RW和ZI到底是什么,下面我将给出几个例子,最直观的来说明RO,RW,ZI在C中是什么意思。
1; RO
看下面两段程序,他们之间差了一条语句,这条语句就是声明一个字符常量。因此按照我们之前说的,他们之间应该只会在RO数据中相差一个字节(字符常量为1字节)。
Prog1:
  1. #i nclude
  2. void main(void)
  3. {
  4. ;
  5. }
  6. Prog2:
  7. #i nclude
  8. const char a = 5;
  9. void main(void)
  10. {
  11. ;
  12. }
Prog1编译出来后的信息如下:
  1. ================================================================================
  2. Code RO Data RW Data ZI Data Debug
  3. 948 60 0 96 0 Grand Totals
  4. ================================================================================
  5. Total RO Size(Code + RO Data) 1008 ( 0.98kB)
  6. Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
  7. Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
  8. ================================================================================
Prog2编译出来后的信息如下:
  1. ================================================================================
  2. Code RO Data RW Data ZI Data Debug
  3. 948 61 0 96 0 Grand Totals
  4. ================================================================================
  5. Total RO Size(Code + RO Data) 1009 ( 0.99kB)
  6. Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
  7. Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)
  8. ================================================================================
以上两个程序编译出来后的信息可以看出:
Prog1和Prog2的RO包含了Code和RO Data两类数据。他们的唯一区别就是Prog2的RO Data比Prog1多了1个字节。这正和之前的推测一致。
如果增加的是一条指令而不是一个常量,则结果应该是Code数据大小有差别。
2; RW
同样再看两个程序,他们之间只相差一个“已初始化的变量”,按照之前所讲的,已初始化的变量应该是算在RW中的,所以两个程序之间应该是RW大小有区别。
Prog3:
  1. #i nclude
  2. void main(void)
  3. {
  4. ;
  5. }
Prog4:
  1. #i nclude
  2. char a = 5;
  3. void main(void)
  4. {
  5. ;
  6. }
Prog3编译出来后的信息如下:
  1. ================================================================================
  2. Code RO Data RW Data ZI Data Debug
  3. 948 60 0 96 0 Grand Totals
  4. ================================================================================
  5. Total RO Size(Code + RO Data) 1008 ( 0.98kB)
  6. Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
  7. Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
  8. ================================================================================
Prog4编译出来后的信息如下:
  1. ================================================================================
  2. Code RO Data RW Data ZI Data Debug
  3. 948 60 1 96 0 Grand Totals
  4. ================================================================================
  5. Total RO Size(Code + RO Data) 1008 ( 0.98kB)
  6. Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
  7. Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)
  8. ================================================================================
可以看出Prog3和Prog4之间确实只有RW Data之间相差了1个字节,这个字节正是被初始化过的一个字符型变量“a”所引起的。
3; ZI
再看两个程序,他们之间的差别是一个未初始化的变量“a”,从之前的了解中,应该可以推测,这两个程序之间应该只有ZI大小有差别。
Prog3:
  1. #i nclude
  2. void main(void)
  3. {
  4. ;
  5. }
Prog4:
  1. #i nclude
  2. char a;
  3. void main(void)
  4. {
  5. ;
  6. }
Prog3编译出来后的信息如下:
  1. ================================================================================
  2. Code RO Data RW Data ZI Data Debug
  3. 948 60 0 96 0 Grand Totals
  4. ================================================================================
  5. Total RO Size(Code + RO Data) 1008 ( 0.98kB)
  6. Total RW Size(RW Data + ZI Data)96 ( 0.09kB)
  7. Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
  8. ================================================================================
Prog4编译出来后的信息如下:
  1. ================================================================================
  2. Code RO Data RW Data ZI Data Debug
  3. 948 60 0 97 0 Grand Totals
  4. ================================================================================
  5. Total RO Size(Code + RO Data) 1008 ( 0.98kB)
  6. Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
  7. Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
  8. ================================================================================
编译的结果完全符合推测,只有ZI数据相差了1个字节。这个字节正是未初始化的一个字符型变量“a”所引起的。
注意:如果一个变量被初始化为0,则该变量的处理方法与未初始化华变量一样放在ZI区域。
即:ARM C程序中,所有的未初始化变量都会被自动初始化为0。

总结:
1; C中的指令以及常量被编译后是RO类型数据。
2; C中的未被初始化或初始化为0的变量编译后是ZI类型数据。
3; C中的已被初始化成非0值的变量编译后市RW类型数据。

回帖(1)

钟志明

2020-11-14 23:40:21
解说清晰易懂,对理解代码的大小变化很有帮助
举报

更多回帖

发帖
×
20
完善资料,
赚取积分