linux 的swap、swappiness及kswapd原理【转】

本文讨论的 swap基于Linux4.4内核代码 。Linux内存管理是一套很是复杂的系统,而swap只是其中一个很小的处理逻辑。linux

但愿本文能让读者了解Linux对swap的使用大概是什么样子。阅读完本文,应该能够帮你解决如下问题:算法

  1. swap究竟是干吗的?
  2. swappiness究竟是用来调节什么的?
  3. kswapd何时会进行swap操做?
  4. 什么是内存水位标记?
  5. swap分区的优先级(priority)有啥用?

一、什么是SWAP,究竟是干吗的?

咱们通常所说的swap,指的是一个交换分区或文件。在Linux上可使用swapon -s命令查看当前系统上正在使用的交换空间有哪些,以及相关信息:数据库

[zorro@zorrozou-pc0 linux-4.4]$ swapon -s
Filename            Type        Size        Used    Priority
/dev/dm-4           partition    33554428    0        -1

从功能上讲,交换分区主要是在内存不够用的时候,将部份内存上的数据交换到swap空间上,以便让系统不会因内存不够用而致使oom或者更致命的状况出现。apache

因此,当内存使用存在压力,开始触发内存回收的行为时,就可能会使用swap空间。编程

内核对swap的使用其实是跟内存回收行为紧密结合的。那么关于内存回收和swap的关系,咱们须要思考如下几个问题:缓存

  1. 为何要进行内存回收?
  2. 哪些内存可能会被回收呢?
  3. 回收的过程当中何时会进行交换呢?
  4. 具体怎么交换?

下面咱们就从这些问题出发,一个一个进行分析。服务器

为何要进行内存回收?

内核之因此要进行内存回收,主要缘由有两个:架构

  1. 内核须要为任什么时候刻突发到来的内存申请提供足够的内存。因此通常状况下保证有足够的free空间对于内核来讲是必要的。<br>另外,Linux内核使用cache的策略虽然是不用白不用,内核会使用内存中的page cache对部分文件进行缓存,以便提高文件的读写效率。<br> 因此内核有必要设计一个周期性回收内存的机制,以便cache的使用和其余相关内存的使用不至于让系统的剩余内存长期处于不多的状态。app

  2. 当真的有大于空闲内存的申请到来的时候,会触发强制内存回收。 <br>因此,内核在应对这两类回收的需求下,分别实现了两种不一样的机制:less

因此,内核在应对这两类回收的需求下,分别实现了两种不一样的机制:

  • 一个是使用 kswapd进程对内存进行周期检查 ,以保证日常状态下剩余内存尽量够用。
  • 另外一个是 直接内存回收(directpagereclaim) ,就是当内存分配时没有空闲内存能够知足要求时,触发直接内存回收。

这两种内存回收的触发路径不一样:

  • 一个是使用 kswapd进程对内存进行周期检查 ,以保证日常状态下剩余内存尽量够用。
  • 另外一个是 直接内存回收(directpagereclaim) ,就是当内存分配时没有空闲内存能够知足要求时,触发直接内存回收。

这两种内存回收的触发路径不一样:

  • 一个是由内核进程kswapd直接调用内存回收的逻辑进行内存回收;<br> 参见mm/vmscan.c中的kswapd()主逻辑

  • 另外一个是内存申请的时候进入slow path的内存申请逻辑进行回收。<br> 参见内核代码中的mm/page_alloc.c中的__alloc_pages_slowpath方法

这两个方法中实际进行内存回收的过程异曲同工,最终都是 调用shrink_zone() 方法进行针对每一个zone的内存页缩减。

这个方法中会再调用shrink_lruvec()这个方法对每一个组织页的链表进程检查。找到这个线索以后,咱们就能够清晰的看到内存回收操做究竟针对的page有哪些了。

这些链表主要定义在mm/vmscan.c一个enum中:

根据这个enum能够看到,内存回收主要须要进行扫描的链表有以下4个:

  • anon的inactive
  • anon的active
  • file的inactive
  • file的active

就是说,内存回收操做主要针对的就是内存中的文件页(file cache)和匿名页。

关于活跃(active)仍是不活跃(inactive)的判断内核会使用lru算法进行处理并进行标记,咱们这里不详细解释这个过程。

整个扫描的过程分几个循环:

  1. 首先扫描每一个zone上的cgroup组;
  2. 而后再以cgroup的内存为单元进行page链表的扫描;
  3. 内核会先扫描anon的active链表,将不频繁的放进inactive链表中,而后扫描inactive链表,将里面活跃的移回active中;
  4. 进行swap的时候,先对inactive的页进行换出;
  5. 若是是file的文件映射page页,则判断其是否为脏数据,若是是脏数据就写回,不是脏数据能够直接释放。

这样看来, 内存回收这个行为会对两种内存的使用进行回收:

  • 一种是anon的匿名页内存,主要回收手段是swap;
  • 另外一种是file-backed的文件映射页,主要的释放手段是写回和清空。

由于针对filebased的内存,不必进行交换,其数据本来就在硬盘上,回收这部份内存只要在有脏数据时写回,并清空内存就能够了,之后有须要再从对应的文件读回来。

内存对匿名页和文件缓存一共用了 四条链表 进行组织,回收过程主要是针对这四条链表进行扫描和操做。

二、swappiness究竟是用来调节什么的?

不少人应该都知道 /proc/sys/vm/swappiness 这个文件,是个能够用来调整跟swap相关的参数。这个文件的默认值是60,能够的取值范围是0-100。

这很容易给你们一个暗示:我是个百分比哦!

那么这个文件具体到底表明什么意思呢?咱们先来看一下说明:

======

swappiness

This control is used to define how aggressive the kernel will swap memory pages. Higher values will increase agressiveness, lower values decrease the amount of swap.

A value of 0 instructs the kernel not to initiate swap until the amount of free and file-backed pages is less than the high water mark in a zone.

The default value is 60.

======

这个文件的值用来定义内核使用swap的积极程度:

  • 值越高,内核就会越积极的使用swap;
  • 值越低,就会下降对swap的使用积极性。
  • 若是这个值为0,那么内存在free和file-backed使用的页面总量小于高水位标记(high water mark)以前,不会发生交换。

在这里咱们能够理解file-backed这个词的含义了,实际上就是上文所说的文件映射页的大小。

那么这个swappiness到底起到了什么做用呢?

咱们换个思路考虑这个事情。假设让咱们设计一个内存回收机制,要去考虑将一部份内存写到swap分区上,将一部分file-backed的内存写回并清空,剩余部份内存出来,咱们将怎么设计?

我想应该主要考虑这样几个问题:

  1. 若是回收内存能够有两种途径(匿名页交换和file缓存清空),那么我应该考虑在本次回收的时候,什么状况下多进行file写回,什么状况下应该多进行swap交换。说白了就是平衡两种回收手段的使用,以达到最优。
  2. 若是符合交换条件的内存较长,是否是能够不用所有交换出去?好比能够交换的内存有100M,可是目前只须要50M内存,实际只要交换50M就能够了,不用把能交换的都交换出去。

分析代码会发现,Linux内核对这部分逻辑的实现代码在 get_scan_count() 这个方法中,这个方法被 shrink_lruvec() 调用。

get_sacn_count()就是处理上述逻辑的,swappiness是它所须要的一个参数,这个参数其实是指导内核在清空内存的时候,是更倾向于清空file-backed内存仍是更倾向于进行匿名页的交换的。

固然,这只是个倾向性,是指在两个都够用的状况下,更愿意用哪一个,若是不够用了,那么该交换仍是要交换。

简单看一下get_sacn_count()函数的处理部分代码,其中关于swappiness的第一个处理是:

这里注释的很清楚:

  1. 若是swappiness设置为100,那么匿名页和文件将用一样的优先级进行回收。

<br> 很明显,使用清空文件的方式将有利于减轻内存回收时可能形成的IO压力。 <br> 由于若是file-backed中的数据不是脏数据的话,那么能够不用写回,这样就没有IO发生,而一旦进行交换,就必定会形成IO。 <br> 因此系统默认将swappiness的值设置为60,这样回收内存时,对file-backed的文件cache内存的清空比例会更大,内核将会更倾向于进行缓存清空而不是交换。

  1. 这里的swappiness值若是是60,那么是否是说内核回收的时候,会按照60:140的比例去作相应的swap和清空file-backed的空间呢?并非。

<br> 在作这个比例计算的时候,内核还要参考当前内存使用的其余信息。对这里具体是怎么处理感兴趣的人,能够本身详细看get_sacn_count()的实现,本文就很少解释了。 <br> 咱们在此要明确的概念是: swappiness的值是用来控制内存回收时,回收的匿名页更多一些仍是回收的file cache更多一些 。

  1. swappiness设置为0的话,是否是内核就根本不会进行swap了呢?这个答案也是否认的。

<br> 首先是内存真的不够用的时候,该swap的话仍是要swap。 <br> 其次在内核中还有一个逻辑会致使直接使用swap, 内核代码 是这样处理的:

三、kswapd何时会进行swap操做?

咱们回到kswapd周期检查和直接内存回收的两种内存回收机制。

直接内存回收比较好理解,当申请的内存大于剩余内存的时候,就会触发直接回收。

那么kswapd进程在周期检查的时候触发回收的条件是什么呢?

仍是从设计角度来看,kswapd进程要周期对内存进行检测,达到必定阈值的时候开始进行内存回收。

这个所谓的阈值能够理解为内存目前的使用压力,就是说,虽然咱们还有剩余内存,可是当剩余内存比较小的时候,就是内存压力较大的时候,就应该开始试图回收些内存了,这样才能保证系统尽量的有足够的内存给突发的内存申请所使用。

四、什么是内存水位标记?(watermark)

那么如何描述内存使用的压力呢?

Linux内核使用水位标记(watermark)的概念来描述这个压力状况。

  • Linux为内存的使用设置了三种内存水位标记:high、low、min。他们 所标记的含义分别为:

  • 剩余内存在high以上表示内存剩余较多,目前内存使用压力不大;

  • high-low的范围表示目前剩余内存存在必定压力;

  • low-min表示内存开始有较大使用压力,剩余内存很少了;

  • min是最小的水位标记,当剩余内存达到这个状态时,就说明内存面临很大压力。

  • 小于min这部份内存,内核是保留给特定状况下使用的,通常不会分配。

内存回收行为就是基于剩余内存的水位标记进行决策的:

当系统剩余内存低于watermark[low]的时候,内核的kswapd开始起做用,进行内存回收。直到剩余内存达到watermark[high]的时候中止。

若是内存消耗致使剩余内存达到了或超过了watermark[min]时,就会触发直接回收(direct reclaim)。

明白了水位标记的概念以后,zonefile + zonefree <= high_wmark_pages(zone)这个公式就能理解了。

这里的zonefile至关于内存中文件映射的总量,zonefree至关于剩余内存的总量。

内核通常认为,若是zonefile还有的话,就能够尽可能经过清空文件缓存得到部份内存,而没必要只使用swap方式对anon的内存进行交换。

整个判断的概念是说,在全局回收的状态下(有global_reclaim(sc)标记),若是当前的文件映射内存总量+剩余内存总量的值评估小于等于watermark[high]标记的时候,就能够进行直接swap了。

这样是为了防止进入cache陷阱,具体描述能够见代码注释。

这个判断对系统的影响是, swappiness设置为0时,有剩余内存的状况下也可能发生交换。

那么watermark相关值是如何计算的呢?

全部的内存watermark标记都是根据当前内存总大小和一个可调参数进行运算得来的,这个参数是: /proc/sys/vm/min_free_kbytes

  • 首先这个参数自己决定了系统中每一个zone的watermark[min]的值大小。
  • 而后内核根据min的大小并参考每一个zone的内存大小分别算出每一个zone的low水位和high水位值。

想了解具体逻辑能够参见源代码目录下的该文件:

mm/page_alloc.c

在系统中能够从/proc/zoneinfo文件中查看当前系统的相关的信息和使用状况。

咱们会发现以上内存管理的相关逻辑都是以zone为单位的,这里zone的含义是指内存的分区管理。

Linux将内存分红多个区,主要有:

  • 直接访问区(DMA)
  • 通常区(Normal)
  • 高端内存区(HighMemory)

内核对内存不一样区域的访问由于硬件结构因素会有寻址和效率上的差异。若是在NUMA架构上,不一样CPU所管理的内存也是不一样的zone。

相关参数设置

  • zone_reclaim_mode:

zone_reclaim_mode模式是在2.6版本后期开始加入内核的一种模式,能够用来管理当一个内存区域(zone)内部的内存耗尽时,是从其内部进行内存回收仍是能够从其余zone进行回收的选项,咱们能够经过 /proc/sys/vm/zone_reclaim_mode 文件对这个参数进行调整。

在申请内存时(内核的get_page_from_freelist()方法中),内核在当前zone内没有足够内存可用的状况下,会根据zone_reclaim_mode的设置来决策是从下一个zone找空闲内存仍是在zone内部进行回收。这个值为0时表示能够从下一个zone找可用内存,非0表示在本地回收。

这个文件能够设置的值及其含义以下:

  1. echo 0 > /proc/sys/vm/zone_reclaim_mode:意味着关闭zone_reclaim模式,能够从其余zone或NUMA节点回收内存。
  2. echo 1 > /proc/sys/vm/zone_reclaim_mode:表示打开zone_reclaim模式,这样内存回收只会发生在本地节点内。
  3. echo 2 > /proc/sys/vm/zone_reclaim_mode:在本地回收内存时,能够将cache中的脏数据写回硬盘,以回收内存。
  4. echo 4 > /proc/sys/vm/zone_reclaim_mode:能够用swap方式回收内存。

不一样的参数配置会在NUMA环境中对其余内存节点的内存使用产生不一样的影响,你们能够根据本身的状况进行设置以优化你的应用。

默认状况下,zone_reclaim模式是关闭的。这在不少应用场景下能够提升效率,好比文件服务器,或者依赖内存中cache比较多的应用场景。

这样的场景对内存cache速度的依赖要高于进程进程自己对内存速度的依赖,因此咱们宁肯让内存从其余zone申请使用,也不肯意清本地cache。

若是肯定应用场景是内存需求大于缓存,并且尽可能要避免内存访问跨越NUMA节点形成的性能降低的话,则能够打开zone_reclaim模式。

此时页分配器会优先回收容易回收的可回收内存(主要是当前不用的page cache页),而后再回收其余内存。

打开本地回收模式的写回可能会引起其余内存节点上的大量的脏数据写回处理。若是一个内存zone已经满了,那么脏数据的写回也会致使进程处理速度收到影响,产生处理瓶颈。

这会下降某个内存节点相关的进程的性能,由于进程再也不可以使用其余节点上的内存。可是会增长节点之间的隔离性,其余节点的相关进程运行将不会由于另外一个节点上的内存回收致使性能降低。

除非针对本地节点的内存限制策略或者cpuset配置有变化,对swap的限制会有效约束交换只发生在本地内存节点所管理的区域上。

  • min_unmapped_ratio:

这个参数只在NUMA架构的内核上生效。这个值表示NUMA上每一个内存区域的pages总数的百分比。

在zone_reclaim_mode模式下,只有当相关区域的内存使用达到这个百分比,才会发生区域内存回收。

在zone_reclaim_mode设置为4的时候,内核会比较全部的file-backed和匿名映射页,包括swapcache占用的页以及tmpfs文件的总内存使用是否超过这个百分比。

其余设置的状况下,只比较基于通常文件的未映射页,不考虑其余相关页。

  • page-cluster:

page-cluster是用来控制从swap空间换入数据的时候,一次连续读取的页数,这至关于对交换空间的预读。这里的连续是指在swap空间上的连续,而不是在内存地址上的连续。

由于swap空间通常是在硬盘上,对硬盘设备的连续读取将减小磁头的寻址,提升读取效率。

这个文件中设置的值是2的指数。就是说,若是设置为0,预读的swap页数是2的0次方,等于1页。若是设置为3,就是2的3次方,等于8页。

同时,设置为0也意味着关闭预读功能。文件默认值为3。咱们能够根据咱们的系统负载状态来设置预读的页数大小。

swap的相关操纵命令

可使用mkswap将一个分区或者文件建立成swap空间。swapon能够查看当前的swap空间和启用一个swap分区或者文件。swapoff能够关闭swap空间。

咱们使用一个文件的例子来演示一下整个操做过程

制做swap文件:

启用swap文件:

关闭swap空间:

五、swap分区的优先级(priority)有啥用?

在使用多个swap分区或者文件的时候,还有一个优先级的概念(Priority)。

在swapon的时候,咱们可使用-p参数指定相关swap空间的优先级, 值越大优先级越高 ,能够指定的数字范围是-1到32767。

内核在使用swap空间的时候老是先使用优先级高的空间,后使用优先级低的。

固然若是把多个swap空间的优先级设置成同样的,那么两个swap空间将会以轮询方式并行进行使用。

若是两个swap放在两个不一样的硬盘上,相同的优先级能够起到相似RAID0的效果,增大swap的读写效率。

另外,编程时使用mlock()也能够将指定的内存标记为不会换出,具体帮助能够参考man 2 mlock。

最后 关于swap的使用建议,针对不一样负载状态的系统是不同的。有时咱们但愿swap大一些,能够在内存不够用的时候不至于触发oom-killer致使某些关键进程被杀掉,好比数据库业务。

也有时候咱们但愿不要swap,由于当大量进程爆发增加致使内存爆掉以后,会由于swap致使IO跑死,整个系统都卡住,没法登陆,没法处理。

这时候咱们就但愿不要swap,即便出现oom-killer也形成不了太大影响,可是不能容许服务器由于IO卡死像多米诺骨牌同样所有死机,并且没法登录。跑cpu运算的无状态的apache就是相似这样的进程池架构的程序。

因此:

  • swap到底怎么用?

  • 要仍是不要?

  • 设置大仍是小?

  • 相关参数应该如何配置?

是要根据咱们本身的生产环境的状况而定的。

阅读完本文后但愿你们能够明白一些swap的深层次知识。

Q&A:

  1. 一个内存剩余还比较大的系统中,是否有可能使用swap?

A: 有可能,若是运行中的某个阶段出发了这个条件”zonefile+zonefree<=high_wmark_pages(zone) “,就可能会swap。

  1. swappiness设置为0就至关于关闭swap么?

A: 不是的,关闭swap要使用swapoff命令。swappiness只是在内存发生回收操做的时候用来平衡cache回收和swap交换的一个参数,调整为0意味着,尽可能经过清缓存来回收内存。

  1. A: swappiness设置为100表明系统会尽可能少用剩余内存而多使用swap么?

不是的,这个值设置为100表示内存发生回收时,从cache回收内存和swap交换的优先级同样。就是说,若是目前需求100M内存,那么较大机率会从cache中清除50M内存,再将匿名页换出50M,把回收到的内存给应用程序使用。可是这还要看cache中是否能有空间,以及swap是否能够交换50m。内核只是试图对它们平衡一些而已。

  1. kswapd进程何时开始内存回收?

A: kswapd根据内存水位标记决定是否开始回收内存,若是标记达到low就开始回收,回收到剩余内存达到high标记为止。

  1. 如何查看当前系统的内存水位标记?

A: cat /proc/zoneinfo。

相关文章
相关标签/搜索