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

Posted 雨落水涨next

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是虚拟地址空间?从架构视角来解释相关的知识,希望对你有一定的参考价值。

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

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

本文操作系统以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_HIGHMEM896M-.,高端内存,标记超出内核虚拟地址空间的物理内存段,内核不能直接使用。
  • ZONE_MOVEABLE:支持物理内存碎片的机制中会用到该内存区域。
  • ZONE_DEVICE:支持热插拔设备而分配的非易失性内存。

为了让内核能访问物理地址空间,必须先建立映射关系,然后通过虚拟地址来访问。为了能够访问所有的物理地址空间,就要将全部物理地址空间映射到1G的内核线性空间中,这显然不可能,因此,内核将0-896M的物理地址空间一对一映射到自己的线性地址空间中,这样它便可以随机访问ZONE_DMAZONE_NORMAL的物理页面;此时剩下的128G线性地址空间不足以完全映射所有的ZONE_HIGHMEMLinux采取动态映射的方法,即按需的将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 内核 内存管理虚拟地址空间布局架构 ① ( 虚拟地址空间布局架构 | 用户虚拟地址空间划分 )



文章目录






一、虚拟地址空间布局架构



在 64 64 64 位的 Linux 操作系统中 , " ARM64 架构 " 并 不支持 64 64 64 位的虚拟地址 , 最大只支持 48 48 48 位的虚拟地址 , 64 64 64 位地址太大 , 并不需要那么大的内存空间 ;

" ARM64 架构 " 中 , Linux 系统的 " 内核虚拟地址 “ 与 ” 用户虚拟地址 " 是等同的 ;

  • 用户虚拟地址 :​0x 0000 0000 0000 0000​​​ ~​​0x 0000 FFFF FFFF FFFF​​ , 48 位有效地址 ;
  • 内核虚拟地址 :​0x FFFF 0000 0000 0000​​​ ~​​0x FFFF FFFF FFFF FFFF​​ , 48 位有效地址 ;

【Linux






二、用户虚拟地址空间划分



Linux 操作系统 进程 的 " 用户虚拟空间 " 起始地址 为 0 0 0 ;

" 用户虚拟空间 " 的大小为 ​​TASK_SIZE​​​ , 该值与 处理器 架构 有关 , 不同的处理器 , 定义的 ​​TASK_SIZE​​ 宏不同 ;

  • 32 位处理器定义的​​TASK_SIZE​​​ 宏为​​TASK_SIZE_32​​ , 该值为0x100000000 字节 , 大约 4GB ;
#define TASK_SIZE_32    UL(0x100000000)
  • 64 位处理器定义的​​TASK_SIZE​​​ 宏为​​TASK_SIZE_64​​ 该值为2VA_BITS 字节 ;
#define TASK_SIZE_64    (UL(1) << VA_BITS)



在 Linux 内核源码的 LINUX-4.12\\arch\\arm64\\include\\asm\\memory.h#86 中 , 定义了 ​​TASK_SIZE​​​ 与 ​​TASK_SIZE_64​​ 宏 ;

​VA_BITS​​ 是编译内核时 , 选择的 " 虚拟地址空间 " 的地址位数 ,



​TASK_SIZE​​​ 与 ​​TASK_SIZE_64​​ 宏 相关源码如下 :

/*
* PAGE_OFFSET - the virtual address of the start of the linear map (top
* (VA_BITS - 1))
* KIMAGE_VADDR - the virtual address of the start of the kernel image
* VA_BITS - the maximum number of bits for virtual addresses.
* VA_START - the first kernel virtual address.
* TASK_SIZE - the maximum size of a user space task.
* TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area.
*/
#define VA_BITS (CONFIG_ARM64_VA_BITS)
#define VA_START (UL(0xffffffffffffffff) << VA_BITS)
#define PAGE_OFFSET (UL(0xffffffffffffffff) << (VA_BITS - 1))
#define KIMAGE_VADDR (MODULES_END)
#define MODULES_END (MODULES_VADDR + MODULES_VSIZE)
#define MODULES_VADDR (VA_START + KASAN_SHADOW_SIZE)
#define MODULES_VSIZE (SZ_128M)
#define VMEMMAP_START (PAGE_OFFSET - VMEMMAP_SIZE)
#define PCI_IO_END (VMEMMAP_START - SZ_2M)
#define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE)
#define FIXADDR_TOP (PCI_IO_START - SZ_2M)
#define TASK_SIZE_64 (UL(1) << VA_BITS)

#ifdef CONFIG_COMPAT
#define TASK_SIZE_32 UL(0x100000000)
#define TASK_SIZE (test_thread_flag(TIF_32BIT) ? \\
TASK_SIZE_32 : TASK_SIZE_64)
#define TASK_SIZE_OF(tsk) (test_tsk_thread_flag(tsk, TIF_32BIT) ? \\
TASK_SIZE_32 : TASK_SIZE_64)
#else
#define TASK_SIZE TASK_SIZE_64
#endif /* CONFIG_COMPAT */

源码路径 : LINUX-4.12\\arch\\arm64\\include\\asm\\memory.h#86



以上是关于什么是虚拟地址空间?从架构视角来解释的主要内容,如果未能解决你的问题,请参考以下文章

Linux内存管理2---段机制

Linux 内核 内存管理虚拟地址空间布局架构 ⑤ ( Linux 内核中对 “ 虚拟地址空间 “ 的描述 | task_struct 结构体源码 )

Linux - 用户态内存映射 和 内核态内存映射

Linux内存管理4---虚拟地址空间管理

Linux 内核 内存管理虚拟地址空间布局架构 ⑦ ( Linux 内核地址空间布局简介 )

Linux从无到有进程的地址空间