您的位置 首页 FPGA

[跟我学嵌入式开发] 堆和栈

堆(heap)和栈(stack)是非常重要的概念,当我们进行程序开发时理解它们非常重要,尤其是对于嵌入式系统开发。比如在嵌入式系统中,任务

堆(heap)和栈(stack)是十分重要的概念,当咱们进行程序开发时了解它们十分重要,尤其是关于嵌入式体系开发。比方在嵌入式体系中,使命的栈一般都很小,或许也就几K字节。在这种情况下,咱们就应当尽或许不要将占用内存大的变量分配在栈上,而是应当分配在堆上;此外,也尽量不要选用递归的办法来规划程序,不然很简单形成栈溢出。

从实质上说,堆和栈都是内存,那么咱们只能从概念上对其进行区分了。为了便利阐明,现在假定嵌入式软件是一个单体程序(这一术语并不是嵌入式体系开发中的专用术语,是我为了便利阐明而运用的),也便是操作体系和咱们的使用程序是被编译在同一个可执行程序傍边的,比方,来自WindRiver的VxWorks便是选用这种办法的。咱们知道一个可执行程序存在最为重要的三个段。.text段用于寄存程序的代码,即放的是处理器的运转指令。.data用于寄存初始化好的数据,当boot loader(请拜见《什么是boot loader》)加载程序文件时,会将程序文件中的.data段拷贝到内存的VMA(Virtual Memory Address,在《了解binutils东西集》中有所提及,在嵌入式体系中绝大部分不必虚拟内存,因而,VMA便是实地址)处,然后完结变量的初始化操作。尽管,咱们在C/C++程序中是对全局变量一个一个初始化的,但实际上boot loader是对一切的全局变量经过将程序文件中的.data段拷贝到内存中一次性的完结初始化的。.bss段用于寄存没有初始化好的变量,程序文件中并不寄存.bss段的具体内容,仅仅存有.bss段的开端地址和巨细,当boot loader加载咱们的嵌入式程序文件时,仅仅依据程序文件中的.bss信息对内存中的.bss块进行清零操作。

图 1示例了boot loader与咱们的单体程序共存的一个内存和FLASH映射快照。其间咱们假定内存的巨细是8M字节。能够看出在FLASH上即寄存了boot loader程序,又寄存了咱们的单体程序,至于FLASH上是否有文件体系咱们在此并不必关怀。在内存中你能够看出也存在一块boot loader区,这一块区是由FLASH中的boot loader将自己加载到内存中的,以便加速运转速度,能够想像这块内存区是最早被拷贝到内存中的。当内存中的boot loader运转时,其会读取FLASH中的单体程序,这一单体程序一般是ELF格局的。其间的ELF头指示了各段的VMA地址和巨细以及各段内容在单体程序中的偏移地址。boot loader经过ELF头信息,将.texe段和.data段从FLASH拷贝到内存中。明显,boot loader和单体程序在内存中所占用的地址空间是不能堆叠的,不然当boot loader将单体程序从FLASH拷贝到内存时,会将其本身的内容给覆盖掉,然后形成自己无法正常运转。地址的规划是咱们规划boot loader时需求考虑到的。图中咱们只示例了一个中断向量表,其实,很有或许boot loader内存区也有一个中断向量表,是供boot loader运转时用的。还有便是有一块暂时的栈空间,这一空间能够是boot loader和单体程序一起运用的,关于一起运用,需求着重的是boot loader与单体程序并不会一起运转,当boot loader运转完了今后,会调转到单体程序的进口处开端运转,进口地址明显应当坐落内存的.text段。而单体程序在一开端运转时(此刻操作体系还没有起作用),仍是需求一块小的内存作为栈来运用,以便能进行函数调用。依据不同的规划,咱们能够在单体程序运转的初始阶段运用与boot loader相同的栈,当然也能够运用不同的栈。这儿咱们假定运用相同的栈。从图1中,咱们能够看出,内存中还存在很大的一块搁置区,这一块区暂时还没有运用用处。


图 1

一旦boot loader运转了咱们的单体程序,咱们说boot loader就不存在了,那此刻boot loader所占用的内存空间也就开释出来了,如图 2所示。从图中能够看出内存中的搁置空间加大了。那搁置空间被咱们的单体程序用来做什么呢?做堆!在单体程序中的操作体系部分,会供给必定的办理模块来办理这块堆,并供给API(Application Programming Interface,使用程序编程接口)让咱们调用,然后完成从堆平分配或是开释内存,这些API类似于C语言中的malloc ()/free ()。堆在办理上有一个特色,从堆平分配出来的内存应当是以某一巨细字节为鸿沟的。比方,假如CPU中的double类型是占用内存最多的数据类型且是8字节,那么堆分配出来的内存就必须确保是以8字节为鸿沟的。这一点请读者想一想为什么?除了选用动态的内存分配,在嵌入式体系中一般还会选用固定巨细内存块的分配办法,这种分配办法的优点是十分的快,并且这种内存在运用的过程中不会发生内存碎片。


图 2

堆咱们说过了,那接下来咱们看一看假如咱们的单体程序持续运转,会呈现什么样的内存布局。咱们知道,一般咱们的单体程序在初始化时往往需求创立多个使命来完成其使用功用。关于每一个使命,它一块内存是私有的,那便是栈!当使命运转时,其需求用栈来做为函数调用时的参数传递空间,以及用栈来存储函数内的局部变量。假定咱们的单体程序需求创立两个使命A和B,这需求经过调用操作体系中的使命创立函数来到达这一意图。操作体系所供给的使命创立API往往需求咱们指定使命栈的巨细,有的乃至能够指定栈内存空间。一旦使命创立的API被调用,那么操作体系会调用堆分配API为使命分配栈,此刻的内存布局如图 3所示。使命创立完了今后,各使命就能够依据使用程序逻辑的需求审请堆空间以完成其事务逻辑。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/fangan/fpga/256443.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部