操作系统中的重要角色--内存管理

在开发工作中,虽然CPU,内存和硬盘都是必不可少的硬件,不过,编程中,我们常常受到困扰的往往是内存相关的bug(编程中遇到CPU和硬盘相关的bug极少)。

这是因为我们的程序和数据虽然是存放在硬盘上的,但是运行时,CPU并不是直接从硬盘加载程序和数据的。

直接从硬盘读取指令非常慢,会成为整个系统的严重瓶颈,因此,程序及其数据首先被复制到内存(比硬盘驱动器小,但速度快得多)中,CPU从内存读取指令速度会快很多。

内存可以看作是一长串单元,每个单元都包含一些二进制数据,并标有一个称为存储器地址的数字。
内存地址 的范围从0到N,取决于系统中可用的主内存量。

程序使用的地址范围称为地址空间

如下图,两个加载到内存空间 中的程序Program-1Program-2

它们分别占用了内存地址 0~25~8的位置。

1. 早期的内存管理

在操作系统的早期,程序可以直接访问整个主存储器,如何管理内存是程序员的工作之一。

当时编写软件的一大挑战性就在于开发人员需要设计一种管理RAM访问的好方法,并确保整个程序不会溢出可用内存。

后来,随着多任务处理的出现,当多个程序可以在同一台计算机上运行时,内存管理变得越来越棘手。

程序员不得不面对自己管理内存带来的主要问题:

  1. 内存布局问题 :位于RAM中的第一个程序之后的程序将有一定量的地址空间偏移,不再是初始范围0到N(比如上面图片中的Program-2)。多个程序加载内存时,极大增加管理难度。
  2. 内存碎片问题:当程序或数据在内存中来回移动时,可用空间会被碎片化为越来越小的块。这将使它更难找到可用的空间来加载新的程序和内存中的数据
  3. 安全性问题:如果程序A不小心覆盖了程序B的内存怎么办?或者,更糟糕的是:如果它故意从另一个程序中读取敏感数据,如密码或信用卡信息,该怎么办?

因此,对于20世纪60年代早期的硬件架构师来说,急需一种自动化的内存管理形式,这样可以显著简化编程并解决更关键的内存保护问题。

最后,他们想出了今天被称为虚拟内存的东西。

2. 虚拟内存管理

虚拟内存 中,程序不能直接访问物理RAM。相反,它与一个名为虚拟内存 的空间交互。

操作系统与CPU一起提供这样的虚拟地址空间,并迟早将其转换为物理地址空间。

每个内存访问都是通过一个虚拟地址来执行的,该地址并不指向内存中的实际物理位置。

程序总是读取或写入虚拟地址,它完全不知道底层硬件中发生了什么。

比如,仍然是上面的Program-1Program-2,对于这两个程序来说,开发人员可以假定它们的地址都是从0开始。

而它们实际在物理内存中的位置开发人员不用关心,交给操作系统来负责就可以了。

2.1. 虚拟内存的优势

从上面的图中,我们可以看出虚拟内存的明显好处:

  1. 每个程序都有一个从0开始的虚拟地址空间,大大简化了程序员的负担,不再需要手动跟踪内存偏移
  2. 虚拟内存总是连续的,即使底层的物理内存不是连续的。操作系统完成了将可用内存块聚集到一个单一的、统一的虚拟内存块中的艰巨任务
  3. 虚拟内存机制还解决了内存有限的问题,开发时给人一种印象,不用担心物理内存还有多少(当然实际运行时,如果内存不足,操作系统会提示错误)
  4. 虚拟内存保证了安全性:操作系统会保证程序A不能读取或写入分配给程序B的虚拟内存

2.2. 虚拟内存管理的核心结构

虚拟内存机制 需要一个位置来存储虚拟地址和物理地址之间的映射。

也就是说,给定虚拟地址X,系统必须能够找到对应的物理地址Y

但是,不能将这样的信息保存为1:1关系,否则就需要一个与整个物理内存一样大的虚拟地址库。

现代虚拟内存实现通过将虚拟内存物理内存 解释为一长串固定大小的小块来克服这个问题(以及许多其他问题)。
虚拟内存 中将这个块称为物理内存 中将这个块称为

在CPU中有一个硬件组件叫做内存管理单元(MMU),它将 之间的映射信息存储在一个称为页表 的特殊数据结构中。
页表 中每一行都包含一个 索引及其对应的 索引,每个正在运行的程序在MMU中都有一个自己的页表,

如下图所示:

程序Program-1占用3个 内存页,编号为0~2,通过MMU页表映射到物理内存中帧3,4,8

虚拟内存的虚拟地址由两部分组成:

  1. 一个页面索引,告诉虚拟地址所属的页面
  2. 帧偏移量,表示帧内物理地址的位置

2.3. page faults是什么

当程序访问当前未映射到物理帧的虚拟地址时,会发生页面错误page faults)。

更具体地说,当页面存在于程序的页面表中,但指向物理内存中不存在或尚未可用的帧时,就会发生页面错误

比如:

MMU检测到页面错误会将消息反馈到操作系统,操作系统将尽最大努力在物理内存中找到用于映射的帧。

大多数情况下,这是一个简单的操作,除非系统内存不足。

2.4. 内存分页(paging)是什么

分页paging)是另一个内存管理技巧:操作系统将一些页面移动到硬盘驱动器,以便在没有更多物理内存可用时为其他程序或数据腾出空间。
分页 有时也被称为交换swapping),交换是将整个进程移动到磁盘上。

分页 给程序一种无限可用内存的错觉,操作系统乐观地允许虚拟内存地址空间大于物理内存地址空间,知道数据可以在需要时移入和移出硬盘驱动器。

有些系统(如Windows)使用一个特殊的文件,称为分页文件。其他操作系统(例如Linux)有一个称为交换区域的专用硬盘分区。

不过,需要注意的是,硬盘驱动器比主内存慢得多。

因此,当发生页面错误并且页面临时移动到硬盘驱动器时,操作系统必须从缓慢的介质中读取数据并将其移回内存,从而导致延迟。

总而言之,更少的分页意味着系统可以更有效地运行。

2.5. 内存颠簸(Thrashing)是什么

当系统在分页上花费的时间多于运行应用程序的时间时,就会发生抖动,这是由不断的页面错误流触发的。

这是一种极端的情况,比如你运行了太多的程序,占用了整个内存以及在硬盘上的分页区域,

这时就容易发生页面错误,操作系统为了跟上大量的页面错误请求,不断地在硬盘驱动器和物理内存之间移动数据,使系统陷入停顿。

解决这个问题可以通过增加内存的容量,或者减少正在运行的程序的数量,或再次通过调整交换文件的大小来避免抖动。

2.6. 存储保护

虚拟内存还提供了跨运行应用程序的安全性,比如你的浏览器无法窥视你的文本编辑器的虚拟内存,反之亦然。

内存保护的主要目的是防止进程访问不属于它的内存。

内存保护机制通常由MMU及其管理的页表提供。当一个程序试图访问一部分它不拥有的虚拟内存时,就会触发一个无效的页面错误。
MMU和操作系统捕获信号并引发故障条件,称为分段错误(就是耳熟能详的segmentation fault),操作系统通常会终止程序作为响应。

3. 总结

总之,虚拟内存为我们解决了很多问题,也简化了简化了程序员的工作,是目前主流的内存管理方式。

相关推荐
wang_yb8 小时前
manim边学边做--移动动画
databook·manim
wang_yb1 天前
解锁 Git Log 更多实用技巧
git·databook
wang_yb3 天前
manim边学边做--旋转
databook·manim
wang_yb4 天前
从混沌到秩序:Python的依赖管理工具分析
python·databook
wang_yb5 天前
manim边学边做--突出显示
databook·manim
wang_yb6 天前
『玩转Streamlit』--集成Matplotlib
streamlit·databook
wang_yb7 天前
manim边学边做--渐变生长
databook·manim
wang_yb9 天前
谈谈Python中的接口与抽象基类
python·databook
wang_yb10 天前
manim边做边学--淡入淡出
databook·manim
wang_yb11 天前
高效文件处理:Python pathlib实战指南
python·databook