计算机的虚拟内存

一个系统中的进程是与其他进程共享CPU和主存资源的。然而,共享主存会形成一些特殊的挑战。随着对CPU需求的增长,进程以某种合理的平滑方式慢了下来。但是如果太多的进程需要太多的内存,那么它们中的一些就根本无法运行。当一个程序没有空间可用时,那就是它运气不好了。内存还很容易被破坏。如果某个进程不小心写了另一个进程使用的内存,它就可能以某种完全和程序逻辑无关的令人迷惑的方式失败。

为了更加有效地管理内存并且少出错,现代系统提供了一种对主存的抽象概念,叫做虚拟内存(VM)。虚拟内存是硬件异常、硬件地址翻译、主存、磁盘文件和内核软件的完美交互,它为每个进程提供了一个大的、一致的和私有的地址空间。通过一个很清晰的机制,虚拟内存提供了三个重要的能力:

1)它将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,它高效地使用了主存。

2)它为每个进程提供了一致的地址空间,从而简化了内存管理。

3)它保护了每个进程的地址空间不被其他进程破坏。

物理地址和虚拟地址

计算机系统的主存被组织成一个由M个连续的字节大小的单元组成的数组。每字节

都有一个唯一的物理地址(Physical Address,PA)。第一个字节的地址为0,接下来的字节地址为1,再下一个为2,依此类推。给定这种简单的结构,CPU访问内存的最自然的方式就是使用物理地址。我们把这种方式称为物理寻址(physicaladdressing)。

如图:

而现在大多数系统使用的是虚拟地址来访问主存,CPU通过生成一个虚拟地址(Virtual Address,VA)来访问主存,这个虚拟地址在被送到内存之前先转换成适当的物理地址。将一个虚拟地址转换为物理地址的任务叫做地址翻译(address translation)。

地址空间

地址空间(Address Space)是计算机系统中一个核心的抽象概念,它定义了程序可以访问的内存地址范围。在现代操作系统中,每个进程都拥有自己独立的地址空间,这为进程提供了内存隔离和保护。

1. 地址空间的基本概念

地址空间可以分为两种主要类型:

  • 物理地址空间:对应实际物理内存(RAM)的地址范围。物理地址是内存控制器直接使用的地址,用于访问内存芯片中的特定存储单元。
  • 虚拟地址空间:每个进程看到的、独立的地址范围。虚拟地址由CPU生成,需要通过内存管理单元(MMU)转换为物理地址后才能访问实际内存。

2. 虚拟地址空间的特点

虚拟地址空间为每个进程提供了以下重要特性:

  • 独立性:每个进程都认为自己独占整个地址空间(通常是4GB或更大),与其他进程的地址空间完全隔离。
  • 连续性:虚拟地址空间是连续的线性地址范围,从0开始向上扩展,简化了程序的地址计算。
  • 保护性:操作系统通过页表等机制,确保一个进程无法访问其他进程的地址空间,防止恶意或错误的访问。
  • 灵活性:虚拟内存可以比物理内存大得多,通过分页和交换技术,可以将暂时不用的数据存储到磁盘上。

3. 地址空间的组成

典型的进程虚拟地址空间通常包含以下几个主要区域:

  • 代码段(Text Segment):存放可执行代码,通常是只读的。
  • 数据段(Data Segment):包含已初始化的全局变量和静态变量。
  • BSS段(BSS Segment):存放未初始化的全局变量和静态变量。
  • 堆(Heap):动态分配的内存区域,向高地址方向增长。
  • 栈(Stack):用于函数调用、局部变量等,向低地址方向增长。
  • 共享库区域:映射共享库代码和数据。
  • 内核空间:操作系统内核代码和数据,通常位于地址空间的高端。

4. 地址翻译过程

当CPU生成一个虚拟地址时,地址翻译过程如下:

  1. CPU将虚拟地址发送给内存管理单元(MMU)。
  2. MMU查询页表(Page Table),找到对应的物理页框号。
  3. 如果页表中没有对应条目(页错误),则触发缺页异常,由操作系统处理。
  4. MMU将物理页框号与页内偏移组合,得到物理地址。
  5. 物理地址被发送到内存总线,访问实际内存。

5. 地址空间的实际意义

地址空间的抽象带来了多重好处:

  • 简化编程:程序员无需关心物理内存的分配和布局,只需在虚拟地址空间中编写程序。
  • 内存保护:防止进程间的非法内存访问,提高系统稳定性。
  • 内存共享:多个进程可以共享相同的代码段或数据段(如共享库),节省内存。
  • 内存扩展:通过虚拟内存技术,程序可以使用比物理内存更大的地址空间。
  • 简化加载:可执行文件可以加载到虚拟地址空间的任意位置,无需重定位。

虚拟页表

概念上而言,虚拟内存被组织为一个由存放在磁盘上的N个连续的字节大小的单元组成的数组。每字节都有一个唯一的虚拟地址,作为到数组的索引。磁盘上数组的内容被缓存在主存中。和存储器层次结构中其他缓存一样,磁盘(较低层)上的数据被分割成块,这些块作为磁盘和主存(较高层)之间的传输单元。VM系统通过将虚拟内存分割为称为虚拟页(Virtual Page,VP)的大小固定的块来处理这个问题。每个虚拟页的大小为P=2字节。类似地,物理内存被分割为物理页(Physical Page,PP),大小也为P字节(物理页也被称为页帧(page frame))。

在任意时刻,虚拟页面的集合都分为三个不相交的子集:

●未分配的:VM系统还未分配(或者创建)的页。未分配的块没有任何数据和它们相

关联,因此也就不占用任何磁盘空间。

●缓存的:当前已缓存在物理内存中的已分配页。

●未缓存的:未缓存在物理内存中的已分配页。

可以通过图来理解他们之间的关系:

以malloc为例:操作系统是这样分配地址空间的

页表

同任何缓存一样,虚拟内存系统必须有某种方法来判定一个虚拟页是否缓存在DRAM中的某个地方。如果是,系统还必须确定这个虚拟页存放在哪个物理页中。如果不命中(缺页),系统必须判断这个虚拟页存放在磁盘的哪个位置,在物理内存中选择一个牺牲页,并将虚拟页从磁盘复制到DRAM中,替换这个牺牲页。

抖动

抖动(Thrashing)是虚拟内存系统中一种严重的性能问题,指系统花费大量时间在页面置换(换入换出)上,而实际用于执行进程的时间却很少,导致系统整体性能急剧下降。

1. 抖动的产生原因

抖动通常发生在以下情况:

  • 内存过载:系统中运行的进程过多,它们的工作集(Working Set)总和超过了可用物理内存容量。
  • 页面置换频繁:操作系统频繁地在内存和磁盘之间交换页面,导致CPU大部分时间都在等待I/O操作完成。
  • 局部性原理失效:进程频繁访问的页面无法同时驻留在内存中,导致缺页率异常升高。

2. 抖动的工作机制

当系统发生抖动时,会出现以下恶性循环:

  1. 进程A发生缺页,操作系统需要为其调入所需页面。
  2. 由于物理内存已满,操作系统必须选择一个牺牲页(通常是进程B的页面)换出到磁盘。
  3. 不久后,进程B需要访问刚被换出的页面,又发生缺页。
  4. 操作系统又需要换出另一个进程(可能是进程A)的页面来为进程B腾出空间。
  5. 这种频繁的页面换入换出导致磁盘I/O负载极高,CPU利用率却很低。

3. 抖动的表现特征

  • CPU利用率骤降:CPU大部分时间处于空闲状态,等待I/O完成。
  • 磁盘I/O异常高:磁盘活动指示灯持续闪烁,I/O队列长度增加。
  • 系统响应缓慢:用户界面卡顿,命令执行延迟明显。
  • 缺页率急剧上升:每秒缺页次数远超正常水平。
  • 吞吐量下降:单位时间内完成的进程数量减少。

4. 预防和缓解抖动的策略

操作系统采用多种策略来预防和缓解抖动:

4.1 工作集模型

工作集是进程在最近一段时间内访问的页面集合。操作系统通过监控每个进程的工作集大小,确保分配给进程的物理页框数不小于其工作集大小,从而减少缺页。

4.2 页面置换算法优化
  • 局部置换算法:每个进程只能从自己的页面中选择牺牲页,避免影响其他进程。
  • 工作集时钟算法:结合工作集概念和时钟算法的优点,更智能地选择置换页面。
  • 页面老化算法:根据页面访问频率和最近访问时间动态调整页面优先级。
4.3 负载控制
  • 准入控制:当系统内存压力大时,暂停创建新进程或延迟进程调度。
  • 交换出进程:将整个进程换出到磁盘,释放其占用的所有物理内存。
  • 调整多道程序度:动态调整系统中同时运行的进程数量。
4.4 内存分配策略
  • 均衡分配:在所有进程间平均分配物理页框。
  • 按优先级分配:为高优先级进程分配更多物理内存。
  • 按工作集分配:根据进程的工作集大小动态调整分配。

5. 实际系统中的抖动处理

现代操作系统采用综合策略处理抖动:

  • Linux:使用页面回收机制(kswapd)、内存压缩(zswap/zram)和内存控制组(cgroups)来管理内存压力。
  • Windows:采用工作集修剪、内存优先级和超级预取等技术优化内存管理。
  • 实时系统:通过静态内存分配和确定性调度避免抖动。

6. 开发者的应对策略

应用程序开发者也可以采取措施减少抖动的发生:

  • 优化内存使用:减少内存泄漏,及时释放不再使用的内存。
  • 改善局部性:优化数据结构和算法,提高空间和时间局部性。
  • 使用内存池:对于频繁分配释放的小对象,使用内存池减少碎片。
  • 监控内存使用:使用性能分析工具监控应用程序的内存使用模式。
相关推荐
ltl1 小时前
内核追踪:ftrace、kprobe、uprobe、tracepoint 生产实战
linux
·薯条大王1 小时前
经济实惠玩云服务器|一台云服务器多人共用,子账号配置教程
java·linux·运维·服务器·汇编·c++·python
ZJH__GO4 小时前
网络编程v4pro--实现聊天室文件传输功能
java·服务器·网络·计算机网络
小小、码农4 小时前
Linux进程控制:从fork到exec,手写一个简易Shell
linux·运维·服务器
不会就选b5 小时前
Linux之进程间通信(二)---模拟进程池
linux·运维·服务器
NeilYuen6 小时前
用UDP实现向DNS服务器请求IP地址
服务器·tcp/ip·udp
沫璃染墨7 小时前
《从零入门Linux系统篇(十五):系统工具篇·六——GDB调试器详解:从程序执行控制到高级调试技巧》
linux·运维·服务器·c语言·c++
醉城夜风~7 小时前
(超详细)C++ STL list容器详解:从使用方法到双向链表底层原理全面解析
c++·windows
AIgorithmGEEK7 小时前
Linux 环境变量完全指南:从入门到理解底层原理
linux·运维·服务器
迪康Defender8 小时前
从静态存储到动态流转:终端透明加密两种模式实战解析
运维·服务器·网络·数据库·其他