哈喽,我是子牙,一个很卷的硬核男人
深入研究计算机底层、Windows内核、Linux内核、Hotspot源码……聚焦做那些大家想学没地方学的课程。为了保证课程质量及教学效果,一年磨一剑,三年先后做了这些课程:手写JVM、手写OS、带你用纯汇编写OS、手写64位多核OS、实战Linux内核…
这篇文章给大家分享Linux内核内存模块一个很重要的知识点:水位线,就是这玩意
这玩意干啥的呢?控制内存回收的。这玩意是理解Linux内核物理内存管理模块一个非常重要的知识点,还有一个就是lowmem_reserve,这个后面写文章详谈
关于水位线,能搜到的资料还是蛮多的,有的讲得蛮好的,但是我没有看到一篇文章是单步调试Linux内核给出证明的,如所有讲底层的文章还是一样,只是讲原理。所以我准备取其精华去其糟粕,结合自己的理解,再结合我能做到的单步调试Linux内核,写一篇通俗易懂、有理有据的文章。如果你觉得有收获,用你的小手点下分享就是对我最大的支持,创作不易……
我会分享这些:
-
水位线是个什么东东,机制是怎样的?
-
Linux内核中有几条水位线,水位线的值是如何算出来的
-
Linux内核是如何基于水位线控制内存回收线程的
-
NUMA架构中,水位线是如何工作的
-
如果证明你说的都是对的
-
我想根据业务需求调整内存,怎么做
关注公众号【硬核子牙】,我写的硬核文章,关于操作系统、Linux内核、Java虚拟机…
本文是基于64位Linux内核写的,以下,enjoy
01 水位线watermark
如果你是做Java、Python、PHP的,你肯定听过垃圾收集器(GC),你写程序时不用考虑内存的申请与回收,都是由虚拟机帮你完成的。那虚拟机如何知道何时回收呢?有个阈值对吧,内存使用达到那个阈值就启动回收线程进行回收。这里说的水位线就相当于是这个阈值
那Linux内核中有几条水位线呢?三条。对应的阈值就是三个:
这三条水位线与内存回收线程之间是如何协同工作的呢?如图
从图中我们可以得出:
-
如果内存容量在high水位线之上,线程kswapd进入sleep
-
随着内存不断被使用,如果该zone中的可用内存,即managed_pages,低于low水位线,线程kswapd就会激活,进行异步内存回收
-
如果该zone中的可用内存,低于min水位线,这时候就会进入同步内存回收,即在__alloc_pages_slowpath函数中阻塞分配内存并尝试直接内存回收(reclaim)、内存压缩(compact)、更甚者OOM Killer强制回收,直到tatol size大于high水位线,回收才会停止
虽然从图中能得出这些信息,但是我猜这些问题是你想问的。比如:
-
什么是zone
-
kswapd与zone之间的关系
-
水位线watermark与zone之间的关系
接着走……
02 Linux内核内存结构
有木有听过SMP、UMA、NUMA?不了解的话可以看下我之前写的相关文章 传送门
Linux内核是基于NUMA架构设计的,SMP/UMA架构被视为一个NUMA节点,如图
每个NUMA节点,在Linux内核中对应一个pglist_data对象。将每个NUMA节点包含的内存划分为多个zone进行管理,在Linux内核中的存在形式就是zone对象,关于zone是如何划分的,上面提到的文章里有讲到
水位线是与zone相关联的,而回收线程是与pglist_data相关联的,即一个NUMA节点只有一个内存回收线程kswapd。其实在内存紧张的时候,除了内存回收,还有内存压缩,对应的线程是kcompactd,这个后面有机会再聊
那水位线与zone之间的关系是怎样的呢?如图
到这里我们已经知道了:
-
Linux内核是基于NUMA架构进行设计的,SMP/UMA架构在Linux内核中被视为只有一个节点的NUMA架构,用pglist_data对象来表示,内置一个内存回收线程kswapd
-
Linux内核将一个NUMA节点中的内存分成几个ZONE进行管理,如果是64位内核,则包含这三个ZONE:DMA、DMA32、NORMAL
-
每个ZONE都有三条水位线:high、low、min,当每个zone的可用内存,即managed_pages,越过这三条水位线,分别会触发不同的内存回收机制
接下来咱们就来单步调试Linux内核,看三条水位线的阈值是如何算出来的
03 单步调试Linux内核论证
high与low是在min的基础上算出来的,所以我们先来看min是怎么算出来的
网上的文章大部分都说到它们三个之间的关系是这样的,其实是错的
我怎么证明他们说的是错的呢?我单步调试内核看结果的
min的计算公式是这样的
min = 当前NUMA节点要保留的最少page数 * 当前zone的总page数 / 当前NUMA节点的总page数
你是不是想问:公式为什么是这样的?那你得去问写这个Linux内核源码的coder了
当前NUMA节点的总page数是怎么算出来的呢?是每个zone的managed_pages - high_pages相加得到的,如图
得到当前NUMA节点的总page数=1259658pages
当前NUMA节点要保留的最少page数怎么算出来的呢?
将计算得到的new_min_free_kbytes与用户想优化内存设置的user_min_free_kbytes进行比较,直接看代码吧
经过一系列运算,得到当前NUMA节点要保留的最少page数=8978kbytes=2244pages
计算min的三个数据都有了,就可以计算min的值了,比如DMA区域的min的值
2244 * 3977 / 1259658 = 7
再来看high、low的值是如何算出来的
mult_frac的运算公式是
tmp就是在7乘以2与Result之间取最大值,结果是3。所以low的结果是10,high的结果是13
watermark_scale_factor的值是多少呢?
我们学习一个东西是为了用对吧!接下来看看,我们学会了水位线机制,那我们能用它来做什么呢?Linux内核内存调优!接下来讲讲如何做内存调优
04 Linux内核内存优化
从上面的讲解中可以看出,Linux内核提供了两个参数让我们可以调整内存:
-
user_min_free_kbytes
-
watermark_scale_factor
设置user_min_free_kbytes会影响三条水位线,设置watermark_scale_factor只会影响两条水位线:low、high。
user_min_free_kbytes更多地关注于保证系统在内存紧张时的稳定性和关键操作的可行性。所以内存密集型应用,可以调大user_min_free_kbytes,以减少同步回收内存的巨大消耗
而watermark_scale_factor则提供了一种机制,根据系统当前的内存状况动态调整内存回收的敏感度,以平衡性能和资源利用。一般不太会调
如何设置这两个参数呢:
这样设置会立即生效,对应的处理逻辑
至此,关于水位线的all就讲完了。你学会(废)了吗?如果觉得还不错,可以帮忙分享一下吗?
手写x64多核操作系统
我的《实战Linux内核》二期今晚开班,早鸟价最后一天4299,开班后恢复原价5000。如果你也想像我这样玩转Linux内核,可以咨询班主任,领取免费试看视频,了解一下我的讲课风格与技术实力,再做打算
课程共十二大专题,三个月时间,带你手写x86单核os、手写x64多核os打底,再带你以手写Linux驱动的方式实战Linux内核。只有这样学,才能真正学会Linux内核,才能做实验,而不是只是停留在原理层面,无法论证…
学完以后,你可以
为了保证课程质量,课程所有内容由子牙老师亲授!Linux内核中,少部分代码是体系相关的,主要受CPU架构的影响,绝大多数代码是体系无关的。这套课程是基于x86_64架构,你如果是ARM、MIPS、RISCV架构,也可以学,技多不压身!