1. https://juejin.cn/post/6956541214426398757

在介绍 HugePages 之前,我们先来回顾一下 Linux 下 虚拟内存物理内存 之间的关系。

  • 物理内存:也就是安装在计算机中的内存条,比如安装了 2GB 大小的内存条,那么物理内存地址的范围就是 0 ~ 2GB。

  • 虚拟内存:虚拟的内存地址。由于 CPU 只能使用物理内存地址,所以需要将虚拟内存地址转换为物理内存地址才能被 CPU 使用,这个转换过程由 MMU(Memory Management Unit,内存管理单元) 来完成。在 32 位的操作系统中,虚拟内存空间大小为 0 ~ 4GB。

我们通过 图1 来描述虚拟内存地址转换成物理内存地址的过程:

如 图1 所示,页表 保存的是虚拟内存地址与物理内存地址的映射关系,MMU页表 中找到虚拟内存地址所映射的物理内存地址,然后把物理内存地址提交给 CPU,这个过程与 Hash 算法相似。

内存映射是以内存页作为单位的,通常情况下,一个内存页的大小为 4KB(如图1所示),所以称为 分页机制

一、内存映射

我们来看看在 64 位的 Linux 系统中(英特尔 x64 CPU),虚拟内存地址转换成物理内存地址的过程,如图2:

从图2可以看出,Linux 只使用了 64 位虚拟内存地址的前 48 位(0 ~ 47位),并且 Linux 把这 48 位虚拟内存地址分为 5 个部分,如下:

  • PGD索引:39 ~ 47 位(共9个位),指定在 页全局目录(PGD,Page Global Directory)中的索引。

  • PUD索引:30 ~ 38 位(共9个位),指定在 页上级目录(PUD,Page Upper Directory)中的索引。

  • PMD索引:21 ~ 29 位(共9个位),指定在 页中间目录(PMD,Page Middle Directory)中的索引。

  • PTE索引:12 ~ 20 位(共9个位),指定在 页表(PT,Page Table)中的索引。

  • 偏移量:0 ~ 11 位(共12个位),指定在物理内存页中的偏移量。

把 图1 中的 页表 分为 4 级:页全局目录页上级目录页中间目录页表 目的是为了减少内存消耗(思考下为什么可以减少内存消耗)。

注意:页全局目录、页上级目录、页中间目录 和 页表 都占用一个 4KB 大小的物理内存页,由于 64 位内存地址占用 8 个字节,所以一个 4KB 大小的物理内存页可以容纳 512 个 64 位内存地址。

另外,CPU 有个名为 CR3 的寄存器,用于保存 页全局目录 的起始物理内存地址(如图2所示)。所以,虚拟内存地址转换成物理内存地址的过程如下:

  • CR3 寄存器中获取 页全局目录 的物理内存地址,然后以虚拟内存地址的 39 ~ 47 位作为索引,从 页全局目录 中读取到 页上级目录 的物理内存地址。

  • 以虚拟内存地址的 30 ~ 38 位作为索引,从 页上级目录 中读取到 页中间目录 的物理内存地址。

  • 以虚拟内存地址的 21 ~ 29 位作为索引,从 页中间目录 中读取到 页表 的物理内存地址。

  • 以虚拟内存地址的 12 ~ 20 位作为索引,从 页表 中读取到 物理内存页 的物理内存地址。

  • 以虚拟内存地址的 0 ~ 11 位作为 物理内存页 的偏移量,得到最终的物理内存地址。

二、HugePages 原理

上面介绍了以 4KB 的内存页作为内存映射的单位,但有些场景我们希望使用更大的内存页作为映射单位(如 2MB)。使用更大的内存页作为映射单位有如下好处:

  • 减少 TLB(Translation Lookaside Buffer) 的失效情况。

  • 减少 页表 的内存消耗。

  • 减少 PageFault(缺页中断)的次数。

Tips:TLB 是一块高速缓存,TLB 缓存虚拟内存地址与其映射的物理内存地址。MMU 首先从 TLB 查找内存映射的关系,如果找到就不用回溯查找页表。否则,只能根据虚拟内存地址,去页表中查找其映射的物理内存地址。

因为映射的内存页越大,所需要的 页表 就越小(很容易理解);页表 越小,TLB 失效的情况就越少。

使用大于 4KB 的内存页作为内存映射单位的机制叫 HugePages,目前 Linux 常用的 HugePages 大小为 2MB 和 1GB,我们以 2MB 大小的内存页作为例子。

要映射更大的内存页,只需要增加偏移量部分,如 图3 所示:

如 图3 所示,现在把偏移量部分扩展到 21 位(页表部分被覆盖了,21 位能够表示的大小范围为 0 ~ 2MB),所以 页中间目录 直接指向映射的 物理内存页地址

这样,就可以减少 页表 部分的内存消耗。由于内存映射关系变少,所以 TLB 失效的情况也会减少。

三、HugePages 使用

了解了 HugePages 的原理后,我们来介绍一下怎么使用 HugePages。

HugePages 的使用不像普通内存申请那么简单,而是需要借助 Hugetlb文件系统 来创建,下面将会介绍 HugePages 的使用步骤:

1. 挂载 Hugetlb 文件系统

Hugetlb 文件系统是专门为 HugePages 而创造的,我们可以通过以下命令来挂载一个 Hugetlb 文件系统:

 
shell

复制代码
  1. 1$ mkdir /mnt/huge
  2. 2$ mount none /mnt/huge -t hugetlbfs

执行完上面的命令后,我们就在 /mnt/huge 目录下挂载了 Hugetlb 文件系统。

2. 声明可用 HugePages 数量

要使用 HugePages,首先要向内核声明可以使用的 HugePages 数量。/proc/sys/vm/nr_hugepages 文件保存了内核可以使用的 HugePages 数量,我们可以使用以下命令设置新的可用 HugePages 数量:

 
ruby

复制代码
  1. 1$ echo 20 > /proc/sys/vm/nr_hugepages

上面命令设置了可用的 HugePages 数量为 20 个(也就是 20 个 2MB 的内存页)。

3. 编写申请 HugePages 的代码

要使用 HugePages,必须使用 mmap 系统调用把虚拟内存映射到 Hugetlb 文件系统中的文件,如下代码:

 
arduino

复制代码
  1. 1#include <fcntl.h>
  2. 2#include <sys/mman.h>
  3. 3#include <errno.h>
  4. 4#include <stdio.h>
  5. 5
  6. 6#define MAP_LENGTH (10*1024*1024) // 10MB
  7. 7
  8. 8int main()
  9. 9{
  10. 10    int fd;
  11. 11    void * addr;
  12. 12
  13. 13    // 1. 创建一个 Hugetlb 文件系统的文件
  14. 14    fd = open("/mnt/huge/hugepage1", O_CREAT|O_RDWR);
  15. 15    if (fd < 0) {
  16. 16        perror("open()");
  17. 17        return -1;
  18. 18    }
  19. 19
  20. 20    // 2. 把虚拟内存映射到 Hugetlb 文件系统的文件中
  21. 21    addr = mmap(0, MAP_LENGTH, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  22. 22    if (addr == MAP_FAILED) {
  23. 23        perror("mmap()");
  24. 24        close(fd);
  25. 25        unlink("/mnt/huge/hugepage1");
  26. 26        return -1;
  27. 27    }
  28. 28
  29. 29    strcpy(addr, "This is HugePages example...");
  30. 30    printf("%s\n", addr);
  31. 31
  32. 32    // 3. 使用完成后,解除映射关系
  33. 33    munmap(addr, MAP_LENGTH);
  34. 34    close(fd);
  35. 35    unlink("/mnt/huge/hugepage1");
  36. 36
  37. 37    return 0;
  38. 38 }
 
复制代码

编译上面的代码并且执行,如果没有问题,将会输出以下信息:

 
csharp

复制代码
  1. This is HugePages example...

四、总结


本文主要介绍了 HugePages 的原理和使用,虽然 HugePages 有很多优点,但也有其不足的地方。比如调用 fork 系统调用创建子进程时,内核使用了 写时复制 的技术(可参考《Linux 写时复制机制原理》一文),在父子进程内存发生改变时,需要复制更大的内存页,从而影响性能。

作者:JaydenLie
链接:https://juejin.cn/post/6956541214426398757
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

[转帖]一文读懂 HugePages(大内存页)的原理的更多相关文章

  1. 一文读懂机器学习大杀器XGBoost原理

    http://blog.itpub.net/31542119/viewspace-2199549/ XGBoost是boosting算法的其中一种.Boosting算法的思想是将许多弱分类器集成在一起 ...

  2. [转帖]一文读懂 HTTP/2

    一文读懂 HTTP/2 http://support.upyun.com/hc/kb/article/1048799/ 又小拍 • 发表于:2017年05月18日 15:34:45 • 更新于:201 ...

  3. 一文读懂原子操作、内存屏障、锁(偏向锁、轻量级锁、重量级锁、自旋锁)、Disruptor、Go Context之上半部分

    我不想卷,我是被逼的 在做了几年前端之后,发现互联网行情比想象的差,不如赶紧学点后端知识,被裁之后也可接个私活不至于饿死.学习两周Go,如盲人摸象般不知重点,那么重点谁知道呢?肯定是使用Go的后端工程 ...

  4. 【Java基本功】一文读懂String及其包装类的实现原理

    String作为Java中最常用的引用类型,相对来说基本上都比较熟悉,无论在平时的编码过程中还是在笔试面试中,String都很受到青睐,然而,在使用String过程中,又有较多需要注意的细节之处. S ...

  5. 「Java基本功」一文读懂Java内部类的用法和原理

    内部类初探 一.什么是内部类? 内部类是指在一个外部类的内部再定义一个类.内部类作为外部类的一个成员,并且依附于外部类而存在的.内部类可为静态,可用protected和private修饰(而外部类只能 ...

  6. 一文读懂 SuperEdge 边缘容器架构与原理

    前言 superedge是腾讯推出的Kubernetes-native边缘计算管理框架.相比openyurt以及kubeedge,superedge除了具备Kubernetes零侵入以及边缘自治特性, ...

  7. [转帖]MerkleDAG全面解析 一文读懂什么是默克尔有向无环图

    MerkleDAG全面解析 一文读懂什么是默克尔有向无环图 2018-08-16 15:58区块链/技术 MerkleDAG作为IPFS的核心数据结构,它融合了Merkle Tree和DAG的优点,今 ...

  8. [转帖]从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路

    从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路   http://www.52im.net/thread-1709-1-2.html     本文原作者阮一峰,作者博客:r ...

  9. 大数据篇:一文读懂@数据仓库(PPT文字版)

    大数据篇:一文读懂@数据仓库 1 网络词汇总结 1.1 数据中台 数据中台是聚合和治理跨域数据,将数据抽象封装成服务,提供给前台以业务价值的逻辑概念. 数据中台是一套可持续"让企业的数据用起 ...

  10. 一文读懂HTTP/2及HTTP/3特性

    摘要: 学习 HTTP/2 与 HTTP/3. 前言 HTTP/2 相比于 HTTP/1,可以说是大幅度提高了网页的性能,只需要升级到该协议就可以减少很多之前需要做的性能优化工作,当然兼容问题以及如何 ...

随机推荐

  1. Freezable ---探索WPF中Freezable承载数据的原理

    引言 在之前写的一篇文章[WPF --- 如何以Binding方式隐藏DataGrid列]中,我先探索了 DataGridTextColumn 为什么不在可视化树结构内?又给出了解决方案,使用 Fre ...

  2. 2023-09-13:用go语言,给定一个整数数组 nums 和一个正整数 k, 找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。 输入: nums = [4, 3, 2, 3, 5,

    2023-09-13:用go语言,给定一个整数数组 nums 和一个正整数 k, 找出是否有可能把这个数组分成 k 个非空子集,其总和都相等. 输入: nums = [4, 3, 2, 3, 5, 2 ...

  3. LeetCode DFS、BFS篇(102、200、111、752)

    102. 二叉树的层序遍历 给你一个二叉树,请你返回其按 层序遍历 得到的节点值. (即逐层地,从左到右访问所有节点). 示例: 二叉树:[3,9,20,null,null,15,7], 3 / 9 ...

  4. 云图说|分钟级构建业务大屏——Astro大屏应用

    本文分享自华为云社区<[云图说]第271期 Astro Canvas一站式数据可视化开发,分钟级构建业务大屏>,作者:阅识风云 . Astro大屏应用(Astro Canvas)是Astr ...

  5. 重磅更新!Sermant 1.2.0 release版本新特性速览

    本文分享自华为云社区<重磅更新!Sermant 1.2.0 release版本新特性速览>,作者:华为云开源. 10月,Sermant社区正式发布了1.2.0 release版本,距离上一 ...

  6. CWE4.6标准中加入 OWASP 2021 TOP10

    摘要: 新发布的CWE4.6标准,加入了OWASP 2021 TOP10的视图. 本文分享自华为云社区<CWE 4.6 和 OWAPS TOP10(2021)>,作者: Uncle_Tom ...

  7. 开心档之MySQL 创建数据表

    MySQL 创建数据表 创建MySQL数据表需要以下信息: 表名 表字段名 定义每个表字段 语法 以下为创建MySQL数据表的SQL通用语法: CREATE TABLE table_name (col ...

  8. peewee 操作 sqlite 锁表问题分析

    在使用python orm 框架 peewee 操作数据库时时常会抛出以一个异常,具体的报错就是 database is locked 初步了解是因为sqlite锁的颗粒度比较大,是库锁.当一个连接在 ...

  9. Codeforces Round #694 (Div. 2) A~D、E

    比赛链接:Here 1471A. Strange Partition 题意: 给一个数组,数组中的所有元素可以任意合并,求数组的每个元素除以x上取整的和,求结果的最大值和最小值. 思路: 瞎猜.最小值 ...

  10. 从“预见”到“遇见”SAE 引领应用步入 Serverless 全托管新时代

    --黛忻 阿里云SAE产品经理 近年来,企业的数字化随着互联网的普及发展越来越快,技术架构也是几经更迭,尤其是在线业务部分.从最初的单体应用到分布式应用再到云原生应用,出现了进阶式的变化. 带来便利的 ...