手机虚拟空间软件_虚拟空间哪个好用( 二 )


【手机虚拟空间软件_虚拟空间哪个好用】说到堆,它是接下来的一块地址空间 。与栈一样,堆用于运行时内存分配;但不同点是,堆用于存储那些生存期与函数调用无关的数据 。大部分语言都提供了堆管理功能 。因此,满足内存请求就成了语言运行时库及内核共同的任务 。在C语言中,堆分配的接口是malloc()系列函数,而在具有垃圾收集功能的语言(如C#)中,此接口是new关键字 。
如果堆中有足够的空间来满足内存请求,它就可以被语言运行时库处理而不需要内核参与 。否则,堆会被扩大,通过brk()系统调用(实现)来分配请求所需的内存块 。堆管理是很复杂的,需要精细的算法,应付我们程序中杂乱的分配模式,优化速度和内存使用效率 。处理一个堆请求所需的时间会大幅度的变动 。实时系统通过特殊目的分配器来解决这个问题 。堆也可能会变得零零碎碎,如下图所示:

最后,我们来看看最底部的内存段:BSS,数据段,代码段 。在C语言中,BSS和数据段保存的都是静态(全局)变量的内容 。区别在于BSS保存的是未被初始化的静态变量内容,它们的值不是直接在程序的源代码中设定的 。BSS内存区域是匿名的:它不映射到任何文件 。如果你写static int cntActiveUsers,则cntActiveUsers的内容就会保存在BSS中 。
另一方面,数据段保存在源代码中已经初始化了的静态变量内容 。这个内存区域不是匿名的 。它映射了一部分的程序二进制镜像,也就是源代码中指定了初始值的静态变量 。所以,如果你写static int cntWorkerBees = 10,则cntWorkerBees的内容就保存在数据段中了,而且初始值为10 。尽管数据段映射了一个文件,但它是一个私有内存映射,这意味着更改此处的内存不会影响到被映射的文件 。也必须如此,否则给全局变量赋值将会改动你硬盘上的二进制镜像,这是不可想象的 。
下图中数据段的例子更加复杂,因为它用了一个指针 。在此情况下,指针gonzo(4字节内存地址)本身的值保 存在数据段中 。而它所指向的实际字符串则不在这里 。这个字符串保存在代码段中,代码段是只读的,保存了你全部的代码外加零零碎碎的东西,比如字符串字面 值 。代码段将你的二进制文件也映射到了内存中,但对此区域的写操作都会使你的程序收到段错误 。这有助于防范指针错误,虽然不像在C语言编程时就注意防范来得那么有效 。下图展示了这些段以及我们例子中的变量:

你可以通过阅读文件/proc/pid_of_process/maps来检验一个Linux进程中的内存区域 。记住一个段可能包含许多区域 。比如,每个内存映射文件在mmap段中都有属于自己的区域,动态库拥有类似BSS和数据段的额外区域 。下一篇文章讲说明这些"区域"(area)的真正含义 。有时人们提到"数据段",指的就是全部的数据段 + BSS + 堆 。
你可以通过nm和objdump命令来察看二进制镜像,打印其中的符号,它们的地址,段等信息 。最后需要指出的是,前文描述的虚拟地址布局在Linux中是一种"灵活布局"(flexible layout),而且以此作为默认方式已经有些年头了 。它假设我们有值RLIMIT_STACK 。当情况不是这样时,Linux退回使用"经典布局"(classic layout),如下图所示:

对虚拟地址空间的布局就讲这些吧 。下一篇文章将讨论内核是如何跟踪这些内存区域的 。我们会分析内存映射,看看文件的读写操作是如何与之关联的,以及内存使用概况的含义 。