该部分的学习框架如下:
- 了解基本内存管理概念:物理内存、虚拟内存、地址空间等。
- 学习虚拟内存的概念、作用和原理。了解虚拟内存是如何将物理内存和进程的地址空间分开管理的,以及它如何提供了更大的地址空间和更好的内存管理。
- 内存分段:学习内存分段的概念和实现。了解如何将进程的地址空间分成多个段,每个段用于存储不同类型的数据。了解段表、段选择子等相关概念。
- 内存分页:探索内存分页的概念和机制。学习将进程的虚拟地址空间分成固定大小的页面,以便更灵活地管理内存。了解页表、页表项等相关内容。
- 段页式内存管理:了解段页式内存管理是如何将分段和分页结合起来的。掌握它的优点以及如何实现段页式的内存管理模式。
- Linux内存管理:了解Linux如何管理进程的虚拟内存,包括页面置换算法、内存映射、页面分配等。
- 内存管理之间的关联:探索虚拟内存、内存分段、内存分页和段页式内存管理之间的关系。理解它们的优劣势,以及不同操作系统如何在不同场景中选择适当的内存管理策略。
目录
- 1. 内存管理基本概念
- 2. 虚拟内存的概念、作用和原理
- 3. 内存分段和内存分页
- 4. 实际中Linux 采用了哪种方式来管理内存
- 5. 总结
1. 内存管理基本概念
注意在Linux操作系统(一):详解CPU中1.1.4节介绍了存储设备的结构。
内存是一种有限资源。内存管理的主要目标是优化内存的使用,使多个进程能够同时运行,提供良好的性能和用户体验。
名称 | 定义 |
---|---|
物理内存(Physical Memory) | 计算机硬件中实际存在的内存,也称为主存(Main Memory)。由DRAM(动态随机存取存储器)等硬件组件组成,用于存储正在运行的程序和数据。操作系统将程序的指令和数据加载到物理内存中,以供CPU访问。 |
虚拟内存(Virtual Memory) | 扩展了计算机系统的地址空间。每个进程都有自己的虚拟地址空间,在虚拟内存的概念下,操作系统通过使用一种叫做“页表”的数据结构,将进程的虚拟地址映射到物理内存中的实际地址。虚拟内存使得操作系统能够将进程的部分数据和指令存储在物理内存中,而不是完全加载进物理内存,从而允许更多的进程在有限的物理内存中并发运行。 |
地址空间(Address Space) | 地址空间是一个进程在其虚拟内存中的所有可能地址的集合。每个进程都有自己的地址空间,它由多个不同的区域组成,如代码段、数据段、堆、栈等。每个区域用于存储不同类型的数据和指令。 |
2. 虚拟内存的概念、作用和原理
背景:
单片机没有操作系统,每次写完代码都需要借助工具把程序烧录进去。其CPU直接操作内存的物理地址。在这种情况下,要想在内存中同时运行两个程序是不可能的。如果第一个程序在 2000 的位置写入一个新的值,将会擦掉第二个程序存放在相同位置上的所有内容,所以同时运行这两个程序会立刻崩溃。
两个程序都引用了绝对物理地址,导致进程冲突
操作系统针对绝对物理地址冲突的解决办法:
为每个进程分配独立的一套「虚拟地址」,大家玩自己的地址就行,互不干涉。但前提每个进程都不能访问物理地址,至于虚拟地址最终怎么落到物理内存里,进程们不用理会,让操作系统安排,操作系统自会将不同进程的虚拟地址和不同内存的物理地址映射起来。
操作系统是如何管理虚拟地址与物理地址之间的关系?在3. 内存分段和4. 内存分页中介绍。
3. 内存分段和内存分页
目的:操作系统想要管理虚拟地址与物理地址之间的映射关系。
思想:
- 内存分段:通过段表与物理地址进行映射的,分段机制会把程序的虚拟地址分成 4 个段,每个段在段表中有一个项,在这一项找到段的基地址,再加上偏移量,于是就能找到物理内存中的地址。
内存碎片的问题。
为了解决内存碎片问题引入了内存交换内存交换的效率低的问题。
对于多进程的系统来说,用分段的方式,外部内存碎片是很容易产生的,产生了外部内存碎片,那不得不重新 Swap 内存区域,这个过程会产生性能瓶颈。因为硬盘的访问速度要比内存慢太多了,每一次内存交换,我们都需要把一大段连续的内存数据写到硬盘上。
- 内存分页:把整个虚拟和物理内存空间切成一段段固定尺寸的大小。这样一个连续并且尺寸固定的内存空间,我们叫页(Page)。在 Linux 下,每一页的大小为 4KB。虚拟地址与物理地址之间通过页表来映射
这里用一个笔试题来学习页表分页:
若页式虚拟存储管理系统(请求分页系统)采用最佳置换算法,初始时页表为空。当该进程的访问页面顺序为4、3、2、1、4、3、5、4、3、2、1、5,当分配给该作业的物理块数M为3时,试计算在访问过程中所发生的缺页次数和缺页率。
-
页式虚拟存储管理系统的页面置换算法
作用:在程序执行过程中所访问的信息不在内存时需要将所需信息从外存调入内存【操作系统要提供请求调页功能,将缺失页面从外存调入内存】,然后继续执行程序。若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存【操作系统要提供页面置换的功能,将暂时用不到的页面换出外存】。
最佳置换算法性能最好,但无法实现;先进先出置换算法实现简单,但算法性能差;最近最久未使用置换算法性能好,是最接近OPT算法性能的,但是实现起来需要专门的硬件支持,算法开销大。时钟置换算法是一种性能和开销较均衡的算法,又称CLOCK算法,或最近未用算法(NRU,NotRecently Used)。 -
最佳置换算法
思想:每次选择淘汰的页面将是以后最长时间内不再被访问的页面,这样可以保证最低的缺页率。
页面置换1:当进程访问页面1时,将会产生页面置换,4 3 2进行淘汰,往远处(右)观察,页面2最远,则淘汰页面2。
页面置换2:当进程访问页面5时,将会产生页面置换,4 3 1进行淘汰,往远处(右)观察,页面1最远,则淘汰页面1。
页面置换3:当进程访问页面2时,将会产生页面置换,4 3 5进行淘汰,往远处(右)观察,看出5还会用到,但是4和3已经没用了,再往前放(左)观察,4更新的最晚,将4淘汰。
页面置换4:当进程访问页面1时,将会产生页面置换,2 3 5进行淘汰,往远处(右)观察,看出5还会用到,但是2和3已经没用了,再往前放(左)观察,3更新的最晚,将3淘汰。 -
缺页次数(缺页中断)
缺页中断:要访问的页不在主存
缺页率:发生缺页次数/总共的页面数
缺页次数:7
缺页率:7/12
4. 实际中Linux 采用了哪种方式来管理内存
Linux 系统主要采用了分页管理,但是由于 Intel 处理器的发展史,Linux 系统无法避免分段管理。 于是 Linux 就把所有段的基地址设为 0,也就意味着所有程序的地址空间都是线性地址空间(虚拟地址),相当于屏蔽了 CPU 逻辑地址的概念,所以段只被用于访问控制和内存保护。
另外,Linux 系统中虚拟空间分布可分为用户态和内核态两部分,其中用户态的分布:代码段、全局变量、BSS、函数栈、堆内存、映射区。
5. 总结
-
虚拟内存有什么作用?
- 虚拟内存可以使得进程对运行内存超过物理内存大小(扩展物理内存),因为程序运行符合局部性原理,CPU 访问内存会有很明显的重复访问的倾向性,对于那些没有被经常使用到的内存,我们可以把它换出到物理内存之外,比如硬盘上的 swap 区域。
- 由于每个进程都有自己的页表,所以每个进程的虚拟内存空间就是相互独立的。进程也没有办法访问其他进程的页表,所以这些页表是私有的,这就解决了多进程之间地址冲突的问题。
- 页表里的页表项中除了物理地址之外,还有一些标记属性的比特,比如控制一个页的读写权限,标记该页是否存在等。在内存访问方面,操作系统提供了更好的安全性。
-
系统内存紧张时(满了),会发生什么?
- 是物理内存满了还是虚拟内存满了?如果是虚拟内存满了,就是应用程序读写某块虚拟内存时,CPU 会发现这个虚拟内存没有映射到物理内存, CPU 就会产生缺页中断,进程会从用户态切换到内核态,并将缺页中断交给内核的 Page Fault Handler (缺页中断函数)处理。缺页中断处理函数会看是否有空闲的物理内存,如果有,就直接分配物理内存,并建立虚拟内存与物理内存之间的映射关系。如果没有空闲的物理内存,那么内核就会开始进行回收内存的工作,回收的方式主要是两种:直接内存回收和后台内存回收。如果直接内存回收后,空闲的物理内存仍然无法满足此次物理内存的申请,那么内核就会放最后的大招了 ——触发 OOM (Out of Memory)机制。
-
在 4GB 物理内存的机器上,申请 8G 内存会怎么样?
- 32 位系统(最高寻址4GB)的内核空间占用 1G,位于最高处,剩下的 3G 是用户空间;32 位操作系统,进程最多只能申请 3 GB 大小的虚拟内存空间,所以进程申请 8GB 内存的话,在申请虚拟内存阶段就会失败。
- 64 位系统的内核空间和用户空间都是 128T。64 位操作系统,进程可以使用 128 TB 大小的虚拟内存空间,所以进程申请 8GB 内存是没问题的,因为进程申请内存是申请虚拟内存,只要不读写这个虚拟内存,操作系统就不会分配物理内存。如果申请物理内存大小超过了空闲物理内存大小,就要看操作系统有没有开启 Swap 机制:
- 如果没有开启 Swap 机制,程序就会直接 OOM;
- 如果有开启 Swap 机制,程序可以正常运行。
-
Linux的页面请求管理采用什么LRU算法
- 传统LRU算法问题:
- 「预读失效」导致缓存命中率下降:操作系统在读磁盘时会额外多读一些到内存,但这些数据可能至始至终也没有被用到。
- 「缓存污染」导致缓存命中率下降:批量读取时可能把热点页也给切出去。
- MySQL 和 Linux 操作系统是通过改进 LRU 算法来避免「预读失效和缓存污染」而导致缓存命中率下降的问题。
- Linux 操作系统实现两个了 LRU 链表:活跃 LRU 链表(active list)和非活跃 LRU 链表(inactive list)。
- MySQL Innodb 存储引擎是在一个 LRU 链表上划分来 2 个区域:young 区域 和 old 区域。
- 传统LRU算法问题: