前面我们讲了,在32位linux内核里,内核地址空间是0xc0000000~0xffffffff, 大小1G;内核地址空间是0x00000000~0xbfffffff,大小3G。当内核代码访问内存时,cpu是需要把这些逻辑转换为物理地址的,也就是前面我们讲到的二级页表的转换过程。

细细看来,二级页表的转换过程可以是有某种规律的。比如,内核中的二级页表的转换就经过了人为的设定,保证了:逻辑地址(和线性地址一致)与物理地址对应的关系为

物理地址 = 逻辑地址 – 0xC0000000,也就是:

这样的好处是:内核逻辑地址和物理地址的转换变得很更高效、更直观、也更加方便进行内核的debug。

内核里定义了如下宏定义进行物理地址和逻辑地址的转换:

#define __PAGE_OFFSET           (0xC0000000)
……
#define PAGE_OFFSET             ((unsigned long)__PAGE_OFFSET)
#define __pa(x)                 ((unsigned long)(x)-PAGE_OFFSET)
#define __va(x)                 ((void *)((unsigned long)(x)+PAGE_OFFSET))

但是如果按照上面的逻辑,内核逻辑地址空间访问为0xc0000000 ~ 0xffffffff,那么对应的物理内存范围就为0×0 ~ 0×40000000,即只能访问1G物理内存。若机器中安装8G物理内存,那么内核就只能访问前1G物理内存,后面7G物理内存是不是就无法访问了?

很显然是不可能的,作为整个系统的管理单元,内核必须能够访问所有的物理内存,那具体内核是怎么做的呢?

很显然不能将内核地址空间0xc0000000 ~ 0xfffffff全部用来简单的地址映射。因此x86架构中将内核地址空间划分三部分:ZONE_DMA、ZONE_NORMAL和 ZONE_HIGHMEM。ZONE_HIGHMEM即为高端内存,这就是内存高端内存概念的由来。

在x86结构中,三种类型的区域如下:

ZONE_DMA 内存开始的16MB

ZONE_NORMAL 16MB~896MB

ZONE_HIGHMEM 896MB ~ 结束

高端内存HIGH_MEM地址空间范围为 0xF8000000 ~ 0xFFFFFFFF(896MB~1024MB)。那么如内核是如何借助128MB高端内存地址空间是如何实现访问可以所有物理内存

当内核想访问高于896MB物理地址内存时,从0xF8000000 ~ 0xFFFFFFFF地址空间范围内找一段相应大小空闲的逻辑地址空间,借用一会。借用这段逻辑地址空间,建立映射到想访问的那段物理内存(即填充内核PTE页面表),临时用一会,用完后归还。这样别人也可以借用这段地址空间访问其他物理内存,实现了使用有限的地址空间,访问所有所有物理内存。如下图所示:

地址增长方向为从上往下

例如内核想访问2G开始的一段大小为1MB的物理内存,即物理地址范围为0×80000000 ~ 0x800FFFFF。访问之前先找到一段1MB大小的空闲线性地址空间,假设找到的空闲线性地址空间为0xF8700000 ~ 0xF87FFFFF,用这1MB的线性地址空间映射到物理地址空间0×80000000 ~ 0x800FFFFF的内存。映射关系如下:

当内核访问完0×80000000 ~ 0x800FFFFF物理内存后,就将0xF8700000 ~ 0xF87FFFFF内核线性空间释放。这样其他进程或代码也可以使用0xF8700000 ~ 0xF87FFFFF这段地址访问其他物理内存。

从上面的描述,我们可以知道高端内存的最基本思想:借一段线性地址空间,建立临时地址映射,用完后释放,达到这段地址空间可以循环使用,访问所有物理内存。

看到这里,不禁有人会问:万一有内核进程或模块一直占用某段逻辑地址空间不释放,怎么办?

若真的出现的这种情况,则内核的高端内存地址空间越来越紧张,若都被占用不释放,则没有建立映射到物理内存都无法访问了。

接下来,我们看一下linux内核对高端内存的官网划分:

内核将高端内存划分为3部分:VMALLOC_START~VMALLOC_END、KMAP_BASE~FIXADDR_START和FIXADDR_START~4G。

PAGE_OFFSET也就是3G对应的地址。

对 于高端内存,可以通过 alloc_page() 或者其它函数获得对应的 page,但是要想访问实际物理内存,还得把 page 转为线性地址才行(为什么?想想 MMU 是如何访问物理内存的),也就是说,我们需要为高端内存对应的 page 找一个线性空间,这个过程称为高端内存映射。

对应高端内存的3部分,高端内存映射有三种方式:

映射到”内核动态映射空间”(noncontiguous memory allocation)这种方式很简单,因为通过 vmalloc() ,在”内核动态映射空间”申请内存的时候,就可能从高端内存获得页面(参看 vmalloc 的实现),因此说高端内存有可能映射到”内核动态映射空间”中。

持久内核映射(permanent kernel mapping)如果是通过 alloc_page() 获得了高端内存对应的 page,如何给它找个线性空间?内核专门为此留出一块线性空间,从 PKMAP_BASE 到 FIXADDR_START ,用于映射高端内存。在 2.6内核上,这个地址范围是 4G-8M 到 4G-4M 之间。这个空间起叫”内核永久映射空间”或者”永久内核映射空间”。这个空间和其它空间使用同样的页目录表,对于内核来说,就是 swapper_pg_dir,对普通进程来说,通过 CR3 寄存器指向。通常情况下,这个空间是 4M 大小,因此仅仅需要一个页表即可,内核通过来 pkmap_page_table 寻找这个页表。通过 kmap(),可以把一个 page 映射到这个空间来。由于这个空间是 4M 大小,最多能同时映射 1024 个 page。因此,对于不使用的的 page,及应该时从这个空间释放掉(也就是解除映射关系),通过 kunmap() ,可以把一个 page 对应的线性地址从这个空间释放出来。

临时映射(temporary kernel mapping)内核在 FIXADDR_START 到 FIXADDR_TOP 之间保留了一些线性空间用于特殊需求。这个空间称为”固定映射空间”在这个空间中,有一部分用于高端内存的临时映射。

这块空间具有如下特点:(1)每个 CPU 占用一块空间(2)在每个 CPU 占用的那块空间中,又分为多个小空间,每个小空间大小是 1 个 page,每个小空间用于一个目的,这些目的定义在 kmap_types.h 中的 km_type 中。

当要进行一次临时映射的时候,需要指定映射的目的,根据映射目的,可以找到对应的小空间,然后把这个空间的地址作为映射地址。这意味着一次临时映射会导致以前的映射被覆盖。通过 kmap_atomic() 可实现临时映射。

linux内核1G虚拟地址空间的映射规则以及什么是高端内存?相关推荐

  1. Linux内存管理:内存描述之高端内存

    <Linux内存管理:内存描述之内存节点node> <Linux内存管理:内存描述之内存区域zone> <Linux内存管理:内存描述之内存页面page> < ...

  2. linux高端物理内存,Linux内存管理之高端内存

    看了Linux内核内存管理,参考网上的意见整理了一下. 1.页框管理 Linux采用4KB页框大小作为标准的内存分配单元.内核必须记录每个页框的状态,这种状态信息保存在一个类型为page的页描述符中, ...

  3. 高端内存映射之kmap持久内核映射--Linux内存管理(二十)

    日期 内核版本 架构 作者 GitHub CSDN 2016-09-29 Linux-4.7 X86 & arm gatieme LinuxDeviceDrivers Linux内存管理 在内 ...

  4. 【Linux】进程虚拟地址空间

    进程虚拟地址空间打破了我一直以来对于程序地址空间的认识,它真的好神奇. 我们首先来看一下下面这段代码: 1 #include<stdio.h>2 #include<unistd.h& ...

  5. linux 物理内存用完了_Linux用户空间与内核空间(理解高端内存)

    Linux内核地址映射模型 x86 CPU采用了段页式地址映射模型.进程代码中的地址为逻辑地址,经过段页式地址映射后,才真正访问物理内存. 段页式机制如下图. Linux内核地址空间划分 通常32位L ...

  6. Linux用户空间与内核空间(理解高端内存)

    目录 Linux内核地址映射模型 Linux内核地址空间划分 Linux内核高端内存的由来 Linux内核高端内存的理解 Linux内核高端内存的划分 常见问题 小结 1.虚拟内核空间到物理空间的映射 ...

  7. linux 内核高端内存意义,Linux内核高端内存管理

    原先一直都对Linux高端内存的管理认识模模糊糊的,可能主要是初次接触Linux kernel 是0.11版吧,当初的内存设计是16M,Linus对拥有32M的内存都是觊觎万分,1G内存恐怕是天方夜谭 ...

  8. linux 各用户内存_Linux用户空间与内核空间(理解高端内存)

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...

  9. 【Linux】Linux的内核空间(低端内存、高端内存)

    内核也是程序,也应该具有自己的虚存空间,但是作为一种为用户程序服务的程序,内核空间有它自己的特点. 内核空间与用户空间的关系 在一个32位系统中,一个程序的虚拟空间最大可以是4GB,那么最直接的做法就 ...

最新文章

  1. python安装包_迈出Python学习第一步:Python开发环境的下载与安装
  2. eclipse修改文件代码不起作用,输出时还是老的,估计是缓存问题
  3. SQL Server 2005 查询处理器未能为执行并行查询启动必要的线程资源。
  4. java领域对象_java的几种对象(po,dto,dao等)
  5. 【Python教程】python函数后面有多个括号的作用
  6. 小学五年级就已经开始编程啦吗???
  7. Bada学习- C++以及Flash应用开发流程之创建应用工程
  8. HDU 5025Saving Tang Monk BFS + 二进制枚举状态
  9. C++ primer 第7章 类
  10. 直播丨数据库上云趋势下,如何面对海量数据迁移及落地实践-2021云和恩墨大讲堂...
  11. cmake 生成mysql_采用cmake方式编译安装MySQL
  12. 什么时候出来的_DNF手游官网正版什么时候出来?地下城手游上线日期分享
  13. spark生态及各个功能
  14. 当head遇上runat=server之后发生的纠结之事
  15. php脚本语法格式,[PHP学习笔记][五]PHP基本语法规则
  16. python写彩票程序30选7_写一个彩票程序:30选7
  17. 申请免费的域名并且通过FTP上传及通过自己的域名访问自己的网站
  18. Spring Framework灰度发布
  19. Linux实战(20):Docker部署EKL入门环境记录文档
  20. Centos启动界面:You are in emergency mode

热门文章

  1. Android开发-使用FTP协议和HTTP协议进行文件下载和上传
  2. springboot 整合 LDAP
  3. 什么是MQ?(理论知识)
  4. 多线程之join方法
  5. 吃鸡是python写的吗_吃鸡手游竟是这个编程语言所写,它荣登最流行的编程语言榜首...
  6. MySQL字符集和排序规则详解
  7. 修改mysql默认字符集为latin1_查看、设置与修改MySQL字符集
  8. 现在商业有种竞争叫“跨界打击”
  9. 信用卡“透支”怎么说?
  10. 集中式自动抄表系统原理与应用