0
点赞
收藏
分享

微信扫一扫

什么是虚拟地址空间?从架构视角来解释


引言:小白,虚无,飘渺,渐实,真实,进阶,深化,投入,高阶,系统,架构……操作系统学习之路道阻且长。

为何要从源码的角度来探究?抽象层实在太过抽象!操作系统五大功能,可能学完一学期的课程也难以把它们联系起来,因此从架构角度来解剖,很有必要。

本文操作系统以​​Linux​​为例

此篇也主要是解惑​​操作系统有感​​文章的,毕竟之前觉得操作系统很多东西实在存在的莫名其妙,时至今日,仍有许多疑点。但是对于现代操作系统,特别是​​Linux​​,越来越佩服了。

虚拟内存的三大功能:

  • 高效使用内存:VM将主存看成是存储在磁盘上的地址空间的高速缓存,主存中保存热的数据,根据需要在磁盘和主存之间传送数据;
  • 简化内存管理:VM为每个进程提供了一致的地址空间,从而简化了链接、加载、内存共享等过程;
  • 内存保护:保护每个进程的地址空间不被其他进程破坏。

在现代操作系统的内存管理中,对内存的划分到达了极致(当然基本的功能还在,毕竟是继承自以前的版本),内核态内存,用户态内存等等。

在阅读本文之前,首先存在一个前提,便是​​mem_map​​​的存在,以页帧为单位,它管理着所有物理内存,一般是一个链表,当有内存分配的需求时便从中取出即可(有时候内存对内核内存区和用户内存区分别建立了一个​​mem_map​​链表,用于隔离内存)。

内核初始化内存部分

在当今计算机快速发展的时代,许多计算机早已发展成为了多核多内存的情况,早已不是以前的单核单内存条的状况,因此,对这种情况进行兼容是很需要的,非均匀存储器访问架构​​NUMA, Non-Uniform Memory Access​​正是为了解决这个问题。

非均匀存储器访问架构:​​NUMA​​​,指内存被划分为多个节点,访问一个节点花费的时间取决于处理器与这个节点的距离,每一个处理器内部与一个本地的节点,访问本地节点的速度比访问其他节点的速度快。
均匀存储器访问:​​​UMA​​,也可以称为对城市多处理机加工,意思是所有的处理器访问内存花费的时间都是一样的,也可以理解为整个内存只有一个节点。

内存管理区

内核内存管理区是指把整个物理内存划分为几个区域,每个区域有特殊的含义,可以分为如下几种。

root@huawei ~ # cat /proc/zoneinfo
Node 0, zone DMA
Node 0, zone DMA32
Node 0, zone Normal
Node 0, zone Movable
Node 0, zone Device

下面来一一解释:

  • ​ZONE_DMA​​​:地址范围为​​0-16M​​​,该区域的物理页面专门供​​I/O​​​设备的​​DMA​​​使用,使用该空间时候不需要经过​​MMU​​转换。
  • ​ZONE_NORMAL​​​:范围是​​16M-896M​​,该区域的物理页面是内核能够直接使用的,与内核线性空间存在直接映射关系。
  • ​ZONE_HIGHMEM​​​:​​896M-.​​,高端内存,标记超出内核虚拟地址空间的物理内存段,内核不能直接使用。
  • ​ZONE_MOVEABLE​​:支持物理内存碎片的机制中会用到该内存区域。
  • ​ZONE_DEVICE​​:支持热插拔设备而分配的非易失性内存。

为了让内核能访问物理地址空间,必须先建立映射关系,然后通过虚拟地址来访问。为了能够访问所有的物理地址空间,就要将全部物理地址空间映射到​​1G​​​的内核线性空间中,这显然不可能,因此,内核将​​0-896M​​​的物理地址空间一对一映射到自己的线性地址空间中,这样它便可以随机访问​​ZONE_DMA​​​和​​ZONE_NORMAL​​​的物理页面;此时剩下的​​128G​​​线性地址空间不足以完全映射所有的​​ZONE_HIGHMEM​​​,​​Linux​​​采取动态映射的方法,即按需的将​​ZONE_HIGHMEM​​​里的物理页面映射到最后的​​128M​​​线性地址空间中,使用完之后释放映射关系,以供其他物理页面映射。
所谓的​​​ZONE_HIGHMEM​​​在物理地址空间就是除了原​​0-896M​​​的其他的物理地址空间​​896M-4G​​​,此时才会说​​128M​​​来映射​​896M-4G​​,内核可以访问所有的物理内存。

用户地址空间部分

一个进程的虚拟地址空间主要由两个数据结构来描述,一个是​​mm_struct​​​,一个是​​vm_area_struct​​,前者描述了一个进程的整个虚拟地址空间,后者描述了虚拟地址空间的一个区间。

每一个进程都有自己独立的​​mm_struct​​,这样每一个进程都有自己独立的地址空间,进程之间互不干扰。

​vm_area_struct​​用于定义分配的每个虚拟存储区,包括虚拟存储区的起始和结束地址。以及内存的访问权限等等。

即内存映射:栈,内存映射段,堆,BSS,数据段,代码段
此处注意,BSS段是虚拟内存的概念,在逻辑上是存在的,但是无论是在虚拟地址空间中还是物理地址空间中都是未分配的。

malloc

在​​libc​​​库中,​​malloc​​​函数是实习会根据分配内存的大小来决定使用哪个分配函数。当大小小于等于​​128KB​​​时,调用​​sys_brk​​​分配,对应堆,当大小大于​​128KB​​​时,调用​​sys_mmap​​分配。

​sys_mmap​​在分配过程中主要在堆和栈之间中找一段空闲的虚拟内存并进行映射。

堆由低地址向高地址方向增长,在分配内存时,将指向堆的最高地址的指针​​mm->brk​​向高地址扩展,在释放内存时,把其向低地址收缩。申请堆后,只开辟了一个区域,内核还不好分配真正的物理内存,物理内存的分配是在访问时出现缺页异常之后。

最后有一点请记住,任何的物理内存分配行为都会到​​mem_map​​中进行分配。

结尾

对于前面提出的三个问题,也即是虚拟内存的三大功能,在此可以进行解释了

  • 高效使用内存:当做缓存使用
  • 简化内存管理:所有的用户进程虚拟地址空间都是一个固定的位置,在编译器或者链接器进行执行的时候使用的是虚拟地址空间,减少了因适配性而产生的差错,兼容性
  • 内存保护:每个进程都有自己独立的4G虚拟地址空间,​​0-3G​​​是用户态,​​3G-.​​​是内核态,每个进程都有自己的页表,进程切换的时候使用的是自己的页表,保证了各个进程的虚拟地址空间不会被其他进程给污染(在物理地址中使用统一的​​mem_map​​进行页帧分配)


举报

相关推荐

【Linux】进程虚拟地址空间

0 条评论