本文讨论的 swap基于Linux4.4内核代码 。Linux内存管理是一套非常复杂的系统,而swap只是其中一个很小的处理逻辑。
希望本文能让读者了解Linux对swap的使用大概是什么样子。阅读完本文,应该可以帮你解决以下问题:
- swap到底是干嘛的?
- swappiness到底是用来调节什么的?
- kswapd什么时候会进行swap操作?
- 什么是内存水位标记?
- swap分区的优先级(priority)有啥用?
1、什么是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或者更致命的情况出现。
所以,当内存使用存在压力,开始触发内存回收的行为时,就可能会使用swap空间。
内核对swap的使用实际上是跟内存回收行为紧密结合的。那么关于内存回收和swap的关系,我们需要思考以下几个问题:
- 为什么要进行内存回收?
- 哪些内存可能会被回收呢?
- 回收的过程中什么时候会进行交换呢?
- 具体怎么交换?
下面我们就从这些问题出发,一个一个进行分析。
为什么要进行内存回收?
内核之所以要进行内存回收,主要原因有两个:
内核需要为任何时刻突发到来的内存申请提供足够的内存。所以一般情况下保证有足够的free空间对于内核来说是必要的。
另外,Linux内核使用cache的策略虽然是不用白不用,内核会使用内存中的page cache对部分文件进行缓存,以便提升文件的读写效率。
所以内核有必要设计一个周期性回收内存的机制,以便cache的使用和其他相关内存的使用不至于让系统的剩余内存长期处于很少的状态。当真的有大于空闲内存的申请到来的时候,会触发强制内存回收。
所以,内核在应对这两类回收的需求下,分别实现了两种不同的机制:
所以,内核在应对这两类回收的需求下,分别实现了两种不同的机制:
- 一个是使用 kswapd进程对内存进行周期检查 ,以保证平常状态下剩余内存尽可能够用。
- 另一个是 直接内存回收(directpagereclaim) ,就是当内存分配时没有空闲内存可以满足要求时,触发直接内存回收。
这两种内存回收的触发路径不同:
- 一个是使用 kswapd进程对内存进行周期检查 ,以保证平常状态下剩余内存尽可能够用。
- 另一个是 直接内存回收(directpagereclaim) ,就是当内存分配时没有空闲内存可以满足要求时,触发直接内存回收。
这两种内存回收的触发路径不同:
一个是由内核进程kswapd直接调用内存回收的逻辑进行内存回收;
参见mm/vmscan.c中的kswapd()主逻辑另一个是内存申请的时候进入slow path的内存申请逻辑进行回收。
参见内核代码中的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算法进行处理并进行标记,我们这里不详细解释这个过程。
整个扫描的过程分几个循环:
- 首先扫描每个zone上的cgroup组;
- 然后再以cgroup的内存为单元进行page链表的扫描;
- 内核会先扫描anon的active链表,将不频繁的放进inactive链表中,然后扫描inactive链表,将里面活跃的移回active中;
- 进行swap的时候,先对inactive的页进行换出;
- 如果是file的文件映射page页,则判断其是否为脏数据,如果是脏数据就写回,不是脏数据可以直接释放。
这样看来, 内存回收这个行为会对两种内存的使用进行回收:
- 一种是anon的匿名页内存,主要回收手段是swap;
- 另一种是file-backed的文件映射页,主要的释放手段是写回和清空。
因为针对filebased的内存,没必要进行交换,其数据原本就在硬盘上,回收这部分内存只要在有脏数据时写回,并清空内存就可以了,以后有需要再从对应的文件读回来。
内存对匿名页和文件缓存一共用了 四条链表 进行组织,回收过程主要是针对这四条链表进行扫描和操作。
2、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的内存写回并清空,剩余部分内存出来,我们将怎么设计?
我想应该主要考虑这样几个问题:
- 如果回收内存可以有两种途径(匿名页交换和file缓存清空),那么我应该考虑在本次回收的时候,什么情况下多进行fi