本文已参与「新人创作礼」活动,一起开启掘金创作之路。
三种装入方式
(单道程序阶段,此时还没有操作系统)
1、绝对装入:在编译时,如果知道程序将放到内存中的哪个位置,编译程序将产生绝对地址的目标代码。装入程序按照装入模块中的地址,将程序和数据装入内存。(注意:是装入模块知道放在哪个位置,然后装入程序放到这个位置)只适合于单道程序环境
(早期多道批处理操作系统)
2、静态重定位(又称为:可重定位装入):编译、链接后的装入模块的地址都是从0开始的,指令中使用的地址、数据存放的地址都是相对于起始地址而言的逻辑地址。在装入时对地址进行重定位,将逻辑地址变换为物理地址(地址变换是在装入时一次完成的)(必须分配作业需要的全部内存空间,如果没有足够的内存就不能装入该作业。作业一旦进入内存后,在运行期间就不能再移动,也不能再申请内存空间)
(现代操作系统)
3、动态重定位(又称为:动态运行时装入):编译、链接后的装入模块的地址都是从0开始的,指令中使用的地址、数据存放的地址都是相对于起始地址而言的逻辑地址。装入程序把装入模块装入内存后,并不会立即把逻辑地址转换为物理地址,而是把地址转换推迟到程序真正要执行时才进行。(因此装入内存后所有的地址依然是逻辑地址,这种方式需要一个重定位寄存器(存放进程在内存中的起始物理地址)) ,采用这种方式:允许程序在内存中发生移动,可以将程序分配到不连续的存储区中,在程序运行前只需要装入它的部分代码即可投入运行,然后再程序运行过程中根据需要动态申请分配内存
三种链接方式
1、静态链接:在程序运行之前将各目标模块及它们需要的库函数链接成一个完整的可执行文件(装入模块),之后不再拆开。
2、装入时动态链接:将各目标模块装入内存时,边装入边链接
3、运行时动态链接:在程序执行中需要该目标模块时,才对它进行链接
内存管理到底是干什么的?
-
内存分配与回收
-
内存空间的扩充(实现虚拟性) ,使用某种技术从逻辑上对内存空间进行扩充
-
地址转换,程序的逻辑地址与物理地址的转换,就是上面提到的三种装入方式
-
内存保护,保证进程在自己的内存空间内运行,不会越界访问
- 两种方式:
- 1、设置上下限寄存器(分别记录进程能够访问的物理地址的上下限)
- 2、利用重定位寄存器(存放进程初始的物理地址)、界地址寄存器(限长寄存器,存放进程最大的逻辑地址,因为逻辑地址都是从0开始,所以只需要知道最大的逻辑地址就可以知道是否越界)进行判断
内存空间的扩充
1、覆盖技术,用来解决程序大小超过物理内存总和的问题。
必须由程序员声明覆盖结构,操作系统完成自动覆盖,对用户不透明(用户能感知到),增加了用户编程负担。现已不适用。
2、交换技术,内存空间紧张时,系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存。(进程在内存与外存之间的动态调度,也就是中级调度)
3、虚拟存储技术
内存分配与回收:连续分配管理
连续分配:为每一个进程分配的内存空间必须是连续的!
内部碎片:分配给某进程的内存区域中,进程没有用上的部分。
外部碎片:内存中的某些空闲分区由于太小难以利用。
1、单一连续分配:只支持单道程序,内存分为系统区和用户区,用户程序放在用户区。
无外部碎片,有内部碎片
2、固定分区分配:支持多道程序,内存用户空间分为若干个固定大小的分区,每个分区只能装一道作业,分区又具有两种方式:分区大小相等、分区大小不等。
无外部碎片,有内部碎片(因为是把一整块分区给进程,就看进程能不能刚好用完)
3、动态分区分配:支持多道程序,在进程装入内存时,根据进程的大小动态地建立分区。
无内部碎片,有外部碎片(因为是根据进程大小动态创建分区),但外部碎片可以使用“紧凑”技术解决。 (紧凑:就是把内存中的碎片(外部碎片)拼接成一块连续的内存空间,因为进程需要一整块连续的内存空间)
回收内存分区时,可能遇到四种情况: 回收区之后有空闲分区、回收区之前有空闲分区、回收区前后都有空闲分区、回收区前后没有空闲分区。原则是:如果和空闲分区相邻,就把回收区和相邻的空闲分区合并。
动态分区分配算法
在动态分区分配中,当有很多个空闲分区都能够满足需求时,应该选择哪个分区进行分配?
注意:动态分区分配是一种连续分配方式!!! 为各进程分配的空间必须是连续的一整片区域。
1、首次适应:把空闲分区以地址递增的方式排序,每次从低地址开始查找,找到第一个能够满足大小的分区。(每次分配内存时,顺序查找空闲分区表或者空闲分区链,每次都从第一个元素开始找)
2、最佳适应:把空闲分以容量递增的方式排序,每次分配内存时顺序查找空闲分区表(链),找到第一个能够满足大小的分区。(尽可能留下大块的内存空间)
缺点:会产生很多外部碎片
3、最坏适应:和最佳适应算法相反,空闲分区以容量递减排序,这样分配后剩余的空闲分区不至于太小,不会产生很多外部碎片。
缺点:可能会放不下未来到达的大进程
4、邻近适应:针对首次适应每次都要从头开始检查的问题,同样按照地址递增次序排序,每次在上次查找的位置往下查找。由于不需要对空闲分区表(链)进行排序,算法开销小。
连续分配方式的缺点
单道程序的就不用说了,考虑支持多道程序的固定分区分配和动态分区分配。
1、固定分区分配:不够灵活,会产生大量内部碎片,内存的利用率低
2、动态分区分配:会产生大量外部碎片,虽然可以使用紧凑技术来处理,但是时间开销大
问题的关键在于:连续分配方式必须为每一个进程分配连续的内存空间!
解决方法:将进程离散地装入到许多不相邻的分区中,便可以充分利用内存,不再需要紧凑技术。也即:非连续分配
内存分配与回收:非连续分配管理
为进程分配的可以是分散的内存空间
分页
将内存空间分为一个个大小相等的分区(每个分区就叫做:页框、页帧、内存块、物理块,并且对应有自己的编号,页框号、内存块号、页帧号、物理块号,注意编号从0开始)
同样的,将用户进程的地址空间也分为与页框(物理块)大小相等的一个个区域,称为“页面、页”,每个页面也有一个编号:页号,从0开始。
进程的最后一个页面可能没有一个页框那么大,所以页框不能太大,否则会产生过大的内部碎片(给最后一个页面的空间太大了,这个进程用不完)
页面、页是一个意思
物理块、页框、页帧、内存块是一个意思
实现逻辑地址到物理地址的转换:
关键是要知道:页号、页内偏移量和该页号在内存中的起始位置
分页地址的二进制位数表示:(更加方便计算)
为了知道每个页面在内存中存放的位置,os需要为每个进程建立一个页表
1、一个进程对应一个页表
2、进程的每一页对应一个页表项
3、页表完成页号到物理块号的映射,页表记录进程页面和实际存放的内存块之间的对应关系
4、物理地址:用物理块号(从0开始编号) * 物理块大小
基本地址变换机构
可以借助进程的页表将逻辑地址转换为物理地址,通常会在系统中设置一个页表寄存器(PTR) ,存放页表在内存中的起始地址F和页表长度M。
进程未执行时,页表的起始地址和页表长度存放在PCB(进程控制块)中,当进程被调度时,os内核会把它们放到PTR中。
注意:页号从0开始编号,页表长度=1,说明最大的页号=0,所以如果页号>=页表长度,就会产生越界中断,
页表长度:代表该页表中有多少个页表项(也就是几个页)
页表项长度:每个页表项占多大空间
页面大小:每个页面占多大空间