分享

堆和栈的作用及区别

 thchen0103 2017-02-05
堆(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中,我们可以看出,内存中还存在很大的一块闲置区,这一块区暂时还没有使用用途。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多