嵌入式系统中,程序代码运行在FLASH还是RAM的细说

https://www.zhihu.com/question/387640455

作者:知乎用户
链接:https://www.zhihu.com/question/387640455/answer/1153244080
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 

这是个好问题,要全面回答还是比较麻烦

问题1:FLASH中的代码是如何得到运行的呢?比如PC指针是在哪里由谁设置的?

以ARM为例:

ARM-cortex-M3/4的单片机(比如STM32 MK60等):该类单片机的代码在nor flash中,cortex内核可以直接运行,不需要将代码加载到ram中运行。

ARM-cortex-A系列的SOC(比如Exynos4412):该类SOC更加复杂,通常有内存管理单元(MMU),代码存储在nand flash中,程序运行时,需要先将代码加载到ram中运行,该类SOC的启动环节包含了加载程序。就像Windows操作系统存储在硬盘中,开机的时候,操作系统的代码会加载到内存条(RAM)中。

PC指针:无论什么单片机或者SOC,都有一个PC寄存器,这个寄存器保存了下一条待取指令的地址。正常情况下自动加“4”,遇到分支跳转的时候,由跳转指令设置值。那么指针是什么?指针是一个变量的地址,在含有操作系统(比如Linux、Windows)即硬件层面含有内存管理单元(MMU)的情况下,指针是虚拟地址,不含操作系统的情况下,是物理地址,虚拟地址和物理地址经过MMU转换。

问题2:这些代码需要搬到RAM中才能运行吗?不这样做会有什么不妥吗?

上文讲了,大部分单片机的代码直接在nor flash中运行,少部分需要加载到ram中。nor flash可以直接寻址一个字节,可以找到一个指令的具体地址,因此可以直接运行。nand flash 的存储单元是块,不能对指令直接寻址,因此不能直接运行其中的代码。因此保存在nand flash中的程序不加载到ram中运行不了。即你的硬盘中的Windows不加载到内存条中,运行不起来。

问题3:如果需要搬到RAM,那是片内还是片外有什么区别吗?

片内片外都可以,具体看是那款SOC或CPU了。

问题4:如果用户存在FLASH的实际代码大小(比如1MB),超过了RAM的可用空间(比如512KB),那这个搬移过程是啥样的?

现在实际情况很少遇到这种情况,当然可能会有RAM很小的系统,可以分时分段的使用,即程序运行一段,加载一段,运行完,加载下一段。很不建议这样玩,现在的RAM很大了,你的实际代码达到1MB的时候,你的内存可能都有1G,2G了。比如Linux操作系统编译完后,实际上只有几MB,实际的Linux系统会有几个G 的内存可用。

问题5:片外扩展的FLASH和SRAM与片内的想比,除了空间大小有差别,性能速度上会有怎样的差异呢?

具体要看SOC的总线设计。一般来说片外的性能弱一点。

能不能在Flash中直接运行程序代码,取决于Flash的访问特性。

Flash存储器是按块组织的,在使用时也倾向于按块访问才更加高效。Flash类似于ROM一类的存储器,但它其实是可读可写的,不同于同样可读可写的RAM,它在写入数据时需要先将你所写位置所属的块擦除,不管你是不是只写几个字节,所以如果要改写Flash中的数据,总是会先将数据所属的块缓存到内存中,然后再在内存中改写好数据后又重新将块写回,这样就不会丢失数据,但是花销太大。读的时候,往往也是先定位块的位置,然后在块中顺序读取,在不同块中间断读取数据是非常低效的,所以按块读按块写是Flash的一大特点,它不能够随意的对存储区域寻址,典型的如NAND Flash。

不过有一类Flash存储器在读取数据时可以做到任意的寻址而不会有太大的花销,它的读操作是接近于RAM的,而写操作依然延续了按块擦除然后再按块写的特点,典型的如NOR Flash。

所以正因为这样的特性,Flash通常用于存储不需要频繁改动的掉电不能丢失的数据。

介绍完背景知识,回到你的提问:

首先要清楚的是,CPU需要在存储器中读取指令,指令地址由PC寄存器给出,每执行完一条指令PC会自动的指向下一条指令,如果指令的长度不等会使得给出的地址不总是有一致的对齐,其次程序运行总会伴随跳转,这使得指令的寻址更具有随意性,所以说要直接在某种存储器中执行程序,至少读取数据时要能够任意寻址,而NOR Flash是刚好能满足要求的,市面上常见的MCU内置的Flash就是这种类型,所以能够直接在上面运行存储的程序,而不需要加载到RAM中。其他不具备这种访问特性的存储器是不能直接在上面执行程序的,必须转移到满足这种特性的存储器当中执行,比如加载到RAM。

1、FLASH中的代码是如何得到运行的呢?比如PC指针是在哪里由谁设置的?

采用cortex- m内核的MCU会根据外部启动配置引脚的电平,将启动存储器映射到0x00000000地址,如果是在Flash启动,在内部Flash的起始位置会存储一张异常中断向量表,表中的第一项和第二项存储了初始的栈地址和复位向量,这张表的位置是可配置的,而复位后的位置正是在0x00000000地址。硬件上电复位后,SP,PC寄存器会自动依次设置为表中的前两项,然后根据PC设置的初始值开始执行代码,所以说PC的值是在复位时是自动设置的。

2、这些代码需要搬到RAM中才能运行吗?不这样做会有什么不妥吗?

正如前面叙述的,并不必要。在RAM中执行可能会得到更好的执行性能,但是对于MCU内部的Nor Flash来说是没有必要的。有一点要提及的是,程序一般会由代码段txt,只读数据段rodata,初始化数据段data和未初始化数据段bss(并无数据)组成,只读数据段因为和代码段一样不需要改动,所以可以留在Flash当中 ,但是需要将也存储在Flash中的data段加载到RAM中以及空出空间给bss。这是运行环境的初始化,是有搬运的,只是搬运的不是代码,这发生在进入main函数之前。

3、如果需要搬到RAM,那是片内还是片外有什么区别吗?

在片内的RAM性能会更好,但是容量一般不能做的太大。

4、如果用户存在FLASH的实际代码大小(比如1MB),超过了RAM的可用空间(比如512KB),那这个搬移过程是啥样的?

是可以分阶段加载执行的,但是对程序的组织会变得复杂,运行变得低效,如果出现了这种情况应该考虑更换硬件配置或者对程序优化裁剪。

5、片外扩展的FLASH和SRAM与片内的想比,除了空间大小有差别,性能速度上会有怎样的差异呢?

这取决于存储器的时钟速率和访问延迟,集成在内部的存储器性能一般是能比片外的更好的,所以要使程序有更高的运行性能应该优先使用内部存储器。低端MCU由于运行速率低,内部和外部不会有太大的区别。

可以从以下三个方面可以回答这个问题:

1、计算机组成原理

冯诺依曼模型

计算机专业的同学对这张图一定不陌生,这是最经典的计算机模型,现在所有的计算机设备(当然也包括嵌入式)都没有跳出这个模型。里面的五项可以分为三部分:(1)CU和ALU是CPU(2)Memory是内存设备(理想的内存设备)(3)Input和Output是各种外设设备(键盘、鼠标、显示器······)。

我们这里关注的点是内存设备。冯诺依曼模型中将Memory想象成理想内存设备。所谓理想内存设备就是可读可写、非易失、随机读写。对于理论模型来说,简介易懂是关键。但是在现实中却没有这么理想,受限于成本,不同的存储器只能满足部分指标,这就是接下来要说的主流存储器。

2、主流存储器的特点

现在的存储器可以大致分为两类:RAM和ROM。关于这两类存储器的具体定义和发展历程已经有很多总结,这里就不再赘述,只从我个人的角度谈一下对这两类存储器的理解。

(1)RAM,具体可以分为SRAM和DRAM

共同特点(RAM的根本特点):可读可写、随机读写

区别:SRAM上电即可用,DRAM需要初始化后才能使用,并且SRAM单位成本高于DRAM。

(2)ROM,具体可分为硬盘、Flash(NOR Flash和NAND Flash)

共同特点:非易失

区别:硬盘和NAND Flash都是整块读写、NOR Flash可以随机读,但是需要整块写。

NOR Flash非易失、随机读的特性让它可以作为系统的启动介质。

3、CPU与存储器之间的速度差异是现在制约计算机性能的主要原因。

4、具体到上面5个问题

(1)具体要看是什么Flash,如果是NOR Flash,那么系统可以直接访问执行。如果是NAND Flash,则需要将代码加载到RAM中再运行。PC寄存器在CPU中,在CPU上电时由硬件设置一个特定的值(例如:ARM Cortex-M3的PC寄存器上电默认是0x4)。

(2)和第一问一样,需不需要搬移代码要看Flash类型。

(3)如果是相同类型的RAM,片内和片外没有区别。如果RAM类型不同,就需要具体情况具体分析。

(4)这里假设Flash是1MB,RAM是512KB,猜测应该是NOR Flash和SRAM(例如STM32),则代码不需要搬移。如果是NAND Flash,一般会和DRAM搭配,容量会大很多,应该假设不成立。

(5)和第三问一样。

推荐几本嵌入式的书:

《嵌入式系统开发之道-菜鸟成长日志与项目经理的私房菜》,很老的一本书,从项目的角度系统的讲解了嵌入式系统开发的全过程,虽然比较老了,但是核心并没有过时,可以对嵌入式有一个系统的了解。

《深入理解计算机系统》,可以对整个计算机有一个全面的了解,不要想着一遍看懂,常看常新。

  • 29
    点赞
  • 143
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 在嵌入式操作系统添加C++运行时库需要以下步骤: 1. 确认操作系统是否支持C++。有些嵌入式操作系统只支持C语言,需要在操作系统添加C++支持。 2. 下载适当版本的C++运行时库。根据嵌入式操作系统的架构和编译器选择相应版本的C++运行时库。 3. 将C++运行时库添加到编译器搜索路径。在编译代码时,编译器需要知道C++运行时库在哪里。 4. 将C++运行时库添加到嵌入式操作系统。根据操作系统的不同,添加C++运行时库的方式也不同,可以通过修改操作系统源代码来添加。 5. 在应用程序使用C++。在编写应用程序时,可以使用C++语言和标准库函数。 注意事项: 添加C++运行时库可能会增加操作系统的大小,影响系统的性能和响应速度。因此,需要仔细考虑是否需要添加C++支持。 ### 回答2: 在嵌入式操作系统添加C运行时库的过程如下所述。 首先,需要确认嵌入式操作系统支持C运行时库的加载和使用。某些嵌入式操作系统可能已经默认包含了C运行时库,无需额外添加。若不支持,需要查看操作系统文档或寻求官方支持以了解如何添加C运行时库。 其次,获取相应版本的C运行时库。C运行时库是一个预编译好的二进制文件,可以从多个来源获取,如操作系统厂商、开源社区或第三方供应商。确保选择适合目标嵌入式操作系统和硬件平台的C运行时库版本。 接下来,将C运行时库文件包含到嵌入式操作系统的构建系统。这需要修改操作系统的构建配置文件或脚本,添加C运行时库的路径和文件名。在构建过程编译器会将C运行时库链接到目标可执行文件。 在构建系统添加C运行时库后,需要重新构建操作系统。这通常会涉及到使用交叉编译工具链,将编译选项设置为包含C运行时库路径和文件名,以便在构建过程正确地链接C运行时库。 最后,将构建好的嵌入式操作系统烧录到目标设备进行测试。确保设备能够正常启动并运行使用C运行时库的应用程序。 需要注意的是,添加C运行时库可能会增加系统的存储空间和加载时间。因此,在添加C运行时库之前,需要评估系统的资源限制,确保嵌入式设备具备足够的空间和性能来支持C运行时库的使用。 ### 回答3: 在嵌入式操作系统添加C运行时库的步骤如下: 1.了解目标硬件平台和操作系统:首先需要了解你的嵌入式操作系统以及目标硬件平台的架构和特点,例如处理器架构、操作系统内核等。 2.选择合适的C运行时库:根据目标硬件平台和操作系统的特点,选择适合的C运行时库。常见的C运行时库有newlib、uClibc等,它们提供了标准C库的各种函数和特性。 3.配置编译工具链:根据目标硬件平台和操作系统的需求,在交叉编译环境下配置适用的编译工具链。工具链包括交叉编译器、链接器等工具,用于将C源代码编译为可在目标硬件平台上运行的可执行文件。 4.交叉编译C运行时库:根据目标硬件平台和操作系统的需求,使用配置好的编译工具链对选择的C运行时库进行交叉编译。在编译过程,需要根据目标硬件平台的架构配置编译参数,确保生成的库文件适用于目标硬件平台。 5.将库文件嵌入操作系统镜像:将编译生成的C运行时库文件嵌入到操作系统的镜像。这通常涉及链接器脚本和配置文件的修改,确保内核可以正确加载和使用这些库函数。 6.重新编译操作系统:根据修改后的操作系统配置,重新编译操作系统的内核和相关组件。这样,新添加的C运行时库将与操作系统一起构建为新的镜像。 7.测试和验证:将新编译的操作系统镜像烧录到目标硬件平台上进行测试和验证。确保操作系统能够正常加载和使用添加的C运行时库,并且其他应用程序可以正确调用库的函数。 通过以上步骤,就可以成功地在嵌入式操作系统添加C运行时库,以提供更丰富的C语言功能和特性供应用程序使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值