关注了就能看到更多这么棒的文章哦~

Clarifying memory management with page folios

By Jonathan Corbet
March 18, 2021
DeepL assisted translation
https://lwn.net/Articles/849538/

内存管理(memory management)一般是以 page 为单位进行的,一个 page 通常包含 4,096 个字节,也可能更大。内核已经将 page 的概念扩展到所谓的 compound page(复合页),即一组组物理连续的单独 page 的组合。这又使得 "page" 的定义变得有些模糊了。Matthew Wilcox 从去年开始就一直在研究一个叫做 "page folios" 的概念,希望能让人们重新关注这个问题。不过,目前还不清楚内存管理社区是否会接受这个概念。

从最底层来说,page 是一个由硬件实现的概念。对内存的跟踪以及判定是否位于 RAM 中这都是以 page 为基本单位来进行的。无论是哪种 CPU 架构,尽管可能会提供有限几种 page size,但一定会从中选择一个 "base" page size,最常见的选择仍然是 4,096 字节——这与 30 年前第一个 Linux 内核发布时的情况一样。

不过,内核经常需要获取更大块的内存(memory in larger chunks)。其中一个例子是管理 "huge pages",huge page 同样也是由硬件实现的。例如,x86 架构可以使用 2MB 的 huge page,在恰当的地方使用 huge page 可以提高性能。内核也会分配用其他 size 为单位来分配一组 page,通常是用于 DMA buffer 或其他那些需要使用连续的若干 page 的场景下。这种页面分组(grouping of pages)在内核中被称为 "compound page(复合页)"。

内核所管理的内存中,每一个 base page 都是用 system memory map 中的一个 page structure 来代表的。如果利用一组 base page 创建出了一个 compound page,那么就会对这组 pages 中的第一个 page("head page")的 page structure 打上一个特殊标记,从而明确指出这是一个 compound page。这个 head page 的 page structure 中的其他信息都是对整个 compound page 生效的。所有其他 page("tail pages")也都被标记出来,使用一个指针指向相关的 head page 的 page structure。关于 compound pages 的组织方式,请参见An introduction to compound pages。

这种机制可以很容易从 tail page 的 page structure 找到整个 compound page 的 head page。内核中的许多接口都利用了这一特性,但它带来一个歧义问题:如果某个函数收到参数是一个指向了 tail page 的 page structure,那么这个函数是应该针对这个 tail page 上执行、还是在整个 compound page 上执行?或者,正如 Wilcox 在 12 月对 folio 系列 patch 的第一封邮件中所说的:

一个有 struct page 参数的函数通常预期收到的是一个 head page 或者 base page,如果给它一个 tail page 的话它就会出现 BUG。有的函数也许对任何 page 都可以支持,直接操作 PAGE_SIZE 这么多字节;有的函数可能针对 head page 会操作 page_size()这么多字节,而对 base page 或者 tail page 则操作 PAGE_SIZE 这么多字节;还有的可能收到 head page 或者 tail page 的时候都是按照 page_size()字节数来处理。我们现在的代码中以上情况都有出现。

(PAGE_SIZE 是一个 base page 的 size,而 page_size()则返回一个 page 的整体大小(它可能是个 compound page)。这个特殊的 API 似乎在历史上并没有出现过很多 bug,但是作为一个定义得这么含糊的接口,迟早会引发问题。

为了让这种情况的处理更加明晰,Wilcox 提出了 "page folio" 的概念,它实际上仍然是一个 page structure,只是保证了它一定不是 tail page。任何接受 folio page 参数的函数都会是对整个 compound page 进行操作(如果传入的确实是一个 compound page 的话),这样就不会有任何歧义。从而可以使内核里的内存管理子系统更加清晰;也就是说,如果某个函数被改为只接受 folio page 作为参数的话,很明确,它们不适用于对 tail page 的操作。

当 Wilcox 第一次发布这一组 patch 时,他强调的是这个改动会带来的另一个好处:任何函数,如果它必须要对整个 compound page 操作,但是却传入的只是一个 tail page,都必须将指向 tail page 结构的指针改为指向 head page 的指针。这通常是通过调用这个函数来实现的:

struct page *compound_head(struct page *page);

这个函数相对来说耗时很少,但是在对页面进行一次操作的过程中可能会被调用多次。这使得内核变大了(因为它是一个 inline 函数),并且也变慢了。一个接受 folio page 参数的函数就知道它收到的不会是 tail page,因此不需要调用 compound_head()。这样既节省了时间又节省了内存。

folio 类型本身被定义为一个简单的封装结构:

struct folio {struct page page;
};

根据这个结构,建立起一套新的基础设施。例如,get_folio()和 put_folio() 函数就跟 get_page()和 put_page() 函数一样,用来管理对 folio page 的引用,但不需要再调用 current_head()。此外还有一整套更高级的函数。其实这里真正耗费时间的工作是将各个内核子系统中的 API 转换为使用新的类型;Wilcox 并没有对这项工作的性质进行美化:

这里会是大量的繁重工作,并且都是破坏性的。包括修改每一个文件系统,以及许多设备驱动程序!不过我觉得这个工作是值得的。

到 3 月 5 日发布这组 patch 的第四个版本时,核心 patch 和转换 API 的 patch(Wilcox 没有发布出来)加起来大约有 100 个 commit,对于 review 来说,工作量已经非常大了。

也许是由于 patch 太大了,之前的邮件并没有引起许多讨论。不过针对最新的一版,Andrew Morton 看了一下,感到很担心:

天啊,这些改动真是太繁杂了。我们需要记住更多的东西,今后我们将永远有一个 "page "和 "folio "的混搭,到处都是从一个转换到另一个的代码。不断地增加 folio 访问代码以及操作代码来覆盖现有的 page 访问代码和操作代码,等等。

我不清楚这一切是否真的值得。

Hugh Dickins,也表示对这项工作缺乏兴趣。另一方面,Kirill Shutemov 和 Michal Hocko 都表示支持,至少在概念上支持。Dave Chinner 说,对于文件系统开发者来说,"这种抽象是绝对必要的",尤其是当 page cache 今后能够管理多种不同大小的 compound page 的时候。

所以,换句话说,对于这项工作是否能改善内核,目前核心开发者之间还没有达成共识。随着时间的推移,情况可能会发生变化,因为会有更多的人关注它,它的优势(或缺乏优势)将变得更加明显。但一般来说,在内存管理子系统中,变化往往是缓慢发生的,哪怕不是这么巨大、杂乱的改动也是慢慢进行的。还要注意的是命名的问题,很明显,"folio" 并不受欢迎,尽管目前还没有什么更合适的名字。因此,至少有一个结论是明确的:内核很可能会有类似 folio 这样的东西,但似乎不太可能很快发生。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

LWN:利用page folio来明确内存操作!相关推荐

  1. PostgreSQL Huge Page 使用建议 - 大内存主机、实例注意

    标签 PostgreSQL , Linux , huge page , shared buffer , page table , 虚拟地址 , 物理地址 , 内存地址转换表 背景 当内存很大时,除了刷 ...

  2. java io--内存操作流_JavaIO——内存操作流、打印流

    我们之前所做的都是对文件进行IO处理,实则我们也可以对内存进行IO处理.我们将发生在内存中的IO处理称为内存流. 内存操作流也可分为两类:字节内存流和字符内存流. (1)ByteArrayInputS ...

  3. 利用python对Excel进行读写操作

    最近在写论文做实验的过程中,利用python自带的matplotlib库进行绘画,但是尝尝会出现在程序跑完后发现图片里面有一些小细节没有注意到,导致整个代码重新跑.代码重新跑短则几小时,长则就不好说了 ...

  4. chrome frame节点 取_爬虫3-下(利用Selenium + Chrome Driver模拟用户操作浏览器)

    一.前言 前面利用request的方法爬取页面数据的操作,今天用另外一种方法:利用Selenium + Chrome Driver模拟用户操作浏览器,来爬取数据. 在此之前需要做一些准备工作:安装se ...

  5. 字符操作库函数以及内存操作库函数 C语言实现

    字符操作库函数 strlen strlen判断结束的标志为找到字符串中的'\0',也就是说如果字符串中间出现'\0'将会导致strlen停止 即strlen的返回值是'\0'前所出现的字符个数   然 ...

  6. 【嵌入式开发】ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

    文章目录 一. 内存 简介 1. 两大内存分类 ( 1 ) DRAM 简介 ( 定期刷新 | 速度慢 | 成本低 ) ( 2 ) SRAM 简介 ( 不需刷新 | 存取速度快 | 功耗大 | 成本高 ...

  7. 计算机通过路由器连接打印机共享的打印机,如何利用无线路由器进行打印机共享访问操作...

    如何利用无线路由器进行打印机共享访问操作 随着无线网络的快速发展,其应用的范围更加的广阔.现在不少单位已经开始使用无线网络了,在该无线网络环境中虽然我们也可以非常轻松地进行文件夹共享访问操作,但是要想 ...

  8. C语言处理字符串及内存操作

    字符串处理函数 1.字符串长度 strlen表示包含的字符的个数,size_t strlen(char cosnt *string), 返回的是size_t类型,它是无符号整数类型,在表达式中进行运算 ...

  9. 【C 语言】结构体 ( 指针运算与指针内存操作 | 结构体成员偏移量计算 )

    文章目录 一.指针运算 与 指针内存操作 二.结构体偏移量计算 一.指针运算 与 指针内存操作 指针变量算术运算 ( 指针可以是任意值 ) : 指针 是一个变量 , 如果对指针进行 算术 / 逻辑 等 ...

最新文章

  1. 怎么在线安装php文件,PHP在线安装数据库
  2. Linux下安装rlwrap
  3. Android之自定义Adapter的ListView
  4. 判断三角形java代码_java基础编程题之异常处理
  5. 卷积神经网络(Convolutional Neural Networks,CNNS/ConvNets)
  6. Objective-C setter和getter
  7. 用easyx画五角星_【洛谷日报#195】有个东西叫EasyX
  8. Nginx学习总结(13)——Nginx 重要知识点回顾
  9. 【报告分享】2019移动互联网行业报告暨无监督机器学习下的2019行业价值人群聚类报告.pdf...
  10. 如何保证数据库结构的合理性(三、建立可靠的关系)
  11. PAT之树:一般树、二叉树、完全二叉树、二叉搜索树、二叉平衡树、并查集
  12. 51单片机蓝牙小车程序详解
  13. 电吉他效果器音频处理(1)——失真效果器、超载失真效果器、移相效果器、弗兰格效果器
  14. Tokenized的设计哲学(三)
  15. Fastdb安装与使用
  16. linux xia复制命令,Linux常用命令整理
  17. 什么是java socket
  18. 奥鹏计算机基础计算机病毒是指,奥鹏教育《计算机应用基础》在线考核A卷.doc...
  19. Tkx/tk——瞬态窗口
  20. 推荐几个 Go 圈子的高质量肝帝

热门文章

  1. Excel如何将科学计数法转成文本格式且末位数字不变0
  2. Jest+Enzyme测试React组件(上)
  3. 暗黑起源服务器维护,暗黑起源李连杰版本,暗黑起源李连杰代言手游官网预约 v1.3.1-手游汇...
  4. NPUCTF writeup
  5. 1709 ltsb 内存占用_一次C++伪“内存泄漏”的排查之旅
  6. 【你好,windows】Windows 10_2016_LTSB_14393.1944纯净经典版2021.4.6
  7. 泛微oa明细表添加按钮_泛微OA 新增功能:表格单建模详解.doc
  8. 拯救你的年底 KPI:前端性能优化
  9. java mvvm_MVVM
  10. 实战 | 基于OpenCV的停车场空余车位实时监测系统(详细步骤 + 源码)