内存管理是操作系统最重要最复杂的内容之一,虽然计算机硬件技术在飞速发展,内存容量也在不断扩大,但是仍然不可能将所有的用户进程都全部放入主存,因此操作系统必须对内存空间进行合理的划分和有效的动态分配,操作系统对内存的的划分和动态分配就是内存管理。
我们创建一个进程首先要将程序装入内存(进程使程序得抽象,程序的一次活动就叫做进程),但是程序员通过高级语言所写出来的程序(存放在文件当中)并不在内存当中,而且也无法直接装入内存,因为计算机是无法识别高级语言的。所以这个时候就必须将我们所写的程序经过一定的处理,形成计算机可识别的二进制得可执行文件,这个时候,我们所写的程序才可以装入内存。这些处理需要以下步骤。
注释:这里的编译、链接、装入过程复杂,日后我会专门出一个专题来讲述编译、链接这部分的内容,今天在这里就不做过多的讲解了。
程序经过编译后,他的每个目标模块都是从0号单元开始编址,这称为该目标模块的相对地址(逻辑地址),当链接程序将各个模块链接成为一个完整的可执行目标程序时,链接程序顺序依次按照各个模块的相对地址构成统一的从0号单元开始编址的逻辑地址空间(虚拟地址空间)。
也就是说进程在运行的时候,我们所看到的和使用的地址全部都是逻辑地址,用户程序和程序员只需要知道逻辑地址,而内存管理的机制对用户完全是透明的,不同的进程可以拥有相同的逻辑地址,因为这些相同的逻辑地址可以映射到不同的内存空间去。
物理地址空间是指内存中物理单元的集合,它是地址转换的最终地址,进程在运行时执行指令和访问数据的时候都必须通过物理地址从内存中存取,当装入程序将可执行文件装入内存中的时候必须通过地址变换将逻辑地址变换成为物理地址才能够顺利地将文件装入内存,这个过程称为地址重定位。
操作系统通过内存管理部件将进程所使用的逻辑地址转换为物理地址,进程在执行的时候使用的是虚拟内存空间当中的地址,而操作系统必须通过一定的硬件配合将虚拟地址转换为物理地址,逻辑地址是通过页表映射到物理内存的,而页表则是由操作系统维护并被处理器使用。
注释:页表这里涉及到了虚拟内存的知识,各位小伙伴们如果有对于虚拟内存不太理解的话,可以参考我的上一篇文章《深度学习CPU(番外篇)——虚拟内存》
原文链接:http://t.csdn.cn/sjiRL
我们要知道一个程序要想要执行的话,必须将其调入内存,而在程序调入内存后就会形成一个进程,因为进程就是程序的一次活动,或者说进程本身就是程序的抽象。当一个程序调入内存的时候,就形成了进程的内存映像,一个进程的内存映像有以下几个要素。
操作系统内核区(用户不可见) |
用户栈(运行时创建) |
共享库的存储映射区 |
动态生成的堆(运行时由malloc创建) |
读/写数据段 |
只读代码段 |
未使用区 |
注释:上图是进程在内存当中的一个概念图,注意内存当中的每个物理块都有相应的地址(上图并未标出),栈区会有一个栈指针来对栈区进行维护。
确保每个进程都有一个单独的内存空间,内存分配前,需要保护操作系统不受用户进程的影响,同时保护该进程不受其他进程的影响,内存保护可以采取两种方法。
1)上下限寄存器:
在CPU中设置一对上下限寄存器,存放程序在内存中的上限和下限地址,每当CPU要访问一个地址的时候都要分别和两个寄存器的值相比,判断有无越界。
2)采用重定位寄存器和界地址寄存器:
重定位寄存器又称为基地址寄存器,界地址寄存器又称为限长寄存器,重定位寄存器包含最小的物理地址,界地址寄存器包含逻辑地址的最大值,内存管理机构动态地将逻辑地址与界地址寄存器进行比较,若没有发生越界,逻辑地址则加上重定位寄存器中的物理地址以后映射成为物理地址,再送交给内存单元。
注意:重定位寄存器是用来加的,逻辑地址加上重定位寄存器中的物理地址以后就能得到物理地址,界地址寄存器是用来比的,通过比较界地址寄存器中的逻辑地址最大值来判断是否越界。
加载重定位寄存器和界地址寄存器时必须拥有特权指令才可以,只有操作系统内核才可以加载这两个存储器,只有操作系统才可以修改这两个寄存器的值,而用户是不可以修改的。
在这里还要再强调一下,不管是之前的上下限寄存器还是现在的重定位寄存器和界地址寄存器都是设置在CPU当中的,也就是内存的保护是通过CPU来实现的,可能会有小伙伴会问为什么重定位寄存器当中的物理地址加上逻辑地址就可以得出真实的物理地址了呢?这里要涉及到CPU的一个重要部件就是ALU算术逻辑单元,这是CPU当中专门用于计算的单元,至于他是怎么算出来的,我们不需要知道。
并不是所有的进程内存空间都适合共享,只有那些只读的区域才适合共享,可重入代码又称为纯代码,是一种允许多个进程访问但是不允许多个进程修改的代码,但在实际执行的时候,也可以为每个进程配备局部数据区,将在执行过程中需要修改的数据复制到该区域,这样程序在执行的时候只需要对该私有区域进行修改即可,并不需要去改变共享的代码。
存储管理方式随着操作系统的发展而发展,在操作系统由单道向多道发展的时候,存储管理方式便由单一连续分配发展为固定分区分配,为了能跟好的适应不同大小的程序要求,又从固定分区分配发展到动态分区分配,又为了能够更好地提高内存利用率,进而从连续分配方式发展到离散分配方式——也是存储管理。
覆盖与交换是多道程序环境下用来扩充内存的两种方法。
在早期的计算机系统中,内存容量很小,虽然主存中只存放一道用户程序,但存空间放不下用户进程的现象也经常发生,这一矛盾可以使用覆盖来解决。
覆盖的基本思想如下,由于程序运行时并非任何时候都要访问程序及数据的各个部分(尤其是大程序),因此可把用户空间分成一个固定区和若干覆盖区,将经常活跃的部分(CPU经常访问的主存中的数据)放在固定区,其余部分按照调用关系分段,即将要访问到的数据放到覆盖区,其余放入外存,在需要调用之前再将其调入覆盖区,替换覆盖区中原有的片段。
这样子即使程序没有全部装入内存的时候也可以执行程序,并且内存当中更新的地方只有覆盖区,固定区当中的代码是常驻内存的。但是当程序的代码量大于内存量的时候,程序时无法执行的。
交换顾名思义就是将正在等待CPU执行的程序从内存当中调到外存中去,将内存空间腾出来,这个过程叫做换出,将再外存当中准备好的程序从外存当中调入内存这个过程叫做换入。
例如,一个CPU采用时间片轮转制度调度算法的多道程序环境,时间片一到就将刚刚执行的程序换出,将另外一个进程换入刚刚释放的内存空间,同时,CPU调度器可以将时间片分配给在内存中的其他进程,每个进程用完时间片以后都要与其他进程交换,理论上只要交换的速度足够的快,总有进程可以在内存中运行。
关于交换,还是要注意一下几个问题:
注意:对于扩充内存现代的操作系统时通过虚拟内存来实现的,而覆盖技术早就已经成为了历史,不过交换技术还是比较有生命力的。
今天为大家详细地介绍了操作系统当中的内存管理的基本概念,下一篇文章就该带大家领略一下内存空间分配的三大方式,以及页式存储管理、段式存储管理、页段式存储管理等重要内容,我们不见不散。