手机虚拟空间软件 虚拟空间哪个好用

内存管理模块是操作系统的心脏;这对应用和系统管理非常重要 。在本文中,我将关注实际的内存问题,但我不会回避技术内幕 。因为很多概念是通用的,所以本文中的大多数例子都取自32位x86平台的Linux和Windows系统 。本系列的第一篇文章讨论应用程序的内存布局 。
多任务操作系统中的每个进程都在自己的内存池中运行 。该池是虚拟地址空间空,在32位模式下始终是4GB内存地址块 。这些虚拟地址通过页表映射到物理内存,页表由操作系统维护,由处理器引用 。每个流程都有自己的页表,但还是有隐藏的故事 。只要虚拟地址被启用,它就会作用于在这台机器上运行的所有软件,包括内核本身 。因此,必须为内核保留一些虚拟地址:
这并不意味着内核使用了这么多物理内存,只是可以控制这么大的地址空,可以根据内核的需要映射到物理内存 。内核/在Linux中,内核/内核代码和数据总是可寻址的,随时可以处理中断和系统调用 。相反,用户模式地址空之间的映射随着进程切换的发生而不断变化:
蓝色区域表示映射到物理内存的虚拟地址,而白色区域表示未映射的部分 。在上面的例子中,火狐使用了相当多的虚拟地址空房间,因为它是一个传奇的内存消耗者 。地址空中的每个条带对应于不同的内存段,如堆和堆栈 。请记住,这些段只是简单的内存地址范围,与英特尔处理器的段无关 。总之,以下是Linux进程的标准内存段布局:
当计算机快乐、安全、可爱、正常运行时,几乎每个进程的每个段的起始虚拟地址都与上图完全一致,这也为远程发现程序安全漏洞打开了大门 。一个挖掘过程经常需要引用绝对内存地址:栈地址、库函数地址等 。远程攻击者必须依靠地址空之间布局的一致性来摸索这些地址 。如果他们猜对了,就会有人完蛋 。因此,地址空之间的随机排列逐渐流行起来 。Linux通过向堆栈、内存映射段和堆栈的起始地址添加随机偏移量来破坏布局 。遗憾的是,32位地址空相当紧凑,随机化留下的空并不大,削弱了这种技术的效果 。
进程地址/调用方法或函数会将新的堆栈帧推入堆栈 。当函数返回时,堆栈框架被清理 。也许是因为数据严格遵循LIFO顺序,这种简单的设计意味着您不必使用复杂的数据结构来跟踪堆栈的内容,只需一个指向堆栈顶部的简单指针 。因此,推动和弹出过程非常快速和准确 。此外,堆栈空的持续重用有助于将活动堆栈内存保留在CPU缓存中,从而加速访问 。进程中的每个线程都有自己的堆栈 。
通过持续将数据压入堆栈,如果数据超出其容量,则对应于堆栈的存储区域将被耗尽 。这将触发一个页面错误,将由Linux的expand_stack()处理,它将调用acct_stack_growth()来检查是否有适合堆栈增长的地方 。如果栈的大小低于RLIMIT_STACK(通常为8MB),栈一般会加长,程序会继续愉快运行,不会感觉到发生了什么 。这是将堆栈扩展到所需大小的常规机制 。但是,如果达到最大堆栈空大小,将出现堆栈溢出,程序将收到分段错误 。当映射的堆栈区域扩展到所需的大小时,它不会收缩,即使堆栈不是那么满 。这就像联邦预算,总是在增长 。
动态堆栈增长是唯一允许访问未映射内存区域(图中白色区域)的情况 。对未映射内存区域的任何其他访问都将触发页面错误,从而导致段错误 。有些映射区域是只读的,因此试图写入这些区域也会导致段错误 。
堆栈下面是我们的内存映射部分 。这里,内核将文件的内容直接映射到内存 。任何应用程序都可以通过Linux的mmap()系统调用(实现)或Windows的create file mapping()/mapviewpoffice()请求这个映射 。内存映射是一种方便高效的文件I/O方法,因此被用来加载动态库 。也可以创建不对应任何文件的匿名内存映射 。该方法用于存储程序的数据 。在Linux中,如果您通过malloc()请求大块内存,C运行时将创建这样一个匿名映射,而不是使用堆内存 。“chunk”表示大于MMAP_THRESHOLD,默认值为128KB,可以通过mallopt()进行调整 。
说到堆,就是下一块地址空 。与堆栈一样,堆在运行时用于内存分配;但不同的是,堆用于存储其生存期独立于函数调用的数据 。大多数语言都提供堆管理功能 。因此,满足内存请求已经成为语言运行时库和内核的共同任务 。在C语言中,堆分配的接口是malloc()系列函数,而在具有垃圾收集功能的语言(如C#)中,这个接口是新的关键字 。
如果堆中有足够的空空间来满足内存请求,它可以由语言运行时库处理,而无需内核的参与 。否则,堆将被扩大,请求所需的内存块将通过brk()系统调用(实现)来分配 。堆管理非常复杂,需要精心设计的算法来处理程序中杂乱的分配模式,并优化速度和内存效率 。处理堆请求所需的时间可能有很大差异 。实时系统通过专用分配器解决了这个问题 。堆也可能变得碎片化,如下图所示:

手机虚拟空间软件 虚拟空间哪个好用

文章插图
最后,我们来看看最底层的内存段:BSS、数据段和代码段 。在C语言中,BSS和数据段存储静态(全局)变量的内容 。不同的是,BSS存储的是未初始化的静态变量内容,它们的值不是直接在程序的源代码中设置的 。BSS内存区域是匿名的:它不映射到任何文件 。如果你写静态的int cntActiveUsers,cntActiveUsers的内容将保存在BSS中 。
另一方面,数据段在源代码中保存初始化的静态变量内容 。这个内存区域不是匿名的 。它将程序的二进制映像的一部分,即静态变量与源代码中指定的初始值进行映射 。因此,如果您编写static int cntWorkerBees = 10,则cntWorkerBees的内容将保存在数据部分,初始值为10 。虽然数据段被映射到一个文件,但它是一个私有内存映射,这意味着在这里更改内存不会影响映射的文件 。一定也是这样,否则给全局变量赋值会改变硬盘上的二进制映像,这是不可想象的 。
下图中的数据段示例更复杂,因为它使用了指针 。在这种情况下,指针gonzo(4字节内存地址)本身的值保存在数据段中 。它指向的实际字符串不在这里 。该字符串存储在只读的代码段中 。它保存你所有的代码加上一些小片段,比如字符串的文字值 。代码段也将您的二进制文件映射到内存,但是写入该区域将导致您的程序收到代码段错误 。这有助于防止指针错误,尽管它不如用C语言编程时注意预防那么有效 。下图显示了我们示例中的这些段和变量:
手机虚拟空间软件 虚拟空间哪个好用

文章插图
【手机虚拟空间软件 虚拟空间哪个好用】您可以通过读取文件/proc/PID _ of _ process/map来检查Linux进程中的内存区域 。请记住,一个片段可能包含许多区域 。例如,每个内存映射文件在mmap部分都有自己的区域,动态库有类似于BSS和数据部分的附加区域 。下一篇文章将解释这些“区域”的真正含义 。有时候人们会提到“数据段”,意思是所有的数据段+BSS+堆 。
您可以通过nm和objdump命令查看二进制图像并打印符号、它们的地址、段和其他信息 。最后需要指出的是,上面描述的虚拟地址布局在Linux中是一种“灵活布局”,并且已经作为默认方法使用了一些年 。它假设我们有值RLIMIT_STACK 。当情况不是这样时,Linux返回到经典布局,如下图所示:
以上就是虚拟地址空之间的布局 。下一篇文章将讨论内核如何跟踪这些内存区域 。我们将分析内存映射,看看文件的读写操作是如何与之相关的,以及内存使用配置文件的意义 。

    推荐阅读