计算机的虚拟内存

一个系统中的进程是与其他进程共享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. 开发者的应对策略

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

  • 优化内存使用:减少内存泄漏,及时释放不再使用的内存。
  • 改善局部性:优化数据结构和算法,提高空间和时间局部性。
  • 使用内存池:对于频繁分配释放的小对象,使用内存池减少碎片。
  • 监控内存使用:使用性能分析工具监控应用程序的内存使用模式。
相关推荐
暴力求解7 小时前
Linux ---线程控制(二)
linux·运维·服务器·操作系统
smartvxworks7 小时前
Linux 实时内核(Linux Real-Time Kernel)详解:原理、实践与优化
java·linux·服务器
梦痕长情7 小时前
等保三级三权账户
linux·运维·服务器
wuqingshun3141598 小时前
说说TCP的3次握手?为什么需要三次握手?
服务器·网络协议·tcp/ip
CHANG_THE_WORLD8 小时前
1.简单的服务端和客户端(仅作为内容补充)
linux·服务器·网络
jiecy8 小时前
为Linux系统双网卡配置静态路由
linux·运维
CodexDave9 小时前
数据库连接池耗尽:排查顺序与三层兜底
服务器·前端·数据库·git·云原生·容器·kubernetes
weixin_307779139 小时前
Linux下Jenkins数据故障的系统化排查Shell脚本
linux·运维·服务器·jenkins