Linux的内存管理方式经常会在面试时作为操作系统基础被问道。搞清楚这个问题的好处很多,近的话可以应付面试,远的可以提高对于操作系统底层的认识,为程序的性能优化打下基础。

我们对于计算机内存,最直观和简陋的概念就是机器的物理内存,程序都被放在物理内存上执行。物理内存一般都有限制,比如说4G或者8G。

但是如果真的这样直接的使用物理内存会发生什么状况?

1、进程地址空间不能隔离

由于程序直接访问的是物理内存,这个时候程序所使用的内存空间不是隔离的。恶意程序或者是木马程序可以轻而易举的破快其他的程序,系统的安全性也就得不到保障了,这对用户来说也是不能容忍的。

2、内存使用的效率低

由于物理内存一般都有限制,当物理内存不够用时,需要把暂时不需要运行的程序放到磁盘上,试想将整个程序放入磁盘,我们知道IO操作比较耗时,所以这个过程效率将会十分低下。

3、程序运行的地址不能确定

程序每次需要运行时,都需要在内存中非配一块足够大的空闲区域,而问题是这个空闲的位置是不能确定的,这会带来一些重定位的问题,重定位的问题确定就是程序中引用的变量和函数的地址。

可以通过引入一个中间层来解决上面的问题。

现在的内存管理方法就是在程序和物理内存之间引入了虚拟内存这个概念。虚拟内存位于程序和物理内存之间,程序只能看见虚拟内存,再也不能直接访问物理内存。每个程序都有自己独立的进程地址空间,这样就做到了进程隔离。这里的进程地址空间是指虚拟地址。顾名思义既然是虚拟地址,也就是虚的,不是现实存在的地址空间。

既然我们在程序和物理地址空间之间增加了虚拟地址,那么就要解决怎么从虚拟地址映射到物理地址,因为程序最终肯定是运行在物理内存中的,主要有分段和分页两种技术。

分段(Segmentation):这种方法是人们最开始使用的一种方法,基本思路是将程序所需要的内存地址空间大小的虚拟空间映射到某个物理地址空间。

段映射机制

每个程序都有自己的独立虚拟的进程地址空间。进程的只能看到自己的虚拟地址空间,这就使得进程和实际的物理地址解除耦合。两块大小相同的虚拟地址空间和实际物理地址空间一一映射,即虚拟地址空间中的每个字节对应于实际地址空间中的每个字节,这个映射过程由软件来设置映射的机制,实际的转换由硬件来完成。

这种分段的机制解决了文章一开始提到的3个问题中的进程地址空间隔离(1)和程序地址重定位(3)的问题。(PS:既然隔离了,那么缓冲区溢出为啥还能那么牛掰?答案最后讲。)

程序A和程序B有自己独立的虚拟地址空间,而且该虚拟地址空间被映射到了互相不重叠的物理地址空间,如果程序A访问虚拟地址空间的地址不在0x00000000-0x00A00000这个范围内,那么内核就会拒绝这个请求,所以它解决了隔离地址空间的问题。我们应用程序A只需要关心其虚拟地址空间0x00000000-0x00A00000,而其被映射到哪个物理地址我们无需关心,所以程序永远按照这个虚拟地址空间来放置变量、代码,不需要重新定位。

分段机制解决了上面两个问题,是一个很大的进步,但是对于内存效率问题仍然无能为力。因为这种内存映射机制仍然是以程序为单位,当内存不足时仍然需要将整个程序交换到磁盘,这样内存使用的效率仍然很低。事实上,根据程序的局部性运行原理,一个程序在运行的过程当中,在某个时间段内,只有一小部分数据会被经常用到。所以我们需要更加小粒度的内存分割和映射方法,此时是否会想到Linux中的Buddy算法和slab内存分配机制呢,哈哈。另一种将虚拟地址转换为物理地址的方法分页机制应运而生了。

分页机制就是把内存地址空间分为若干个很小的固定大小的页,每一页的大小由内存决定,就像Linux中ext文件系统将磁盘分成若干个Block一样,这样做是分别是为了提高内存和磁盘的利用率。

Linux中一般页的大小是4KB,我们把进程的地址空间按页分割,把常用的数据和代码页装载到内存中,不常用的代码和数据保存在磁盘中,我们还是以一个例子来说明,如下图:

分页机制

我们可以看到进程1和进程2的虚拟地址空间都被映射到了不连续的物理地址空间内。

有一天我们的连续物理地址空间不够,但是不连续的地址空间很多,如果没有这种技术,我们的程序就没有办法运行,甚至他们共用了一部分物理地址空间,这就是共享内存。

进程1的虚拟页VP2和VP3被交换到了磁盘中,在程序需要这两页的时候,Linux内核会产生一个缺页异常,然后异常管理程序会将其读到内存中。

分页机制的实现需要硬件的实现,这个硬件名字叫做MMU(Memory Management Unit),他就是专门负责从虚拟地址到物理地址转换的,也就是从虚拟页找到物理页。

有的时候,单个页表无法表示所有内存页信息,我们还需要多级页表的帮助才行。(后面再讲。)

下面继续聊聊进程地址的概念,当然都是基于Linux操作系统。

进程内部通过分段的方式划分了:数据段、代码段。数据段又可以分为:静态数据段、栈、堆。

由此有几个地址需要讲一下:

逻辑地址:段基值确定它所在的段居于整个存储空间的位置,偏移量确定它在段内的位置,这种地址表示方式称为逻辑地址。机器语言指令中出现的内存地址(&操作符),都是逻辑地址。

线性地址:又叫虚拟地址,是一个32位无符号整数,可以用来表示高达4GB的地址,跟逻辑地址类似,它也是一个不真实的地址,如果逻辑地址是对应的硬件平台段式管理转换前地址的话,那么线性地址则对应了硬件页式内存的转换前地址。

物理地址:用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。

CPU将一个虚拟内存空间中的地址转换为物理地址,需要进行两步:首先将给定一个逻辑地址,CPU要利用其段式内存管理单元,先将为个逻辑地址转换成一个线性地址,再利用其页式内存管理单元,转换为最终物理地址。

逻辑地址----段式内存管理单元----线性地址----页式内存管理单元----物理地址

Linux中逻辑地址等于线性地址。为什么这么说呢?因为Linux所有的段(用户代码段、用户数据段、内核代码段、内核数据段)的线性地址都是从 0x00000000 开始,长度4G,这样线性地址=逻辑地址+ 0x00000000,也就是说逻辑地址等于线性地址了。

Linux主要以分页的方式实现内存管理。

前面说了Linux中逻辑地址等于线性地址,那么线性地址怎么对应到物理地址呢?这个大家都知道,那就是通过分页机制,具体的说,就是通过页表查找来对应物理地址。

准确的说分页是CPU提供的一种机制,Linux只是根据这种机制的规则,利用它实现了内存管理。

分页的基本原理是把内存划分成大小固定的若干单元,每个单元称为一页(page),每页包含4k字节的地址空间(为简化分析,我们不考虑扩展分页的情况)。这样每一页的起始地址都是4k字节对齐的。为了能转换成物理地址,我们需要给CPU提供当前任务的线性地址转物理地址的查找表,即页表(page table)。注意,为了实现每个任务的平坦的虚拟内存,每个任务都有自己的页目录表和页表。

32位的线性地址被分成3个部分:最高10位 Directory 页目录表偏移量,中间10位 Table是页表偏移量,最低12位Offset是物理页内的字节偏移量。

页目录表的大小为4k(刚好是一个页的大小),包含1024项,每个项4字节(32位),项目里存储的内容就是页表的物理地址。如果页目录表中的页表尚未分配,则物理地址填0。

页表的大小也是4k,同样包含1024项,每个项4字节,内容为最终物理页的物理内存起始地址。

每个活动的任务,必须要先分配给它一个页目录表,并把页目录表的物理地址存入cr3寄存器。页表可以提前分配好,也可以在用到的时候再分配。

以 mov 0x80495b0, %eax 中的地址为例分析一下线性地址转物理地址的过程。

前面说到Linux中逻辑地址等于线性地址,那么我们要转换的线性地址就是0x80495b0。转换的过程是由CPU自动完成的,Linux所要做的就是准备好转换所需的页目录表和页表(假设已经准备好,给页目录表和页表分配物理内存的过程很复杂,后面再分析)。

内核先将当前任务的页目录表的物理地址填入cr3寄存器。

线性地址 0x80495b0 转换成二进制后是 0000 1000 0000 0100 1001 0101 1011 0000,最高10位0000 1000 00的十进制是32,CPU查看页目录表第32项,里面存放的是页表的物理地址。线性地址中间10位00 0100 1001 的十进制是73,页表的第73项存储的是最终物理页的物理起始地址。物理页基地址加上线性地址中最低12位的偏移量,CPU就找到了线性地址最终对应的物理内存单元。

我们知道Linux中用户进程线性地址能寻址的范围是0 - 3G,那么是不是需要提前先把这3G虚拟内存的页表都建立好呢?一般情况下,物理内存是远远小于3G的,加上同时有很多进程都在运行,根本无法给每个进程提前建立3G的线性地址页表。Linux利用CPU的一个机制解决了这个问题。进程创建后我们可以给页目录表的表项值都填0,CPU在查找页表时,如果表项的内容为0,则会引发一个缺页异常,进程暂停执行,Linux内核这时候可以通过一系列复杂的算法给分配一个物理页,并把物理页的地址填入表项中,进程再恢复执行。当然进程在这个过程中是被蒙蔽的,它自己的感觉还是正常访问到了物理内存。

线性地址转物理地址

linux有个很大的内存目录,Linux中的内存管理相关推荐

  1. linux gdb打印内存命令,gdb中查看内存方法总结

    出自计组第三次上机附加题第二题 用gdb运行程序b,输出中相应地址究竟指向了什么? 请贴上你是如何找到的(使用了什么gdb指令等等) 在查看地址前首先需要断点定位到需要查看的位置 显示代码内容 (gd ...

  2. linux 系统显示很大,在Linux中可视化显示内存占用情况的方法

    物理内存不足对Linux桌面系统和服务器系统的性能影响都很大.当你的计算机变慢时,要做的第一件事就是释放内存.尤其是在多用户环境以及执行关键任务的服务器环境下,内存消耗会变得更加关键,因为多个用户和应 ...

  3. java 内存溢出和内存泄漏_JAVA中的内存溢出和内存泄漏有很大的区别

    JAVA中的内存溢出和内存泄漏分别是什么,有什么联系和区别,我谈谈自己的理解. 内存泄漏(memory leak ):申请了内存不释放,比如100m的内存,分配了10m的内存一直不回收,那么可以用的内 ...

  4. 苹果手机其他占内存很大去哪删除_手机没内存,照片又不舍得删除?这里有6大方法能帮你解决!...

    虽说目前市面上可供购买的智能手机之中,大部分的内存都很大,可是,这一点并不足以成功吸引到多数的手机用户去更换手机.一般而言,我们的手机只要它的外观保护得较好,内在性能也还算过得去的话,我们大都不会买新 ...

  5. linux不允许将硬链接指向目录,linux 文件(目录)之软链接,硬链接 -

    前提必须清楚一点,当指向数据的文件个数为0 时,数据块就会被释放掉,硬链接相当于硬盘上一块数据的多个指针,而软链接相当于指向数据指针的指针.个人理解成下面的图例 左边的是硬链接,右边的是软链接,无论对 ...

  6. java jvm内存模型_Java(JVM)内存模型– Java中的内存管理

    java jvm内存模型 Understanding JVM Memory Model, Java Memory Management are very important if you want t ...

  7. vs调试c语言检查内存泄露,VisualStudio中检查内存泄露方法

    项目工程中存在内存泄露,被折磨了一晚上,终于查了出来,因为之前没有相关的经验,还比较生疏,在此记录下来,方便以后查找. 对于malloc出的内存的检测方法 这篇文章中详细地记录了从检查到找到确定位置到 ...

  8. android中内存泄露,Android中的内存泄露

    编辑推荐: 本文来自于csdn,本文主要从java的内存模型讲起,最终举出几个内存泄露的例子和解决方案. java运行时内存模型 具体信息:http://gityuan.com/2016/01/09/ ...

  9. java imageio 内存问题_java中的内存泄漏ImageIO.read()

    我正在使用ImageIO.read().这是由原始应用的主要方法调用的类是这样的:java中的内存泄漏ImageIO.read() import java.awt.*; import javax.sw ...

最新文章

  1. 独家 | 用随机森林预测“美版拼多多”商品销量
  2. OVS ovs-vsctl(二十五)
  3. layui实现select下拉选择框组件(含代码、案例、截图)
  4. MFC小笔记:系统托盘实现
  5. 微型计算机接口与技术答案,微型计算机接口技术与应用习题答案(刘乐善).doc
  6. java 遍历arrayList的四种方法
  7. python实践项目(四)
  8. extract()函数:用于从一个date或者interval类型中截取到特定的部分
  9. [入门系列]什么是面向服务的体系结构(SOA)?
  10. 工程从进场到竣工 资料报验的一般程序
  11. Windows、Linux、Mac OS下的锐捷认证的程序——mentohust
  12. VC++6.0下编译xvidcore1.1.0
  13. 基于ZFS+SAS的Tier2/backup存储系统解决方案
  14. 2、一个向量乘它的转置,其几何意义是什么?
  15. Games101-课程16笔记
  16. IDA动态调试夜神模拟器
  17. ECharts - 15.旭日图
  18. ubuntu系统安装完nvidia显卡驱动后黑屏,不能进入系统
  19. 分析一下onedns系统
  20. 三十六 我在软件园的那些日子里

热门文章

  1. python基础 // 与 / % 的区别
  2. verilog练习:hdlbits网站上的做题笔记(8)
  3. Borax.Lunardate:中国农历日期
  4. group by 用法
  5. 洛谷P3386-二分图最大匹配
  6. 衡水中学计算机老师,衡水中学资深老师:电脑阅卷本就是一种淘汰机制,学生都不以为然...
  7. 【宋红康 MySQL数据库 】【高级篇】【03】MySQL的数据目录
  8. Java面试题集(1-50)
  9. ArcGIS提取斜坡单元
  10. Python爬取10529条《三十而已》热评,看看大家都说了些啥!