基本概念

基本硬件

CPU可以直接访问的通用存储只有内存和处理器的内置的寄存器。机器指令可以用内存地址作为参数,而不能用磁盘地址作为参数。所以执行指令以及指令使用的数据,应在这些可执行访问的存储设备上,如果数据不在内存中,那么在CPU使用他们之前应把数据移到内存上。

CPU内置寄存器通常可以在一个CPU时钟周期内完成访问,但是对于内存,完成内存的访问可能需要多个CPU时钟周期,这种结果造成的影响就是如果没有数据用于完成正在执行的指令,那么CPU可能将会多次中断(暂停)。所以需要在CPU和内存之间增加一个高速缓存

为了确保进程的执行正确,需要为每个进程分配一块单独的内存空间,从而使进程内存空间保护进程而互相不受到影响。通过两个寄存器,通常为基地址寄存器界限地址寄存器。基地址寄存器中含有最小的合法的物理内存大小;界限地址寄存器中指定了进程范围的大小。内存空间的保护实现是通过在CPU硬件对在用户模式下产生的地址和寄存器的地址进行比较来完成的。也只有操作系统可以通过特殊的特权指令,才能加载基地址寄存器和界限地址寄存器,因为特权指令只能在内核模式下执行,所以只有操作系统可以加载基地址寄存器和界限地址寄存器。

地址绑定

通常程序是作为二进制可执行文件存放在磁盘上的,如果需要执行的话,首先要将程序调入内存并放在进程中。在磁盘上等待调入内存以便执行的进程形成了输入队列

大多数情况下,用户程序在执行前,需要经过好几个步骤,在这些步骤中,地址可能会有不同的表示形式。源程序中地址通常使用符号表示,编译器通常将这些符号地址绑定到可重定位的地址。链接程序或加载程序在将这些可重定位的地址绑定到绝对地址。每次绑定都是从一个地址空间到另一个地址空间的映射。



通常,指令和数据绑定到存储器地址可在沿途的任何一步中进行:

  • 编译时:如果编译时就已经知道进程将在内存中的驻留地址,那么就可以生成绝对代码,如果将来开始地址发生变化,那么就有必要重新编译代码。
  • 加载时:如果在编译时不知道进程将驻留在何处,那么编译器就应生成可重定位代码。对这种情况,最后绑定会延迟到加载时才进行。如果开始地址发生变化,那么只需要重新加载用户代码以合并更改的值。
  • 执行时:如果进程在执行时可以从一个内存段移到另一个内存段,那么绑定应延迟到执行时才进行。大多数通用操作系统就是采用这种方式的。

逻辑地址空间和物理地址空间

CPU生成的地址通常称为逻辑地址,而内存单元看到的地址(即加载到内存地址寄存器的地址)通常是物理地址。编译时和加载时的地址绑定方法生成相同的逻辑地址和物理地址,但是执行时的地址绑定方案生成不同的逻辑地址和物理地址。在这种情况下,通常成逻辑地址为虚拟地址。

从虚拟地址到物理地址的运行时映射是由**内存管理单元(MMU)**的硬件设备来完成。用户进程所生成的地址在送交到内存之前,都将加上重定位寄存器的值(基地址寄存器)。而用户程序不会看到真实的物理地址。

动态加载

在之前的进程加载到内存中,是将进程的整个程序和所有数据都加载到物理内存当中,所以进程的大小受限于内存的大小,为了获得内存空间利用率,可以采用动态加载。采用动态加载时,一个程序只有在调用时才会加载,所有程序都以可重定位加载格式保存在磁盘中。主程序被加载到内存并执行,当一个程序需要调用另一个程序时,调用程序首先检查另外一个程序是否已经加载到内存中。如果没有,可重定位链接程序会加载所需的程序到内存中,并更新程序的地址表以反映这个变化,接着控制传递给新加载的程序。所以,动态加载的好处就是,只有一个程序需要时才会被加载

动态链接

动态链接库是系统库,可链接到用户程序,以便运行。

如果是静态链接的话,它的系统库与其他目标模块一样,通过加载程序被合并到二进制程序映像。但是动态链接库类似动态加载(注意在这里不是加载而是链接),会延迟到运行时。

如果有动态链接,在二进制映像中,每个库程序的引用都有一个存根。存根是一小段代码,用来支出如何定位适当的内存驻留库程序,或者在程序不在内存时应如何加载库。当执行存根时,他首先检查所需程序是否已经在内存中,如果不在,将程序加到内存中。存根会用程序地址来代替自己,并开始执行程序,所以下次在执行程序代码的时候,就可以直接进行,而不会因动态链接产生任何开销。

进程交换

进程必须在内存中以便执行,但是,进程可以短暂的从内存交换到备份存储,当再次执行时在调回到内存中。

标准交换

标准交换在内存和备份存储之间移动进程,备份存储通常是快速磁盘。备份存储应该足够的大,以容纳所有用户的所有内存映像的副本,并且应提供对这些存储器映像的直接访问。系统维护一个可运行的所有进程的就绪队列,他们的映像在备份存储和内存中,当CPU调度器决定要执行一个进程时,它调用分派器。分派器检查队列中的下一个进程是否在内存中,如果不在且没有空闲内存区域,那么分派器会换出当前位于内存的一个进程并换入所需进程,然后重新加载寄存器,并将控制权给所选进程。

移动系统的交换

移动系统通常不支持任何形式的交换,移动设备通常采用闪存,而不是空间更大的硬盘作为他的永久存储。苹果的IOS和谷歌的Android的具体交换策略可以自行百度。

连续内存分配

连续内存分配是早期OS所采用的一种内存分配策略,在采用连续内存分配时每个进程位于一个连续的内存区域,与包含下一个进程的内存相连。

内存保护

为了放置进程访问不属于他们的内存,依旧通过重定位寄存器和界限寄存器来实现保护,MMU通过动态的将逻辑地址加上重定位寄存器的值。当CPU调度器选择一个进程来执行时,作为上下文切换工作的一部分,分派器会用正确的值来加载重定位寄存器和界限寄存器。由于CPU所产生的每个地址都需要与这些寄存器进行核对,所以可以保证操作系统和其他用户的程序和数据不受该运行进程的影响。

内存分配

最简单的内存分配方法,就是将内存分为多个固定大小的分区。每个分区可以只包含一个进程。所以多道程序的程度受限于分区数。

对于可变分区方法,操作系统维护一张表,用于记录哪些内存可用和哪些内存已用。开始时所有内存都可用于用户进程,因此可以作为一大块的可用内存,称为。最后内存有一个集合,以包含各种大小的孔。

通常可用的内存块为分散在内存里的不同大小的孔的集合。当新进程需要内存时,系统为该进程查找足够大的孔。如果孔太大,那么就分为两块:一块分给进程,一块返回孔集合。这种方法是通用动态存储分配问题的一个例子。从一组可用孔中选择一个空闲孔的最常用方法有:

  • 首次适应:分配首个足够大的孔,查找可以从头开始,也可以从上次首次适应结束时开始。一旦找到足够大的空闲孔,就可以停止.
  • 最优适应:分配最小的足够大的孔。应查找整个列表,除非列表按大小排序。这种方法可以产生最小剩余孔
  • 最差适应:分配最大的孔。同样应该查找整个列表,除非列表按大小排序这种方法可以产生最大剩余孔。

碎片

用于内存分配的首次适应和最优适应算法都会有外部碎片。对于内存的碎片可以是内部碎片,也可以是外部的。比如假设有一个18464字节大小的孔,有一个进程需要18462字节,如果只能分配所要求的块,那么还剩下2字节的孔。因此通常按固定大小的块为单位来分配内存,采用这种方法,进程所分配的内存可能比所需要的大,这两个数字之差称为内部碎片,这部分内存存在于分区内部,但是又不能用。

外部碎片的一种解决方法是紧缩,移动内存内容,以便将所有的空闲空间合并成一整块。但是紧缩并不是总是可能的。如果重定位是静态的,并且在汇编时或加载时进行的,那么就不能紧缩;只有重定位是动态的,且在运行时进行的,那么才可以采用紧缩。

OS之内存管理 ---基本的内存管理策略(一)的更多相关文章

  1. 【uTenux实验】内存池管理(固定内存池和可变内存池)

    1.固定内存池管理实验 内存管理是操作系统的一个基础功能.uTenux的内存池管理函数提供了基于软件的内存池管理和内存块分配管理.uTenux的内存池有固定大小的内存池和大小可变的内存池之分,它们被看 ...

  2. 转:内存区划分、内存分配、常量存储区、堆、栈、自由存储区、全局区[C++][内存管理][转载]

    内存区划分.内存分配.常量存储区.堆.栈.自由存储区.全局区[C++][内存管理][转载] 一. 在c中分为这几个存储区1.栈 - 由编译器自动分配释放2.堆 - 一般由程序员分配释放,若程序员不释放 ...

  3. (转)从内存管 理、内存泄漏、内存回收探讨C++内存管理

    http://www.cr173.com/html/18898_all.html 内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟 ...

  4. 5. c++ 内存管理 C/C++ 内存机制

    参考自:http://blog.csdn.net/wpf_ml/article/details/7759911 1. 内存,Cache,寄存器内存:通常计算机将数据存放在物理内存,cache及寄存器中 ...

  5. Java内存管理:Java内存区域 JVM运行时数据区

    转自:https://blog.csdn.net/tjiyu/article/details/53915869 下面我们详细了解Java内存区域:先说明JVM规范定义的JVM运行时分配的数据区有哪些, ...

  6. Linux-内存管理机制、内存监控、buffer/cache异同

    在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,主要特点是,无论物理内存有多大,Linux 都将其充份利用,将 ...

  7. 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配

    垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己 ...

  8. Windows内存管理和linux内存管理

    windows内存管理 windows 内存管理方式主要分为:页式管理,段式管理,段页式管理. 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页:页式管理把内存空间按照页的大小划分成片或 ...

  9. 内存管理概述、内存分配与释放、地址映射机制(mm_struct, vm_area_struct)、malloc/free 的实现

    http://blog.csdn.net/pi9nc/article/details/23334659 注:本分类下文章大多整理自<深入分析linux内核源代码>一书,另有参考其他一些资料 ...

随机推荐

  1. 2018.07.04 POJ 1113 Wall(凸包)

    Wall Time Limit: 1000MS Memory Limit: 10000K Description Once upon a time there was a greedy King wh ...

  2. html自适应布局,@media screen,媒体查询

    html自适应布局,@media screen,媒体查询 自适应代码示例: <!doctype html> <html> <head> <meta chars ...

  3. DVWA

    DVWA默认的用户有5个,用户名密码如下(一个足以): admin/password gordonb/abc123 1337/charley pablo/letmein smithy/password

  4. 不用快捷键就能使用Eclipse的自动完成功能

    偶然间看到了这个,或许有和我一样不喜欢按 alt-/ 兄弟用得上.不用老去按那个 alt-/ 了,还是方便不少.         打开 Eclipse -> Window -> Perfe ...

  5. spring boot docker 初尝试

    Docker服务中进程间通信通过/var/run/docker.sock实现,默认服务不提供监听端口,因此使用docker remote api 需要手动绑定端口. 在centos7.2下,可以进行这 ...

  6. 《Forward团队-爬虫豆瓣top250项目-代码设计规范》

    成员:马壮,李志宇,刘子轩,年光宇,邢云淇,张良 1.缩进采用一个Tab键 2.大括号:如: if (条件){ 语句: } 3.分行:不把多条语句放在同一行 4.变量命名:统一用英文 5.注释:注释前 ...

  7. day13(反射,BeanUtils包)

    反射, 获取字节码文件的方式: 方式一: 对象.getClass(); 方式二: 类名.Class; 方式三:   Class.forName(String Class); 通过字节码文件获取对象 定 ...

  8. C - 无间道之并查集 HihoCoder - 1066

    输入 每个测试点(输入文件)有且仅有一组测试数据. 每组测试数据的第1行为一个整数N,表示黑叔叔总共进行的操作次数. 每组测试数据的第2~N+1行,每行分别描述黑叔叔的一次操作,其中第i+1行为一个整 ...

  9. eclipse 安装

    做一个项目,需要搭建环境.使用sql server 2005,tomcat,eclipse.过程真复杂,碰到些小问题,不过都解决了,还算顺利. win7下sql server 2005安装:http: ...

  10. const 用法全面总结

    C++中的const关键字的用法非常灵活,而使用const将大大改善程序的健壮性,本人根据各方面查到的资料进行总结如下,期望对朋友们有所帮助. Const 是C++中常用的类型修饰符,常类型是指使用类 ...