物理地址和虚拟地址的分布

上一节内容的学习我们知道了CPU是如何访问内存的,CPU拿到内存后就可以向其它人(kernel的其它模块、内核线程、用户空间进程、等等)提供服务,主要包括:

  • 以虚拟地址(VA)的形式,为应用程序提供远大于物理内存的虚拟地址空间(Virtual Address Space)
  • 每个进程都有独立的虚拟地址空间,不会相互影响,进而可提供非常好的内存保护(memory protection)
  • 提供内存映射(Memory Mapping)机制,以便把物理内存、I/O空间、Kernel Image、文件等对象映射到相应进程的地址空间中,方便进程的访问
  • 提供公平、高效的物理内存分配(Physical Memory Allocation)算法
  • 提供进程间内存共享的方法(以虚拟内存的形式),也称作Shared Virtual Memory

在提供这些服务之前需要对内存进行合理的划分和管理,下面让我们看下是如何划分的。

物理地址空间布局

Linux系统在初始化时,会根据实际的物理内存的大小,为每个物理页面创建一个page对象,所有的page对象构成一个mem_map数组。进一步,针对不同的用途,Linux内核将所有的物理页面划分到3类内存管理区中,如图,分别为ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。

  • ZONE_DMA 的范围是 0~16M,该区域的物理页面专门供 I/O 设备的 DMA 使用。之所以需要单独管理 DMA 的物理页面,是因为 DMA 使用物理地址访问内存,不经过 MMU,并且需要连续的缓冲区,所以为了能够提供物理上连续的缓冲区,必须从物理地址空间专门划分一段区域用于 DMA。
  • ZONE_NORMAL 的范围是 16M~896M,该区域的物理页面是内核能够直接使用的。
  • ZONE_HIGHMEM 的范围是 896M~结束,该区域即为高端内存,内核不能直接使用。

Linux内核空间虚拟地址分布

在 Kernel Image 下面有 16M 的内核空间用于 DMA 操作。位于内核空间高端的 128M 地址主要由3部分组成,分别为 vmalloc area、持久化内核映射区、临时内核映射区。

由于 ZONE_NORMAL 和内核线性空间存在直接映射关系,所以内核会将频繁使用的数据如 Kernel 代码、GDT、IDT、PGD、mem_map 数组等放在 ZONE_NORMAL 里。而将用户数据、页表(PT)等不常用数据放在 ZONE_HIGHMEM 里,只在要访问这些数据时才建立映射关系(kmap())。比如,当内核要访问 I/O 设备存储空间时,就使用 ioremap() 将位于物理地址高端的 mmio 区内存映射到内核空间的 vmalloc area 中,在使用完之后便断开映射关系。

Linux用户空间虚拟地址分布

用户进程的代码区一般从虚拟地址空间的 0x08048000 开始,这是为了便于检查空指针。代码区之上便是数据区,未初始化数据区,堆区,栈区,以及参数、全局环境变量。

Linux物理地址和虚拟地址的关系

Linux 将 4G 的线性地址空间分为2部分,0~3G 为 user space,3G~4G 为 kernel space。

由于开启了分页机制,内核想要访问物理地址空间的话,必须先建立映射关系,然后通过虚拟地址来访问。为了能够访问所有的物理地址空间,就要将全部物理地址空间映射到 1G 的内核线性空间中,这显然不可能。于是,内核将 0~896M 的物理地址空间一对一映射到自己的线性地址空间中,这样它便可以随时访问 ZONE_DMA 和 ZONE_NORMAL 里的物理页面;此时内核剩下的 128M 线性地址空间不足以完全映射所有的 ZONE_HIGHMEM,Linux 采取了动态映射的方法,即按需的将 ZONE_HIGHMEM 里的物理页面映射到 kernel space 的最后 128M 线性地址空间里,使用完之后释放映射关系,以供其它物理页面映射。虽然这样存在效率的问题,但是内核毕竟可以正常的访问所有的物理地址空间了。

到这里我们应该知道了 Linux 是如何用虚拟地址来映射物理地址的,最后我们用一张图来总结一下:

原文发布于微信公众号 - 人人都是极客(rrgeek)

原文发表时间:2018-04-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏cs

计算机网络--子网划分+思科CiscroPacket使用

<h1>1.0概念</h1> <p> <p><b>子网划分</b>是通过借用IP地址的若干位主机位来充当子网地址从而将原网络划分为若干子网而实现的...

4129
来自专栏沃趣科技

Oracle中的sysctl.conf内核参数

当我们对Oracle进行安装部署时,需要按照相关要求修改OS内核参数,下面对Oracle按照部署时需要修改的相关内核参数进行简单介绍。

1293
来自专栏乐沙弥的世界

Nginx内置状态信息(http_stub_status)

Nginx提供了一个内置的状态信息监控页面,可用于监控Nginx的整体访问情况。这个内置功能由模块ngx_http_stub_status_module实现。如...

882
来自专栏从零开始学自动化测试

Appium+python自动化27-android特有的wait_activity

前言 在启动app的时候,如果直接做下一步点击操作,经常会报错,于是我们会在启动完成的时候加sleep。 那么问题来了,这个sleep时间到底设置多少合适呢?设...

3434
来自专栏小鹏的专栏

tf API 研读6:Running Graphs

会话管理 (Session management) 操作 描述 class tf.Session 运行TF操作的类, 一个Session对象将操作节...

2136
来自专栏Danny的专栏

MySQL安装图解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

2153
来自专栏java架构师

Hadoop学习17--yarn配置篇-内存管理

这篇文章来自于:董的博客,记录备查 内存管理,主要是管理nodemanager上的物理内存和虚拟内存。 YARN允许用户配置每个节点上可用的物理内存资源,注意,...

3078
来自专栏我是业余自学C/C++的

汇编语言-第二章 寄存器(CPU工作原理)之执行指令过程(一)

2233
来自专栏FreeBuf

某开源博客系统最新版源码审计

近期审计一个JAVA开源博客系统mblog2.5最新版,整体系统还是很安全的,漏洞较少,传统的问题像XSS系统使用全局过滤的方式进行实体化处理,上传问题使用白名...

1395
来自专栏H2Cloud

linux epoll 开发指南-【ffrpc源码解析】

摘要 关于epoll的问题很早就像写文章讲讲自己的看法,但是由于ffrpc一直没有完工,所以也就拖下来了。Epoll主要在服务器编程中使用,本文主要探讨服务器程...

3935

扫码关注云+社区