什么是虚拟地址空间?从架构视角来解释
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_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 内核 内存管理虚拟地址空间布局架构 ① ( 虚拟地址空间布局架构 | 用户虚拟地址空间划分 )
文章目录
一、虚拟地址空间布局架构
在 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 操作系统 进程 的 " 用户虚拟空间 " 起始地址 为 0 0 0 ;
" 用户虚拟空间 " 的大小为 TASK_SIZE
, 该值与 处理器 架构 有关 , 不同的处理器 , 定义的 TASK_SIZE
宏不同 ;
- 32 位处理器定义的
TASK_SIZE
宏为TASK_SIZE_32
, 该值为0x100000000 字节 , 大约 4GB ;
- 64 位处理器定义的
TASK_SIZE
宏为TASK_SIZE_64
该值为2VA_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.
*/
/* CONFIG_COMPAT */
源码路径 : LINUX-4.12\\arch\\arm64\\include\\asm\\memory.h#86
以上是关于什么是虚拟地址空间?从架构视角来解释的主要内容,如果未能解决你的问题,请参考以下文章
Linux 内核 内存管理虚拟地址空间布局架构 ⑤ ( Linux 内核中对 “ 虚拟地址空间 “ 的描述 | task_struct 结构体源码 )