「Linux」SWAP 深度解读

「Linux」SWAP 深度解读

概述

本文讨论的 swap基于Linux4.4内核代码 。Linux内存管理是一套非常复杂的系统,而swap只是其中一个很小的处理逻辑。主要是:swap、swappiness及kswapd原理,swap分区优先级的妙用。

解决以下问题:

1、 swap到底是干嘛的?

2、 什么是内存水位标记?

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

1、什么是SWAP,到底是干嘛的?

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

$ 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直接调用内存回收的逻辑进行内存回收;参见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的inactiveanon的activefile的inactivefile的active

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

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

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

首先扫描每个zone上的cgroup组;然后再以cgroup的内存为单元进行page链表的扫描;内核会先扫描anon的active链表,将不频繁的放进inactive链表中,然后扫描inactive链表,将里面活跃的移回active中;

4.进行swap的时候,先对inactive的页进行换出;

5.如果是file的文件映射page页,则判断其是否为脏数据,如果是脏数据就写回,不是脏数据可以直接释放。

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

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

因为针对filebased的内存,没必要进行交换,其数据原本就在硬盘上,回收这部分内存只要在有脏数据时写回,并清空内存就可以了,以后有需要再从对应的文件读回来。

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

2 、什么是内存水位标记?(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时,有剩余内存的情况下也可能发生交换。

swap的相关操纵命令

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

我们使用一个文件的例子来演示一下整个操作过程:

制作swap文件:

启用swap文件:

关闭swap空间:

3、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就是类似这样的进程池架构的程序。

觉得有用的关注下哦~

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Proudly powered by WordPress | Theme: HoneyWaves by SpiceThemes