天天动画片 > 八卦谈 > 操作系统(五)——内存管理

操作系统(五)——内存管理

八卦谈 佚名 2024-05-10 16:27:58

一、内存管理

(一)内存的基础知识

内存管理是操作系统设计中最重要和最复杂的内容之一。计算机硬件一直在发展,内容容量也在不断增长,但是仍然不可能将所有用户进程和系统所需要的全部程序和数据全部放入主存中,所以操作系统必须将内存空间进行合理的划分和有效的动态分配。操作系统对内存的划分和动态分配,就是内存管理的概念。

有效的内存管理在多道程序设计中非常重要,不仅方便用户使用存储器、提高内存利用率,还可以通过虚拟技术从逻辑上扩充存储器。

(二)内存管理的概念

内存管理的功能有:

· 内存空间的分配与回收,包括内存的分配和共享。

· 地址转换,把逻辑地址转换成相应的物理地址。

· 内存空间的扩充,利用虚拟技术或自动覆盖技术,从逻辑上扩充内存。

· 存储保护,保证各道作业在各自存储空间内运行,互不干扰。

在进行具体的内存管理之前,需要了解进程运行的基本原理和要求。

创建进程首先要将程序和数据装入内存。将用户原程序变成可在内存中执行的程序,通常需要以下几个步骤。

· 编译,由编译程序将用户源代码编译成若干个目标模块。

· 链接,由链接程序将编译后形成的一组目标模块,以及所需库函数链接,形成完整的装入模块。

· 装入,由装入程序将装入模块装入内存。

程序的链接有以下三种方式:

· 静态链接:在程序运行之前,先将各目标模块及它们所需的库函数链接成一个完整的可执行程序,以后不再拆开。

· 装入时动态链接:将用户源程序编译后所得到的一组目标模块,再装入内存时,采用边装入变链接的方式。

· 运行时动态链接:对某些目标模块的连接,是在程序执行中需要该目标模块时,才对她进行链接。其优点是便于修改和更新,便于实现对目标模块的共享。

将逻辑地址转换成物理地址需要经过以下几个步骤:
确定逻辑地址的分段和分页情况,即确定逻辑地址中哪些位表示页号,哪些位表示页内偏移量
通过页表查询得到页表项,页表项中包含了该页对应的物理页框号。
物理页框号与逻辑地址中的页内偏移量拼接起来,得到物理地址。
将物理地址返回给CPU,CPU就可以访问该物理地址对应的内存单元了。
具体的计算公式为:
物理地址 = (物理页框号 * 页框大小)+ 页内偏移量
其中,页框大小是指一个物理页框所能容纳的字节数量。

内存的装入模块再装入内存时,同样有以下三种方式:

1)绝对装入。在编译时,如果知道程序将驻留在内存的某个位置,编译程序将产生绝对地址的目标代码。绝对装入程序按照装入模块的地址,将程序和数据装入内存。装入模块被装入内存后,由于程序中的逻辑地址与实际地址完全相同,故不需对程序和数据的地址进行修改。

绝对装入方式只适用于单道程序环境。另外,程序中所使用的绝对地址,可在编译或汇编时给出,也可由程序员直接赋予。

2)可重定位装入。在多道程序环境下,多个目标模块的起始地址通常都是从0开始,程序中的其他地址都是相对于起始地址的,此时应采用可重定位装入方式。根据内存的当前情况,将装入模块装入到内存的适当位置。装入时对目标程序中指令和数据的修改过程称为重定位,地址变换通常是装入时一次完成,所以成为静态重定位。

其特点是在一个作业装入内存时,必须分配器要求的全部内存空间,如果没有足够的内存,就不能装入,此外一旦作业进入内存后,在整个运行期间,不能在内存中移动,也不能再申请内存空间。

3)动态运行时装入,也成为动态重定位,程序在内存中如果发生移动,就需要采用动态的装入方式。

动态运行时的装入程序在把装入模块装入内存后,并不立即把装入模块中的相对地址转换为绝对地址,而是把这种地址转换推迟到程序真正要执行时才进行。因此,装入内存后的所有地址均为相对地址,这种方式需要一个重定位寄存器的支持。

其特点是可以将程序分配到不连续的存储区中;在程序运行之前可以只装入它的部分代码即可运行,然后在程序运行期间,根据需要动态申请分配内存;便于程序段的共享,可以向用户提供一个比存储空间大得多的地址空间。

编译后,一个目标程序所限定的地址范围称为改程序的逻辑地址空间。编译程序在对一个源程序进行编译时,总是从0号单元开始为其分配地址,地址空间中的所有地址都是相对起始地址0的,因而逻辑地址也称为相对地址。用户程序和程序员只需要知道逻辑地址,而内存管理的具体机制则是透明的,这些只有系统编程人员才会涉及。不同进程可以有相同的逻辑地址,因为这些相同的逻辑地址可以映射到主存的不同位置。

物理地址空间实质内存中物理单位的集合,它是地址转换的最终地址,进程在运行时执行指令和访问数据最后都要通过物理地址来存取主存。当装入程序将可执行代码装入内存时,必须通过地址转换将逻辑地址转换成物理地址,这个过程称为地址重定位。

内存分配前,需要保护操作系统不受用户进程的影响,同时保护用户进程不受其他用户进程的影响。通过采用重定位寄存器和界地址寄存器来实现这种保护。重定位寄存器含最小的物理地址值,界地址寄存器含逻辑地址值。每个逻辑地址值必须小于界地址寄存器。内存管理机构动态地将逻辑地址加上重定位寄存器的值后映射成物理地址,再送交内存单元。

当CPU调度程序选择进程执行时,派遣程序会初始化重定位寄存器和界地址寄存器。每个地址都需要与寄存器进行核对,可以保证操作系统和其他用户程序及数据不被该进程运行所影响。

物理地址、逻辑地址、有效地址、线性地址和虚拟地址都是计算机中使用的地址类型,它们的区别如下:
物理地址:也称为实际地址,是指硬件设备(如内存)上的真实地址,它是由硬件生成的。物理地址是由硬件处理器(CPU)直接使用的,用于访问计算机的物理内存和外设等硬件资源。
逻辑地址:也称为程序员视图地址,是指程序员在编写程序时使用的地址,它是相对于程序而言的。逻辑地址是程序员定义的地址,用于访问程序中的数据和指令。
有效地址:是指程序中使用的地址在执行时被翻译成的实际地址,它是逻辑地址到物理地址的转换结果。有效地址是由操作系统管理的,用于将逻辑地址转换为物理地址。
线性地址:也称为虚实地址,是指逻辑地址转换为物理地址前的中间地址。线性地址是在程序执行前由操作系统将逻辑地址转换为的地址,这个地址是在逻辑地址和物理地址之间的中间层。在现代操作系统中,线性地址和虚拟地址通常是等同的。
虚拟地址:是指应用程序所使用的地址空间,它是相对于进程而言的。虚拟地址是在进程中使用的地址,其实际的物理地址由操作系统通过页表等机制进行映射。在虚拟内存管理中,虚拟地址是由程序使用的地址,与物理地址分离,使得操作系统可以使用更大的地址空间,并且可以将内存中的数据和进程间的通信隔离开来。
静态地址重定位(static address relocation)是一种内存管理技术,常用于操作系统和编译器中。它可以使程序在编译时确定程序中所有的地址引用,并且在程序加载到内存时根据程序起始地址调整地址引用,从而避免了动态地址重定位时的地址计算开销和内存碎片问题。
在静态地址重定位技术中,程序代码中的地址引用被表示为绝对地址(absolute address),即在编译时就已经确定了。在程序加载到内存中时,操作系统会为程序分配一段连续的内存空间,并将程序中的绝对地址与程序起始地址相加得到真实的物理地址,然后将这些真实的物理地址写入程序代码中的地址引用中。这个过程被称为地址重定位
当程序需要访问内存时,CPU会直接将绝对地址作为物理地址进行读写操作。如果程序需要加载新的模块或动态扩展内存空间,则需要重新编译程序,重新确定地址引用,并将程序加载到新的内存区域中。
静态地址重定位技术的优点在于可以在编译时确定所有的地址引用,从而避免了动态地址重定位时的地址计算开销和内存碎片问题。缺点在于需要重新编译程序来支持动态扩展内存空间的功能,并且也不如动态地址重定位技术灵活。
动态地址重定位(dynamic address relocation)是一种内存管理技术,常用于操作系统和编译器中。它可以使程序在运行时动态地分配内存,并且在执行时根据实际内存地址调整程序中的地址引用,从而避免了静态地址分配时出现的地址冲突问题
在动态地址重定位技术中,程序代码中的地址引用被表示为相对地址(relative address),即相对于程序起始地址的偏移量。当程序加载到内存中时,操作系统会为程序分配一段连续的内存空间,并将程序的起始地址与每个相对地址相加,得到真实的物理地址,然后将这些真实的物理地址写入程序代码中的地址引用中。这个过程被称为地址重定位
当程序需要访问内存时,CPU会将相对地址与程序起始地址相加,得到真实的物理地址,并向该地址进行读写操作。如果程序需要加载新的模块或动态扩展内存空间,则操作系统会重新进行动态地址重定位,来保证程序中的地址引用仍然指向正确的物理地址。
动态地址重定位技术的优点在于可以动态地分配内存空间,从而提高了内存利用率;同时也可以避免静态地址分配时出现的地址冲突问题。缺点在于需要一定的额外开销来进行地址重定位,同时也需要考虑内存碎片等问题。                                                                                                           在使用虚拟内存的操作系统中,当进程访问虚拟地址空间中的数据时,操作系统会将逻辑地址转换为物理地址,这是一种动态地址重定位这个过程是通过进程页表中的页表项来完成的。页表项中记录了虚拟页号与物理页号的对应关系,操作系统根据页表项来将逻辑地址转换为物理地址。因此,进程的页表在动态地址重定位中起着非常重要的作用。
装入时动态链接(Load-time Dynamic Linking)是一种程序编译和链接技术,它可以在程序运行时动态地将程序中需要调用的函数或库函数链接到程序中。在程序编译和链接时,编译器和链接器并不将所有的函数和库函数都链接到可执行文件中,而是在程序运行时根据需要进行动态链接。这样可以减小可执行文件的大小,提高程序的运行效率,并且使得程序的更新和维护更加方便。
在装入时动态链接的过程中,操作系统会在程序装入到内存时,将程序所需要的函数和库函数动态地链接到程序中。这个过程是由操作系统中的动态链接器(Dynamic Linker)来完成的。动态链接器会在程序运行时查找需要调用的函数或库函数,并将其链接到程序中。在动态链接时,操作系统会使用一种叫做共享库(Shared Library)的机制,将多个程序所需要的库函数链接到同一个共享库中,并在程序运行时共享这个库,从而减小内存占用和提高系统的性能。
装入时动态链接是现代操作系统中常用的一种技术,它可以使得程序更加灵活和高效,同时也方便了程序的开发和维护。
重定位寄存器(Relocation Register)是在早期的计算机系统中用于实现静态地址重定位的一种硬件机制。静态地址重定位是在程序编译时完成的,将程序中的所有地址都转换为绝对地址,以便在程序加载到内存后可以直接执行,而不需要进行地址转换。在这种情况下,由于程序的起始地址是不确定的,因此需要使用重定位寄存器来保存程序的起始地址,以便在程序加载到内存时进行地址重定位。
重定位寄存器通常是一个硬件寄存器,它包含了程序的起始地址。当程序加载到内存时,操作系统会将程序的起始地址加载到重定位寄存器中,并将程序中的所有地址都加上该寄存器中的值,从而完成静态地址重定位。在这个过程中,重定位寄存器充当了一种基地址寄存器的角色,保存了程序的基地址,使得程序能够正确地访问内存中的数据。
需要注意的是,重定位寄存器只能用于实现静态地址重定位,而无法实现动态地址重定位。在现代操作系统中,通常使用虚拟内存技术来实现动态地址重定位,而不需要使用重定位寄存器这样的硬件机制。

(三)内存空间的分配与回收

连续分配方式,是指为一个用户程序分配一个连续的内存空间。它主要包括单一连续分配、固定分区分配和动态分区分配。

内存在此方式下分为系统区和用户区,系统区仅提供给操作系统使用,通常在低地址部分;用户区是为用户提供的除系统外的内存空间。这种方式无需进行内存保护。

这种方式的优点是简单、无外部碎片,可以采用覆盖技术,不需要额外的技术支持。缺点是只能用于单用户、单任务的操作系统中,有内部碎片,存储器的利用率极低。

固定分区分配是最简单的一种多道程序存储管理方式,它将内存用户空间划分为若干个固定大小的区域,每个分区只装入一道作业。当有空闲分区时,便可以再从外存的后备队列中选择适当大小的作业装入该分区。如此循环。

固定分区分配在划分分区时,有两种不同的方法:

l 分区大小相等:用于利用一台计算机去控制多个相同对象的场合。

l 分区大小不等:划分为含有多个较小的分区、适量的中等分区及少量的大分区。

为了便于内存分配,通常将分区按大小排队,并为之建立一张分区使用表,其中个表项包括每个分区的起始地址、大小及状态。当有用户程序要装入时,便检索该表,已找到合适的分区给与分配并将其状态置为“已分配“。未找到合适分区则拒绝为该用户程序分配内存。

这种分区方式存在两个问题:一个程序可能太大而放不进任何一个分区中,这是用户不得不使用覆盖技术来使用内存空间;二是主存利用率低,当程序小于固定分区大小时,也占用了一个完整的内存分区空间,这样分区内部有空间浪费。这种现象成为内部碎片。

固定分区可用于多道程序设计最简单的存储分配,但不能实现多进程共享一个主存区,所以存储空间利用率低。固定分区分配很少用于现在通用的计算机,但在某些用于控制多个相同对象的控制系统中仍发挥着一定的作用。

静态分区:
· 在系统启动时就将内存划分为若干个固定大小的分区。
· 每个分区都有自己的大小和位置,分配和释放内存时只能在这些预先划分好的分区中进行操作。
· 分配和释放内存比较简单,但是可能会浪费内存资源。
动态分区:
· 内存大小不是固定的,而是根据需要动态分配。
· 每个进程需要多少内存,系统就会为其分配多少内存。
· 内存分配和释放比较灵活,可以最大限度地利用内存资源。
因此,静态分区适用于内存资源比较稳定、进程数量相对较少的场景,而动态分区适用于内存资源变化较大、进程数量相对较多的场景。
空闲分区表和空闲分区链是指操作系统中管理内存分配和回收的两种数据结构。
空闲分区表是指将所有的内存空闲区域按照地址从小到大进行排序,并以表格的形式记录下来。它通常由操作系统内核维护,并且在每次内存分配或回收时进行更新。空闲分区表记录了每个空闲区域的起始地址和大小,以及该区域是否被占用等信息。在内存分配时,操作系统会先查找空闲分区表来确定是否有足够的空闲内存区域可供分配。如果有,则从中选取一个大小合适的空闲区域进行分配;如果没有,则需要等待或者触发内存回收操作。
空闲分区链是指将所有空闲区域按照大小从小到大进行排序,并将它们链接成一个链表。这个链表中的每个节点记录了一个空闲区域的大小和起始地址。当需要分配内存时,操作系统会从链表的头部开始查找,找到第一个大小合适的空闲区域进行分配。分配后,操作系统会将该空闲区域从链表中删除,并将剩余部分重新插入链表中。这样,空闲分区链可以避免内存碎片的问题,提高内存利用率。                                                                                                                                       内存回收和垃圾回收不是一回事。它们虽然都与内存管理有关,但是解决的问题不同。
内存回收是指在程序运行过程中,当程序不再需要使用某个内存区域时,将该内存区域释放出来,使其成为可用的空闲内存。内存回收通常由程序员手动进行,例如在程序中调用free函数来释放内存,或者在C++中使用析构函数来释放内存。
垃圾回收是指一种自动内存管理技术,它主要应用于高级编程语言中,例如Java、Python等。垃圾回收器会自动监视程序中的内存使用情况,并在程序运行过程中自动回收不再使用的内存空间。垃圾回收器通过扫描内存中的对象,判断哪些对象已经不再被程序使用,然后自动将其释放,以便重复利用。垃圾回收器可以有效地减少内存泄漏和内存溢出等问题,减轻程序员的负担,提高程序的健壮性和可维护性。

动态分区分配又称为可变分区分配,是一种动态划分内存的分区方法。这种分区方法预先将内存划分,而是在进程装入内存时,根据进程的大小动态的建立分区,并使分区的大小正好适合进程的需要。因此系统中分区的大小和数目是可变的。

动态分区在开始分配时是很好的,但是之后会导致内存中出现许多小的内存块。随着时间的推移,内存中会产生越来越多的碎片,内存的利用率随之下降。这种现象称之为外部碎片现象,指在所有分区外的存储空间会变成越来越多的碎片,这与固定分区中的内部碎片正好相对。克服外部碎片可以通过紧凑技术来解决,就是操作系统不时地对进程进行移动和整理。但是这需要动态定位的支持,且相对费时。紧凑的过程实际上类似于windows系统中的磁盘整理程序,只不过后者是对外存空间的紧凑。

在进程装入或换入主存时。如果内存中有多个足够大的空闲块,操作系统必须确定分配那个内存块给进程使用,这就是动态分区的分配策略。考虑以下几种算法:

1)首次适应算法:空闲分区以地址递增的次序链接。分配内存时顺序查找,找到大小能满足要求的第一个空闲分区。

2)最佳适应算法:空闲分区按容量递增形成分区链,找到第一个能满足要求的空闲分区。

3)最坏适应算法:又称最大适应算法,空闲分区以容量递减次序链接。找到第一个能满足要求的空闲分区,也就是挑选最大的分区。

4)临近适应算法:又称循环首次适应算法,由首次适应算法演变而成。不同之处是分配内存时从此查找结束的位置开始继续查找。

在这几种方法中,首次适应算法不仅是最简单的,而且通常是最好和最快的。在UNIX系统的最初版本中,就是使用首次适应算法为进程分配内存空间,其中使用数组的数据结构(而非链表)来实现。不过,首次适应算法会使得内存的低地址部分出现很多小的空闲分区,而每次分配查找时,都要经过这些分区。

临近适应算法试图解决这一问题,但实际上,它常常会导致在内存的末尾分配空间,分裂成小碎片。它通常比首次适应算法的结果要差。

最佳适应算法虽然称为最佳,但是性能通常很差,因为每次最佳的分配会留下最小的内存块,它会产生最多的碎片。

最坏适应算法与最佳适应算法相反,选择最大的可用块,这看起来最不容易产生碎片,但是却把最大的连续内存划分开,会很快导致没有可用的大的内存块,因此性能非常差。

以上内存分区管理方法有一共同特点,即用户进程在主存中都是连续存放的。

动态分区分配算法是一种内存管理技术,用于在程序运行时动态地分配内存空间,并在不需要的时候释放内存空间。动态分区分配算法的主要目标是最大化内存的利用率,同时尽可能地避免内存碎片。
常见的动态分区分配算法包括以下几种:
首次适应算法(First Fit):从内存的起始位置开始扫描,找到第一个满足要求的空闲块,并将其分配给请求的进程。这种算法的优点是实现简单,但是可能会导致内存碎片。
循环首次适应算法(Next Fit):与首次适应算法类似,但是从上一次分配的位置开始往后扫描,以避免每次都从起始位置开始扫描。
最佳适应算法(Best Fit):从所有空闲块中找到最小的满足要求的块,并将其分配给请求的进程。这种算法可以最小化内存碎片,但是需要更多的时间来搜索合适的空闲块。
最坏适应算法(Worst Fit):从所有空闲块中找到最大的满足要求的块,并将其分配给请求的进程。这种算法可以减少内存碎片,但是可能会导致大量的浪费空间。
总体来说,动态分区分配算法都有其优缺点,选择合适的算法需要根据具体的应用场景来决定。
内存紧缩(Memory Compaction)是一种内存管理技术,用于解决内存碎片问题。在程序运行一段时间后,内存中可能会出现大量的内存碎片,这些碎片可能会导致内存利用率下降,从而影响程序的性能。通过内存紧缩,可以将散落在内存中的碎片整理成一个或多个连续的内存块,从而提高内存的利用率。
内存紧缩的实现可以分为以下几个步骤:
找出所有空闲块:内存紧缩需要将所有的内存碎片整理成连续的内存块,因此需要首先找出所有的空闲块。
将所有空闲块合并:将相邻的空闲块合并成一个更大的空闲块,直到无法继续合并。
移动内存中的数据:将内存中的数据移动到新的内存位置上,以便将空闲块整理成连续的内存块。需要注意的是,移动数据时需要保持数据的完整性和正确性。
更新指针和引用:由于移动了内存中的数据,因此需要更新指向这些数据的指针和引用,以确保程序能够正确地访问这些数据。
需要注意的是,内存紧缩可能会对程序的性能产生一定的影响,因为它需要移动大量的数据。因此,在实际的应用场景中,需要权衡内存紧缩的效果和影响,选择合适的内存管理策略。
内部碎片和外部碎片是指在内存分配过程中出现的两种不同的浪费现象。
内部碎片指的是在内存分配时,由于内存分配单位的大小和所需内存的大小不匹配,导致分配的内存空间中存在一些未被利用的小块空间,这些小块空间无法再次被利用,造成内存浪费。
外部碎片则是在内存分配时,由于已经分配给某个进程的内存空间中存在一些被释放出来的空闲块,这些空闲块的大小不足以满足其他进程的内存需求,导致内存无法得到充分利用,也造成了内存浪费。
简而言之,内部碎片是在已分配的内存空间内部存在未被利用的小块空间,而外部碎片则是由于已分配的内存空间中存在一些不连续的空闲块导致的内存浪费。

内部碎片问题:
内部碎片是指已分配给进程的内存空间中,有一部分空间没有被利用,但是由于其大小不足以再次被分配给其他进程使用,会造成内存的浪费。为了解决内部碎片问题,可以采取以下措施:
采用动态分配内存的方式,只给进程分配它所需要的内存大小,避免过多的内存浪费。
采用内存对齐的方式,在分配内存空间时,将其大小调整为特定的倍数,这样可以避免因为内存不足而造成的内部碎片。
采用内存池技术,将多个小的内存空间合并为一个大的内存池,从而避免内存碎片的产生。
外部碎片问题:
外部碎片是指已分配给进程的内存空间中,有一些空闲的内存块,但它们的大小不连续,不能被合并利用,导致内存无法继续分配给需要更大内存的进程。为了解决外部碎片问题,可以采取以下措施:
采用内存紧缩技术,将内存中的所有进程重新整理,将它们紧密排列,从而消除外部碎片。
采用虚拟内存技术,将物理内存和磁盘空间结合起来,将不常用的进程或数据保存到磁盘上,从而增加可用内存的大小。
采用内存映射技术,将文件映射到内存中,这样可以减少内存的使用,从而避免外部碎片的产生。

(四)覆盖与交换

覆盖与交换技术是在多道程序环境下用来扩充内存的两种方法。覆盖技术主要用在早期的操作系统中,而交换技术则在现代操作系统中仍具有较强的生命力。

早期的计算机系统中,主存容量很小,虽然主存中仅存放一道用户程序,但是存储空间放不下用户进程的现象也经常发生,这一矛盾可以用覆盖技术来解决。其基本思想是:由于程序运行时并非任何时候都要访问程序和数据的各个部分,因此可以把用户空间分成一个固定区和若干个覆盖区。将经常活跃的部分放在固定区,其余部分按调用关系分段。首先将那些将要访问的段放入覆盖区,其他段放在外存中,在需要调用时,系统再将其调入覆盖区,替换其中原有的段。

交换的基本思想是:把处于等待状态(或在CPU调度原则下被剥夺运行权利)的进程从内存移到辅存(外存),把内存空间腾出来,这一过程又叫换出;把准备好竞争CPU运行的进程从辅存移到内存,这一过程又称为换入

例如,有一个CPU采用时间片轮转调度算法的多道程序环境。时间片到,内存管理器将刚刚执行过的进程换出,将另一进程换入到刚刚释放的内存空间中。同时,CPU调度器可以将时间片分配给其他已在内存中的进程。每个进程用完时间片都与另一进程交换。理想情况下。内存管理器的交换过程速度足够快,总有进程在内存中可以执行。

有关交换需要注意以下几个问题:

· 交换需要备份存储,通常是快速磁盘。它必须足够大,并且提供对这些内存影响的直接访问。

· 为了有效使用CPU,需要每个进程的执行时间比交换时间长,而影响交换时间的主要是转移时间。转移时间与所见换的内存空间成正比。

· 如果换出进程,必须确保该进程是完全处于空闲状态。

· 交换空间通常作为磁盘的一整块,且独立与文件系统,因此使用就可能很快。

· 交换通常在有许多进程运行且内存空间吃紧的时候开始启动,而系统负荷降低就暂停。

· 普通的交换使用不多,但交换策略的某些变种在许多系统中仍发挥作用。

交换技术主要是在不同进程之间进行,而覆盖则用于同一个程序中。由于覆盖技术要求给程序段之间的覆盖结构,使得其对用户和程序员不透明,所以对于主存无法存放用户程序的矛盾,现在操作系统是通过虚拟内存技术来解决的,覆盖技术则已成为历史;而交换技术在现代操作系统中仍具有较强的生命力。

对换区用到交换技术,交换技术帮助对换区中进程或数据在内存和磁盘之间快速交换移动,从而提高系统的效率和性能。常见交换技术包括页面交换、分区交换、段式交换等。

(五)页式存储管理(分页存储管理)

非连续分配方式允许一个程序分散的装入不相邻的内存分区中,根据分区的大小是否固定分为分页存储管理方式分段存储管理方式

分页存储管理方式中,又根据运行作业时是否要把作业的所有页面都装入内存才能运行分为基本分页存储管理请求分页存储管理方式

固定分区会产生内部碎片,动态分区会产生外部碎片,两种技术对内存的利用率都比较低。我们希望内存的使用能尽量避免碎片的产生,这就引出了分页思想:把主存空间划分为大小相等且固定的块,块相对较小,作为主存的基本单位每个进程也以块为单位进行划分,进程在执行时,以块为单位逐个申请主存中的块空间

分页存储和分段存储是操作系统中内存管理的两种方式,它们的主要目的是为了更加高效地利用内存资源,实现进程的运行和存储。
分页存储的目的是将进程分成大小相等的若干个页面,每个页面的大小是固定的,一般为2的幂次方,例如4KB或者8KB。这样可以更加方便地管理内存,也可以更好地利用内存空间。分页存储可以将虚拟内存空间和物理内存空间分离,从而实现了更加灵活的内存管理。同时,分页存储还可以实现页面的置换,当内存不够用时,可以将一些不常用的页面置换到硬盘上,从而腾出空间来为新的页面提供服务。
分段存储的目的是将进程划分成若干个大小不等的段,每个段对应一个逻辑单元,例如代码段、数据段、堆栈段等。每个段的大小可以根据需要动态变化,从而更好地满足进程的需求。分段存储可以将进程的不同部分存储在不同的物理地址空间,并且可以为每个段设置不同的保护属性,从而实现了更加灵活的内存管理。同时,分段存储还可以支持动态链接和共享库等高级特性,从而提高了程序的执行效率和可复用性。
综上所述,分页存储和分段存储的目的都是为了更加高效地利用内存资源,实现进程的运行和存储。选择哪种方式要根据具体的应用场景和需求来决定。

分页是把整个虚拟和物理内存空间切成一段段固定尺寸的大小。这样一个连续并且尺寸固定的内存空间,叫(Page)。在 Linux 下,每一页的大小为 4KB。

页表是操作系统中用于实现虚拟内存的重要数据结构,它用于将虚拟地址转换为物理地址。以下是页表的两种实现方法:
基于硬件的页表实现:
硬件页表是一种由硬件支持的页表实现方法,它使用专门的硬件(如MMU)来实现地址转换。硬件页表通常由页表基址寄存器(Page Table Base Register, PTBR)、页表长度寄存器(Page Table Length Register, PTLR)、页表项和页表缓存TLB(Translation Lookaside Buffer)组成。当CPU访问虚拟地址时,硬件会根据PTBR中保存的页表基址和虚拟地址中的页表索引查找对应的页表项,并将物理地址返回给CPU。
基于软件的页表实现:
软件页表是一种由操作系统内核实现的页表实现方法,它使用操作系统内核中的数据结构(如二级页表、三级页表)来实现地址转换。软件页表通常由页目录表、页表和页表项组成。当CPU访问虚拟地址时,软件会根据虚拟地址中的页目录表索引和页表索引查找对应的页表项,并将物理地址返回给CPU。
总的来说,基于硬件的页表实现速度更快,但需要硬件支持;而基于软件的页表实现速度较慢,但不需要硬件支持,可以灵活地调整页表大小和结构。
页框(物理块):将内存空间分成一个个大小相等的分区(页框号或物理块号从0开始);页(页面):将用户进程的地址空间分为与页框大小相等的一个个区域(页号一般也从0开始);页表:系统为每个进程建立的页面映像表。在地址空间内的所有页(0~n),依次在页表中有一页表项,记录了相应页在内存块中对应的物理块号。
与简单页式管理相对应的是复杂页式管理。复杂页式管理相对于简单页式管理来说,增加了更多的特性和机制,以提高内存管理的灵活性和效率。例如,复杂页式管理可以支持不同大小的页,以适应不同程序的需要;它还可以支持虚拟内存技术,使得程序可以使用比物理内存更大的地址空间;此外,它还可以支持页面置换算法和内存保护机制等。总之,复杂页式管理相对于简单页式管理来说,具有更多的功能和可扩展性,适用于更加复杂的内存管理场景。


1.页面和页面大小

进程中的块称为页,内存中的块称为页框。外存也以同样单位划分,直接称为块。进程在执行时需要申请主存空间,就是要为每个页面分配主存中的可用页框,这就产生了页和页框的一一对应。

为了方便地址转换,页面大小应是2的整数幂。同时页面大小应当适中。如果页面太小,会是进程的页面数过多,这样页表就过长,占用大量内存,而且也会增加硬件地址转换的开销,降低页面换入换出的效率。页面过大又会是页面碎片过大,降低内存利用率。所以页面的大小应该适中,考虑到空间效率和时间效率。

2.地址结构

分页存储管理的地址结构包含两部分:前一部分为页号,后一部分为页内偏移量W。地址长度为32位,其中0~11为页内地址,即每页大小为4kB;12~31位为页号,地址空间最多允许有220页。

3.页表

为了便于在内存中找到进程的每个页面所对应的物理块,系统为每个进程建立一张页表,记录页面在内存中对应的物理块号,页表一般存放在内存中。

在配置了页表后,进程执行时,通过查找该表,即可找到每页在内存中的物理块号。可见,页表的作用是实现从页号到物理块号的地址映射。

页表是操作系统中用于实现虚拟内存的重要数据结构,它用于将虚拟地址转换为物理地址。页表通过记录页面和实际存放的内存块之间的映射关系来实现地址转换。具体来说,页表中的每个页表项都记录了一个虚拟页和一个实际物理页之间的映射关系。
在32位的x86体系结构中,一个虚拟地址通常由32个比特位组成,其中高20位用于表示页表索引,低12位用于表示页内偏移量。因此,页表通常分为两级:页目录表和页表页目录表用于存放页表的起始地址,而页表则用于存放实际的页表项。每个页表项通常由以下字段组成:
物理页框号(Physical Frame Number, PFN):表示实际的物理页框号;
有效位(Valid Bit):表示该页表项是否有效;
读写位(Read/Write Bit):表示该页是否可读写;
访问位(Accessed Bit):表示该页是否被访问过;
修改位(Dirty Bit):表示该页是否被修改过;
其他位(其他标志位):用于记录缓存、共享和权限等信息。
当CPU访问虚拟地址时,操作系统会根据虚拟地址的高20位查找页目录表,并根据页目录表中的页表项指向页表。然后,操作系统会根据虚拟地址的中间10位查找页表,并根据页表中的页表项得到实际的物理页框号。最后,操作系统将物理页框号和虚拟地址的低12位组合起来,得到实际的物理地址。这样,就完成了从虚拟地址到物理地址的转换。

页表中记录了页面在内存中的起始地址,但通常不会记录页内偏移量。页内偏移量是指逻辑地址在页面内部的偏移量,它是由CPU生成的,页表中并不会记录这个值。页表中的表项通常只记录页面在内存中的起始地址,以及一些其他的信息,如页面是否被修改过、是否被访问过等等。当CPU访问某个逻辑地址时,会首先根据页表得到对应的物理地址,然后再将页内偏移量加上物理地址,得到真正的物理地址,最终访问物理地址所对应的内存单元。                                          地址转换是指将逻辑地址转换为物理地址的过程。逻辑地址是由程序生成的地址,程序使用逻辑地址来访问内存中的数据。物理地址是实际的硬件地址,用于访问内存中的物理存储单元。在现代计算机系统中,采用虚拟内存技术来管理内存,程序使用的逻辑地址并不是直接映射到物理地址的,而是需要通过地址转换来获得对应的物理地址。地址转换的过程通常由硬件中的内存管理单元(MMU)来完成MMU根据系统中的页表,将逻辑地址转换为物理地址。页表是一种数据结构,用于记录逻辑地址和物理地址之间的映射关系。当CPU访问内存时,它首先生成一个逻辑地址,然后将逻辑地址发送给MMU进行地址转换。MMU根据页表将逻辑地址转换为物理地址,然后将物理地址发送给内存控制器,最终访问物理地址所对应的内存单元。每个进程都有自己的页表,用于将虚拟地址转换为物理地址。在操作系统中,每个进程都有自己独立的地址空间,因此需要有自己的页表来管理这些地址空间。如果多个进程共享相同的物理页面,则它们可以共享相同的页表条目。
CPU在访问内存时生成逻辑地址,生成逻辑地址的过程发生在CPU进行地址转换之前。当CPU访问内存时,它首先生成一个逻辑地址(虚拟地址),包含两个部分:页号和页内偏移量页号用于在页表中查找对应的物理页框号,页内偏移量则用于计算该逻辑地址在物理页框中的偏移量。CPU将虚拟地址发送给内存管理单元(MMU),MMU根据页表将虚拟地址转换为物理地址,然后将物理地址发送给内存控制器,最终访问物理地址所对应的内存单元。因此,CPU生成虚拟地址的过程是在地址转换之前发生的。[
逻辑地址存在于程序代码和数据中。逻辑地址是由程序生成的地址,程序使用逻辑地址来访问内存中的数据。]

4.基本地址变换机构

地址变换机构是实现逻辑地址到物理地址转换的一组硬件机构。

地址变换机构的任务是将逻辑地址中的页号,转换为内存中物理块号,地址变换是借助于页表实现的。

在系统中通常设置一个页表寄存器PTR,存放页表在内存的初值和页表长度。

逻辑地址到物理地址的变换过程如下:

1地址变换机构自动将有效地址分为页号和页内偏移量两部分,再用页号去检索页表。在执行检索之前,先将页号与页表长度比较,如果页号大于或等于页表长度,则表示地址越界并中断。

2若未越界,则将页表始址与页号和页表项长度的乘积相加,便得到该表项在页表中的位置,于是可从中得到该页的物理块号。

3与此同时,在将有效地址中的页内偏移量送入物理地址寄存器的块内地址字段中。

以上整个地址变换过程均是由硬件自动完成的。

下面讨论分页管理方式存在的两个主要问题:1每次访存操作都需要进行逻辑地址到物理地址的转换,地址转换过程必须足够快,否则访存速度会降低;2每个进程引入了页表,用于存储映射机制,页表不能太大,否则内存利用率会降低。

页框(page frame)是内存中一段固定大小的物理存储区域,通常被划分成大小相同的页面(page)。在虚拟内存管理中,每个进程被分配一段虚拟地址空间,这些虚拟地址会被映射到实际的物理页框上。当程序访问虚拟地址时,操作系统将其转换为对应的物理地址,如果所需的页不在内存中,则触发页错误,并将相应的页从磁盘中读取到内存中的一个空闲页框中。页框的大小通常为4KB或者更大,具体取决于硬件和操作系统的设置。页表和页框之间的关系就在于,页表通过映射虚拟地址到物理地址的方式来管理页框中的物理内存。

5.具有快表的地址变换机构

由上面介绍的地址变换过程可知,若页表全部放在内存中,则要存取一个数据或一条指令至少要访问两次内存:一次是访问页表,确定要存取的数据或指令的物理地址,第二次才根据该地址存取数据或指令。显然,这种方法比通常执行指令的速度慢了一半。

为此,在地址变换机构中增设了一个具有并行查找能力的高速缓冲存储器——快表,又称联想寄存器TLB,用以存放当前访问的若干页表项。与此对应,主存中的页表也常称为慢表。

在具有快表的分页机制中,地址的变换过程:

1.CPU给出有效地址后,由硬件进行地址转换,并将页号送入高速缓存寄存器,并将此页号与快表中的所有页号同时进行比较。

2.如果有找到匹配的页号,说明索要访问的页表项在快表中,则可以直接从中读出该页对应的页框号,送到屋里地址寄存器。这样存取数据可以直接一次访存实现。

3.如果没有找到,则需要访问主存中的页表,在读出页表项后,应同时将其存入快表中,以供后面可能的再次访问。但是如果快表已满,就必须按照一定的算法对其中旧的页表项进行替换。注意,有些处理器设计为快表和主存同时查找,如果在快表中匹配成功则终止主存中的查找。

一般快表的命中率可以达到90%,这样,分页带来的速度损失就降到10%。快表的有效性是基于著名的局部性原理。

6.两级(多级)页表

由于引入了分页管理,进程在执行时不需要将所有页调入内存页框中,而只要将保存有映射关系的页表调入内存即可。但是我们仍然需要考虑页表的大小。如果页表太大,肯定是降低了内存利用率的;从另一方面来说,程序所有的页表项也并不需要同时保存在内存中,因为在大多数情况下,映射所需要的页表都再也表的同一个页面中。

我们将页表映射的思想进一步延伸,就可以得到二级分页:将页表的10页空间也进行地址映射,建立上一级页表,所以上一级页表只需要一页就足够。在进程执行时,只需要将这一页上一级页表调入内存即可,进程的页表和进程本身的页面,可以在后面的执行中再调入内存。

页面(Page)是在虚拟内存管理中划分物理内存的最小单位,而页表项(Page Table Entry,PTE)则是一个数据结构,用于记录每个页面的信息和状态。具体来说,每个页面都有对应的一页表项,一页表项中记录了该页面的物理地址、页面状态、访问权限、缓存策略等信息。当CPU执行程序时,会根据程序中的虚拟地址访问内存,此时操作系统通过查找虚拟地址所对应的页表项,从而确定该虚拟地址对应的物理地址,然后返回给CPU,完成内存访问操作。因此,页面和页表项之间的关系是一一对应的,每个页面都对应着一个页表项,页表项中记录了页面的相关信息和状态,以便CPU能够正确地访问内存。每个页面大小通常是固定的,通常为4KB(或4096字节)。每个页表项的大小取决于所使用的体系结构和页表格式。例如,在x86架构中,每个页表项大小为4字节或8字节,具体取决于是否启用了物理地址扩展(PAE)模式。因此,每个页面可以容纳的页表项数量取决于这些因素。在x86架构中,一个4KB的页面最多可以容纳1024个4字节的页表项或512个8字节的页表项。
多级页表相对于单级页表的优点是可以节省内存空间,因为多级页表允许分配更小的页框大小,从而减少每个进程所需的页表大小。此外,多级页表还可提高页面访问速度,因为通过使用多级索引,可以更快地查找和访问虚拟地址的物理位置。
缺点是增加了页表遍历的时间和开销,因为需要在多个层次上进行访问和检索。因此,在大型内存系统中,多级页表可能会导致较长的访问时间。另外,多级页表也可能会增加代码实现的复杂性,因为需要处理多个层次的索引来定位物理内存位置。

(六)段式存储管理(分段存储管理)

1.分段

短时系统按照用户进程中的自然段划分逻辑空间。例如,用户进程由主程序、两个子程序、栈和一段数据组成,于是可以把这个用户进程划分为5个段,每段从0开始编址,并采用一段连续的地址空间(段内要求连续,段间不要求连续),其逻辑地址由两部分组成:段号与段内偏移量,分别记为S、W。

段号为16位,段内偏移量为16位,则一个作业最多可有2^16=65536个段,最大段长64KB。

在页式系统中,逻辑地址的页号和页内偏移量对用户是透明的;但在段式系统中,段号和段内偏移量必须由用户显示提供,在高级程序设计语言中,这个工作由编译程序完成。

2.段表(Segment Table

每个进程都有一张逻辑空间与主存空间映射的段表,其中每一段表项对应进程的一个段,段表项纪录路该段在内存中的起始地址和段的长度。

在配置了段表后,执行中的进程可通过查找段表,找到每个段所对应的内存区。可见,段表用于实现从逻辑端段到物理内存区的映射。

分页和分段的比较

  • 对程序员的透明性:分页透明,但是分段需要程序员显式划分每个段。

  • 地址空间的维度:分页是一维地址空间,分段是二维的。

  • 大小是否可以改变:页的大小不可变,段的大小可以动态改变。

  • 出现的原因:分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。

3.地址变换机构

为了实现进程从逻辑地址到物理地址的变换功能,在系统中设置了段表寄存器,用于存放段表始址和段表长度TL。在进行地址变换时,系统将逻辑地址中的段号,与段表长度TL比较。若段号>段表长度,表示段号太大,访问越界,于是产生越界中断信号。若未越界,则根据段表的始址和该段的段号,计算出该段对应段表项的位置,从中读出该段在内存中的起始地址。然后,在检查段内地址W是否超过该段的段长SL。若超过,同样发出越界中断信号。若未越界,则将该段的基址d与段内地址相加,即可得到要访问的内存物理地址。

(七)段页式内存管理

页式存储管理能有效的提高内存利用率,而分段存储管理能反映程序的逻辑结构并有利于段的共享。如果将这两种存储管理方法结合起来,就形成了段页式存储管理方式。

在段页式系统中,作业的地址空间首先被分成若干个逻辑段,每段都有自己的段号,然后再将每一段分成若干个大小固定的页。对内存空间的管理仍然和分页存储管理一样,将其分成若干个和页面大小相同的存储块,对内存的分配以存储块为单位。

在段页式系统中,作业的逻辑地址分为三部分:段号、段内页号和页内偏移量。

为了实现地址变换,系统为每个进程建立一张段表,而每个分段有一张页表。段表表项中至少包括段号、页表长度和页表起始地址,页表表项中至少包括页号和块号。此外,系统中还应有一个段表寄存器,指出作业的段表起始地址和段表长度。

在进行地址变换时,首先通过段表查到页表起始地址,然后通过页表找到帧号,最后形成物理地址。进行一次访问实际需要三次访问主存(第一次访问段表,得到页表起始地址;第二次访问页表,得到物理页号;第三次将物理页号与页内位移组合,得到物理地址),这里同样可以使用快表提供加快速度,其关键字由段号、页号组成,值是对应的页帧号和保护码。

可用软、硬件相结合的方法实现段页式地址变换,这样虽然增加了硬件成本和系统开销,但提高了内存的利用率。

二、虚拟内存管理

  • 程序所使用的内存地址叫做虚拟内存地址(Virtual Memory Address)

  • 实际存在硬件里面的空间地址叫物理内存地址(Physical Memory Address)。

操作系统引入了虚拟内存,进程持有的虚拟地址会通过 CPU 芯片中的内存管理单元(MMU)的映射关系,来转换变成物理地址,然后再通过物理地址访问内存。

(一)虚拟内存的基本概念

上一节所讨论的各种内存管理策略都是为了同时将多个进程保存在内存中以便允许多道程序设计。他们都具有以下两个共同特征:

1)一次性:作业必须一次性全部装入内存后,方可运行。这会导致两种情况发生:1当作业很大,不能全部被装入内存时,将使该作业无法运行;2当大量作业要求运行时,由于内存不足以容纳所有作业,只能使少数作业先运行,导致系统难以运行多道程序。

2)驻留性:作业被装入内存后,就一直驻留在内存中,其任何部分都不会被换出,直至作业运行结束。运行中的进程,会因等待IO而被阻塞,可能处于长期等待状态。

由上分析可知,许多在程序运行中不用或暂时不用的程序(数据)占据了大量的内存空间,而一些需要运行的作业又无法装入运行,显然浪费了宝贵的内存空间。

要真正理解虚拟内存技术的思想,首先必须了解计算机中著名的局部性原理。著名的Bill Joy说过:“在研究所的时候, 我经常开玩笑的说高速缓存是计算机科学中唯一重要的思想。事实上,高速缓存技术确实极大地影响了计算机系统的设计。”快表、页高速缓存以及虚拟内存技术从广义上讲,都是属于高速缓存技术。这个技术所依赖的原理就是局部性原理。局部性原理既适用于程序结构,也适用于数据结构。

局部性原理表现在以下两个方面:

1)时间局部性。如果程序中的某条指令一旦执行,则不久以后该指令可能再次执行;如果某数据被访问过,则不久以后该数据可能再次被访问。产生时间局部性的典型原因,是由于在程序中存在着大量的循环操作。

2)空间局部性。一旦程序访问量某个存储单元,在不久之后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,其典型情况便是程序的顺序执行。

时间局部性是通过将进来使用的指令和数据保存到高速缓存存储器中,并使用高速缓存的层次结构实现。空间局部性通常是使用较大的高速缓存,并将预取机制集成到高速缓存控制逻辑中实现。虚拟内存技术实际上就是建立了“内存-外存”的两级存储器的结构,利用局部性原理实现高速缓存。

基于局部性原理,在程序装入时,可以将程序的一部分装入内存,而将其与部分留在外存,就可以启动程序执行。在程序执行过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调入内存,然后继续执行程序。另一方面,操作系统将内存中暂时不使用的内容换到外存上,从而腾出空间存放将要调入内存的信息。这样,计算机好像为用户提供了一个比实际内存大的多的存储器,成为虚拟存储器。

之所以将其称为虚拟存储器,是因为这种存储器实际上并不存在,只是由于系统提供了部分装入、请求调入和置换功能后,给用户的感觉是好像存在一个比实际物理内存大得多的存储器。虚拟存储器有以下三个主要特征:

1)多次性,是指无需在作业运行时一次性地全部装入内存,而是允许被分成多次调入内存运行。

2)对换性,是指无需在作业运行时一直常驻内存,而是允许在作业的运行过程中,进行换进和换出。

3)虚拟性 ,是指从逻辑上扩充内存的容量,是用户所看到的内存容量,远大于实际的内存容量。

虚拟内存中,允许讲一个作业分多次调入内存。采用连续分配方式时,会是相当一部分内存空间都处于暂时或永久的空闲状态,造成内存资源的严重浪费,而且也无法从逻辑上扩大内存容量。因此,虚拟内存的实现需要建立在离散分配的内存管理方式的基础上。

虚拟内存的实现有以下三种方式:

· 请求分页存储管理

· 请求分段存储管理

· 请求段页式存储管理

不管哪种方式,都需要有一定的硬件支持。一般需要的支持有以下几个方面:

· 一定容量的内存和外存。

· 页表机制或段表机制,作为主要的数据结构。

· 中断机构,当用户程序要访问的部分尚未调入内存,则产生中断。

· 地址变换机构,逻辑地址到物理地址的变换。

(二)请求分页管理方式

请求分页系统建立在基本分页系统基础上,为了支持虚拟存储器功能而增加了请求调页功能和页面置换功能。请求分页是目前最常用的一种实现虚拟存储器的方法。

在请求分页系统中,只要求将当前需要的一部分页面装入内存,便可以启动作业运行。在作业执行过程中,当所要访问的页面不在内存时,再通过调页功能将其调入,同时还可以通过置换功能将暂时不用的页面换出到外存上,以便腾出内存空间。

为了实现请求分页,系统必须提供一定的硬件支持。除了需要一定容量的内存及外存的计算机系统,还需要有页表机制缺页中断机构地址变换机构

页表机制不同于基本分页系统,请求分页系统在一作业运行之前不要求全部一次性调入内存,因此在作业的运行过程中,必然会出现要访问的页不在内存的情况,如何发现和处理这种情况是请求分页系统必须解决的两个基本问题。为此,在请求页表项中增加了四个字段:状态位P、访问字段A、修改位M、外存地址。

增加的四个字段说明如下:

状态位P:用于指示该页是否已调入内存,供程序访问时参考。

访问字段A:用于记录本页在一段时间内被访问的次数,或记录本页最近已有多长时间未被访问,供置换算法换出页面时参考。

修改位M:表示该页在调入内存后是否被修改过。

外存地址:用于指出该页在外存上的地址,通常是物理块号,供调入该页时参考。

在分页系统中,当CPU请求读取一页不在内存中的数据时,需要把这一页从外存调入内存,这个过程就叫做调页

在请求分页系统中,每当所要访问的页面不在内存时,便产生一个缺页中断,请求操作系统将所缺的页调入内存。缺页中断作为中断同样要经历诸如:保护CPU环境、分析中断原因、转入缺页中断处理程序进行处理、恢复CPU环境等几个步骤。但与一般的中断相比,它有以下两个明显的区别:

①在指令执行期间产生和处理中断信号,而非一条指令执行完后;②一条指令在执行期间,可能产生多次缺页中断。

请求分页系统中的地址变换机构,是在分页系统地址变换机构的基础上,为实现虚拟内存,又增加了某些功能而形成的。

在进行地址变换时,先检索快表。若找到要访问的页,边修改页表中的访问位,然后利用页表项中给出的物理块号和页内地址形成物理地址。若为找到该页的页表项,应到内存中去查找页表,在对比页表项中的状态位P,看该页是否已调入内存,未调入则产生缺页中断,请求从外存把该页调入内存。

(三)页面置换算法

进程运行时,若其访问的页面不在内存而需将其调入,但内存已无空闲空间时,就需要从内存中调出一页程序或数据,送入磁盘的对换区。而选择调出页面的算法就成为页面置换算法。好的页面置换算法应有较低的页面更换频率,也就是说,应将以后不会在访问或者以后较长时间内不会访问的页面先调出。

常见的置换算法有以下四种:

最佳置换算法所选择的被淘汰页面将是以后永不使用的,或者是在最长时间内不再被访问的页面,这样可以保证获得最低的缺页率。但由于人们目前无法预知进程在内存下的若干页面中那个是未来最长时间内不再被访问的,因而该算法无法实现。

FIFO算法优先淘汰最早进入内存的页面,亦即在内存中驻留时间最久的页面。该算法实现简单,只需把调入内存的页面根据先后次序连接成队列,设置一个指针总指向最先的页面。但该算法与进程实际运行时的规律不适应,因为在进程中,有的页面经常被访问。

LRU算法选择最近最长时间未访问过的页面予以淘汰,它认为过去一段时间内未访问过的页面,在最近的将来可能也不会被访问。该算法为每个页面设置一个访问字段,来记录页面自上次被访问以来所经历的时间,淘汰页面时选择现有页面中值最大的予以淘汰。

LRU性能较好,但需要寄存器和栈的硬件支持。LRU是堆栈类的算法。理论上可以证明,堆栈类算法不可能出现Belady异常。FIFO算法基于队列实现,不是堆栈类算法。

LRU算法的性能接近于OPT,但实现起来比较困难,且开销大;FIFO算法实现简单,但性能差。所以操作系统的设计者尝试了很多算法,试图用比较小的开销接近LRU的性能,这类算法都是CLOCK算法的变体。

简单的CLOCK算法是给每一帧关联一个附加位,称为使用位。当某一页首次装入主存时,该帧的使用位设置为1;当该页随后再被访问到时,他的使用位也被置为1.对于页替换算法,用于替换的候选帧集合看做一个循环缓冲区,并且有一个指针与之相关联。当某一页被替换时,该指针被设置成指向缓冲区中的下一帧。当需要替换一页时,操作系统扫描缓冲区,以查找使用位被指为0的帧,每当遇到一个使用位为1的帧时,操作系统就将该位重新置为0;如果在这个过程开始时,缓冲区中所有帧的使用位均为0,则选择遇到的第一个帧替换;如果所有帧的使用位均为1,则指针在缓冲区中完整的循环一周,把所有使用位都置为0,并且停留在最初的位置上,替换该帧中的页。由于该算法循环的检查各页面的情况,故称为CLOCK算法,又称为最近未用NRU( Not recently used)算法。

CLOCK算法的性能比较接近LRU,而通过增加使用的位数目,可是使得CLOCK算法更加高效。在使用位的基础上再增加一个修改位,则得到改进型的CLOCK置换算法。这样,每一帧都出于以下四种情况之一。

1)最近未被访问,也未被修改(u=0,m=0)。

2)最近被访问,但未被修改(u=1,m=0)。

3)最近未被访问,但被修改(u=0,m=1)。

4)最近被访问,被修改(u=1,m=1)。

算法执行如下操作步骤:

1)从指针的当前位置开始,扫描帧缓冲区。在这次扫描过程中,对使用位不作任何修改,选择遇到的第一个帧(u=0,m=0)用于替换。

2)如果第1步失败,则重新扫描,查找(u=0,m=1)的帧。选额遇到的第一个这样的帧用于替换。在这个扫面过程中,对每个跳过的帧,把它的使用位设置成0。

3)如果第2步失败,指针将回到它的最初位置,并且集合中所有帧的使用位均为0.重复第一步,并且如果有必要重复第2步。这样将可以找到供替换的帧。

改进型的CLOCK算法优于简单的CLOCK算法之处在于替换时首选没有变化的页。由于修改过的页在被替换之前必须写回,因而这样做会节省时间。

改进型的时钟置换算法是对经典的时钟置换算法进行了改进和优化。经典的时钟置换算法是针对缓存中的页面进行替换的,它维护一个类似于时钟的数据结构,指针随着访问的页面而转动。若此时页面已被访问,则将指针所指的页面标记为已访问,并将指针转向下一个页面。若指针所指页面未被访问,则将该页面替换出去。
改进型的时钟置换算法引入了两个指针:一般指针和替换指针。其中,一般指针用于在缓存中顺序查找页面,并将访问位清零,替换指针则用于用于插入页面。当一般指针碰到对应的页面时,如果该页面的访问位为0,则将页面插入到替换指针所指位置,并将替换指针指向下一个位置;否则将该页面的访问位清零,指针继续向下查找。这样一来,访问过的页面可以更好地保留在缓存中,提高了缓存的命中率和效率。

(四)页面分配策略

驻留集(Resident Set)是指在虚拟内存的分页式管理中,为特定进程所分配的物理页框的集合。操作系统必须决定为每个进程分配多少页框才能满足进程的需求。驻留集大小的选择需要考虑多个因素,如在一定时间内使用的内存量、I/O 操作数据大小、Page Fault 的代价等。一般来说,如果分配给一个进程的存储量越大,那么任何时候驻留在主存中的进程数就越多,这可以提高处理机的时间利用率。但过大的驻留集也会导致频繁的 Page Fault,从而影响系统性能。(在分页式虚拟内存管理中,Page Fault是指当CPU试图访问一个当前没有调入主存的页时所发生的一种例外情况。也就是说,Page Fault表示CPU需要的数据或代码在当前内存中不存在,需要从磁盘或其他设备上将其重新读入到内存中,并重新启动受阻塞的进程。Page Fault通常会造成一定的延迟,因为需要经过磁盘I/O操作,所以它也会对系统性能产生一定的影响。为了减少Page Fault对系统性能的影响,操作系统会采取一系列策略来优化虚拟内存管理,例如预读取(Prefetching)、页面共享(Page Sharing)等。)

对于分页式的虚拟内存,在准备执行时,不需要也不可能把一个进程的所有页都读取到主存,因此,操作系统必须决定读取多少页。也就是说,给特定的进程分配多大的主存空间。这需要考虑以下几点:

1)分配给一个进程的存储量越小,在任何时候驻留在主存的进程数越多,从而可以提高处理器的时间利用率。

2)如果一个进程在主存中的页数过少,尽管有局部性原理,页错误率仍然会相对较高。

3)如果页数过多,由于局部性原理,给特定的进程分配更多的主存空间对该进程的错误率没有明显的影响。

基于这些因素,现代操作系统通常采用三种策略:

1)固定分配局部置换。它为每个进程分配一定数量的物理块,在整个运行期间都不改变。若进程在运行中发现缺页,则只能从该进程在内存的页面中选出一个换出,然后再调入需要的页面。实现这种策略难以确定为每个进程应分配的物理块数量:太少会频繁出现缺页中断,太多又会使CPU和其他资源利用率下降。

2)可变分配全局置换。这是最易于实现的物理块分配和置换策略,为系统中的每个进程分配一定数量的物理块,操作系统自身也保持一个空闲物理块队列。当某进程发现缺页时,系统从空闲物理块队列中取出物理块分配给该进程,并将于调入的页装入其中。

3)可变分配局部置换。它为每个进程分配一定数目的物理块,当某进程发现缺页时,只允许从该进程在内存的页面中选出一页换出,这样就不会影响其他进程的运行。如果进程在运行中频繁的换页,系统需再为该进程分配若干附加物理块,直至该进程缺页率趋于适当程度为止;反之,若一个进程在运行过程中缺页率特别低,则此时可适当减少该进程的物理块。

物理块(Physical Block)指的是存储介质上被划分出来,用于存储数据和程序的最小物理单位。物理块一般由若干扇区(Sector)组成,每个扇区的大小一般为512字节或4KB。物理块的大小是由硬件设计时确定的,一般对于机械硬盘来说,其物理块大小是512字节,而对于固态硬盘(SSD)来说,物理块大小通常是4KB。不同存储介质之间的物理块大小可能存在差异。在操作系统中,为了将逻辑地址映射到物理地址,需要使用页表将逻辑页面(Page)与物理页面(Page Frame)进行映射。这样,在程序访问数据或代码时就能够通过页表查询到对应的物理块,并将其读写到内存中。

为确定系统将进程运行时所缺的页面调入内存的时机,可采取预调页策略或请求调页策略。

1)预调页策略。根据局部性原理,一次调入若干个相邻的页可能比一次调入一页更高效。但如果调入的一批页面中大多数都未被访问,则又是低效的。所以就需要采用以预测为基础的预调页策略,将预计在不久之后便会被访问的页面预先调入内存。但目前预调页的成功率仅约50%。故这种策略主要用于进程的首次调入时,有程序员指出应该先调入哪些页。

2)请求调页策略。进程在运行中需要访问的页面不在内存而提出的请求,由系统将所需页面调入内存。这种策略调入的页一定会被访问,且这种策略比较易于实现,故在目前的虚拟存储器中大多采用此策略。它的缺点在于每次调入一页,会花费过多的IO开销。

从何处调入页面

请求分页系统中的外存分为两部分:用于存放文件的文件区和用于存放对换页面的对换区。对换区通常是采用连续分配方式,而文件区采用离散分配方式,故对换区的磁盘IO速度比文件区高。这里从何处调入页面有三种情况:

1)系统拥有足够的对换区空间:可以全部从对换区调入所需页面,以提高调页速度。为此,在进程运行前,需将与该进程有关的文件从文件区复制到对换区。

2)系统缺少足够的对换区空间:凡是不会被修改的文件都直接从文件区调入;而当换出这些页面时,由于他们未被修改而不必再将它们换出。但对于那些可能被修改的部分,在将他们换出时需调到对换区,以后需要以后需要时再从对换区调入。

3)UNIX方式:与进程有关的文件都存放在文件区,故未运行过的页面都应从文件区调入。曾经运行过的但有被换出的页面,由于是被放在对换区,因此下次调入时应从对换区调入。进程请求的共享页面若被其他进程调入内粗你,则无需再从对换区调入。

(五)抖动和工作集

在进程的页面置换过程中,频繁的页面调度行为成为抖动,或颠簸。如果一个进程在换页上用的时间多于执行时间,那么这个进程就在颠簸。

使用虚拟内存技术,操作系统中进程通常只有一部分块位于主存中,从而可以在内存中保留更多的进程以提高系统效率。此外,由于未用到的块不需要换入换出内存,因为节省了时间。但是系统必须很“聪明”地管理页面分配方案。在稳定状态,几乎主存的所有空间都被禁成块占据,处理器和操作系统可以直接访问到尽可能多的进程。但如果管理不当,系统发生抖动现象,处理器的大部分时间都将用于交换快,及请求调入页面的操作,而不是执行进程的指令,这就会大大降低系统效率。前面讲解的页面置换算法就是是讨论这里的分配方案,尽量避免抖动现象。

另外,为了防止出现抖动现象,需要选择合适的驻留集大小。驻留集(或工作集)是指在某段时间间隔内,进程要访问的页面集合。经常被使用的页面需要在驻留集中,而长期不被使用的页面要从驻留集中被丢弃。驻留集模型使用较为简单:操作系统跟踪每个进程的驻留集,并为进程分配大于驻留集的的空间。如果还有空闲,那么可启动另一个进程。如果所有驻留集之和增加一直超过了可用物理块的总数,那么系统会监听一个进程,将其页面调出并且将其物理块分配给其他进程。正确选择驻留集的大小,对存储器的有效利用和系统吞吐量的提高,都将产生重要的影响。

(六)内存映射文件

*快速面经

一、物理地址、逻辑地址、有效地址、线性地址、虚拟地址的区别?

物理地址就是内存中真正的地址,它就相当于门牌号,具有唯一性。不管哪种地址,最终都会映射为物理地址

实模式下,段基址 + 段内偏移经过地址加法器的处理,经过地址总线传输,最终也会转换为物理地址

但是在保护模式下,段基址 + 段内偏移被称为线性地址,不过此时的段基址不能称为真正的地址,而是会被称作为一个选择子的东西,选择子就是个索引,相当于数组的下标,通过这个索引能够在 GDT 中找到相应的段描述符,段描述符记录了段的起始、段的大小等信息,这样便得到了基地址。如果此时没有开启内存分页功能,那么这个线性地址可以直接当做物理地址来使用,直接访问内存。如果开启了分页功能,那么这个线性地址又多了一个名字,这个名字就是虚拟地址

不论在实模式还是保护模式下,段内偏移地址都叫做有效地址。有效抵制也是逻辑地址。

线性地址可以看作是虚拟地址,虚拟地址不是真正的物理地址,但是虚拟地址会最终被映射为物理地址。下面是虚拟地址 -> 物理地址的映射。

二、什么是分页?

把内存空间划分为大小相等且固定的块,作为主存的基本单位。因为程序数据存储在不同的页面中,而页面又离散的分布在内存中,因此需要一个页表来记录映射关系,以实现从页号到物理块号的映射。

访问分页系统中内存数据需要两次的内存访问 (一次是从内存中访问页表,从中找到指定的物理块号,加上页内偏移得到实际物理地址;第二次就是根据第一次得到的物理地址访问内存取出数据)。

三、什么是分段?

页是为了提高内存利用率,而分段是为了满足程序员在编写代码的时候的一些逻辑需求(比如数据共享,数据保护,动态链接等)。

分段内存管理当中,地址是二维的,一维是段号,二维是段内地址;其中每个段的长度是不一样的,而且每个段内部都是从0开始编址的。由于分段管理中,每个段内部是连续内存分配,但是段和段之间是离散分配的,因此也存在一个逻辑地址到物理地址的映射关系,相应的就是段表机制。

四、分页和分段的区别是?

  • 分页对程序员是透明的,但是分段需要程序员显式划分每个段。

  • 分页的地址空间是一维地址空间,分段是二维的。

  • 页的大小不可变,段的大小可以动态改变。

  • 分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。

五、什么是交换空间?

操作系统把物理内存(physical RAM)分成一块一块的小内存,每一块内存被称为页(page)。当内存资源不足时,Linux把某些页的内容转移至硬盘上的一块空间上,以释放内存空间。硬盘上的那块空间叫做交换空间(swap space),而这一过程被称为交换(swapping)。物理内存和交换空间的总容量就是虚拟内存的可用容量。

用途:

  • 物理内存不足时一些不常用的页可以被交换出去,腾给系统。

  • 程序启动时很多内存页被用来初始化,之后便不再需要,可以交换出去。

六、什么是虚拟内存?

虚拟内存就是说,让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。虚拟内存使用部分加载的技术,让一个进程或者资源的某些页面加载进内存,从而能够加载更多的进程,甚至能加载比内存大的进程,这样看起来好像内存变大了,这部分内存其实包含了磁盘或者硬盘,并且就叫做虚拟内存。

七、虚拟内存的实现方式有哪些?

虚拟内存中,允许将一个作业分多次调入内存。釆用连续分配方式时,会使相当一部分内存空间都处于暂时或永久的空闲状态,造成内存资源的严重浪费,而且也无法从逻辑上扩大内存容量。因此,虚拟内存的实需要建立在离散分配的内存管理方式的基础上。虚拟内存的实现有以下三种方式:

  • 请求分页存储管理。

  • 请求分段存储管理。

  • 请求段页式存储管理。

八、页面替换算法有哪些?

在程序运行过程中,如果要访问的页面不在内存中,就发生缺页中断从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。

①NRU算法和CLOCK算法是两个不同的页面置换算法,虽然它们都是基于页面访问情况进行页面置换的。NRU算法(最近未使用算法)根据页面的引用位和修改位来划分页面为四类,然后从这四类页面中随机选择一个置换掉。而CLOCK算法利用了循环队列的结构,在每个页面中设置一个访问位,按照访问次序排列,页面被访问时将访问位设置为1,当需要替换页面时,选择一个访问位为0的页面进行置换。因此,它们的实现机制和算法思想是不同的。②第二次机会算法(Second Chance Algorithm)是一种页面置换算法,也被称为二次机会算法。它是用一个循环缓冲队列存储内存中的页,并使用一个指针来指示下一个要被淘汰的页。对于每个页,在内存中都有一个访问位(accessed)和修改位(modified)。当需要一个新的存储块时,指针向前移动,遍历所有的页,如果访问位为0,则选择这个页进行替换,并将指针指向下一个页。如果访问位为1,则给这个页第二次机会,将访问位设置为0,指针继续向前移动,直到找到一个访问位为0的页或者全部的页面都被过一遍(即指针又回到了初始位置),此时就把最早进入队列的页进行替换。该算法基于FIFO算法的思路,但加入了访问位的概念,更好地处理了缓存淘汰中的访问次序信息,提高了性能。

  • 最优算法在当前页面中置换最后要访问的页面。不幸的是,没有办法来判定哪个页面是最后一个要访问的,因此实际上该算法不能使用。然而,它可以作为衡量其他算法的标准。

  • NRU 算法根据 R 位和 M 位的状态将页面分为四类。从编号最小的类别中随机选择一个页面。NRU 算法易于实现,但是性能不是很好。存在更好的算法。

  • FIFO 会跟踪页面加载进入内存中的顺序,并把页面放入一个链表中。有可能删除存在时间最长但是还在使用的页面,因此这个算法也不是一个很好的选择。

  • 第二次机会算法是对 FIFO 的一个修改,它会在删除页面之前检查这个页面是否仍在使用。如果页面正在使用,就会进行保留。这个改进大大提高了性能。

  • 时钟 算法是第二次机会算法的另外一种实现形式,时钟算法和第二次算法的性能差不多,但是会花费更少的时间来执行算法。

  • LRU 算法是一个非常优秀的算法,但是没有特殊的硬件(TLB)很难实现。如果没有硬件,就不能使用 LRU 算法。

  • NFU 算法是一种近似于 LRU 的算法,它的性能不是非常好。

  • 老化 算法是一种更接近 LRU 算法的实现,并且可以更好的实现,因此是一个很好的选择

  • 最后两种算法都使用了工作集算法。工作集算法提供了合理的性能开销,但是它的实现比较复杂。WSClock 是另外一种变体,它不仅能够提供良好的性能,而且可以高效地实现。

最好的算法是老化算法和WSClock算法。他们分别是基于 LRU 和工作集算法。他们都具有良好的性能并且能够被有效的实现。还存在其他一些好的算法,但实际上这两个可能是最重要的。

九、硬链接和软链接有什么区别?

  • 硬链接就是在目录下创建一个条目,记录着文件名与 inode 编号,这个 inode 就是源文件的 inode。删除任意一个条目,文件还是存在,只要引用数量不为 0。但是硬链接有限制,它不能跨越文件系统,也不能对目录进行链接。

  • 符号链接文件保存着源文件所在的绝对路径,在读取时会定位到源文件上,可以理解为 Windows 的快捷方式。当源文件被删除了,链接文件就打不开了。因为记录的是路径,所以可以为目录建立符号链接。


本文标题:操作系统(五)——内存管理 - 八卦谈
本文地址:www.ttdhp.com/article/57052.html

天天动画片声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。
扫码关注我们