Linux内存管理:Swap介绍以及如何使交换具有可扩展性

目录

一、swap分区

1. swap分区的创建

2. swap的数据结构

二、swap out

1. 回收流程

2. swap slot cache

3. ssd查找算法

三、swap in

1. 换入流程

2. swapin_readahead

四、总结

五、使交换具有可扩展性

参考材料


 

由于内存和磁盘的读写性能差异较大,Linux会在内存充裕时将空闲内存用于缓存磁盘数据,以提高I/O性能。相对的在内存紧张时Linux会将这些缓存回收,将脏页回写到磁盘中。而在进程的地址空间中,如heap,stack等匿名页,在磁盘上并没有对应的文件,但同样有回收到磁盘上以释放出空闲内存的需求。swap机制通过在磁盘上开辟专用的swap分区作为匿名页的backing storage,满足了这一需求。本文简要的对Linux中的swap机制进行了介绍。

 

一、swap分区


1. swap分区的创建


Linux中存在两种形式的swap分区:swap disk和swap file。前者是一个专用于做swap的块设备,作为裸设备提供给swap机制操作;后者则是存放在文件系统上的一个特定文件,其实现依赖于不同的文件系统,会有所区别。

图1 两种swap分区

通过mkswap命令可以将一个swap disk或swap file转换为swap分区的格式。随后可通过swapon和swapoff命令开启或关闭对应的swap分区。通过cat /proc/swaps或swapon -s可以查看使用中的swap分区的状态。

图2 swap分区的创建及使用

 

2. swap的数据结构


内核中使用swap_info_struct结构体来管理swap分区,其关键成员如下:

一个swap_info_struct对应一个swap分区。如下图所示,swap分区内部会以page大小为单位划分出多个swap slot,同时通过swap_map对每个slot的使用情况进行记录,为0代表空闲,大于0则代表该slot被map的进程数量。

图3 swap info

发生内存回收时,一个内存页会被回收到一个slot中,同时会修改pte内容为slot对应的swp_entry_t。swp_entry_t是一个64位的变量,其结构如下图所示,其中2-7位存放swap分区的type,8-57位存放slot在分区内的offset。内存换入时,通过pte的内容即可在磁盘上找到对应的slot。

 

二、swap out


1. 回收流程


内核中的内存回收流程,最终都会走到shrink_page_list中,该函数对page_list链表中的内存依次处理,回收满足条件的内存。匿名页回收如下图所示,其回收需要经过两次shrink。

图5 swap out 流程

 

第一次shrink时,内存页会通过add_to_swap分配到对应的swap slot,设置为脏页并进行回写,最后将该page加入到swapcache中,但不进行回收。

第二次shrink时,若脏页已经回写完成,则将该page从swapcache中删除并回收。

 

2. swap slot cache


为了提高swap slot的分配速度,内核引入了swap slot cache机制。该机制定义了一个per_cpu的buffer,包含alloc和free两部分cache,分别用于分配和释放。

图6 swap slot cache

 

3. ssd查找算法


为了提高swap机制在ssd设备上的性能,内核引入了swap_cluster_info结构体。如下图所示,每SWAPFILE_CLUSTER个连续的slot会组合成一个cluster,内核在做内存换出时,会以cluster为单位查找空闲的slot,而不再遍历swap_map,从而降低了锁竞争,也保证了ssd设备上的磨损均衡。

图7 swap_cluster_info

 

三、swap in


1. 换入流程


当换出的内存产生缺页异常时,会通过do_swap_page查找到磁盘上的slot,并将数据读回,其流程如下图所示。

图8 swap in 流程

 

2. swapin_readahead


类似于IO预读机制,swap in的流程中也实现了预读。由于SSD的随机读写能力要强于HDD,在基于物理地址的预读机制之外,内核还引入了基于VMA的预读。两种预读方式可以通过/sys/kernel/mm/swap/vma_ra_enabled节点进行选择。

图9 swapin_readahead

 

四、总结


本文简要介绍了Linux中的swap流程及一些优化机制。随着SSD设备的快速发展以及新型存储设备的逐步应用,swap机制的性能得到了很大改善,并带来了一些新的优化方向。

 

五、使交换具有可扩展性

https://lwn.net/Articles/704478/


交换子系统是匿名页面(其中包含程序数据未由文件系统中的文件支持的页面)在内存压力迫使它们离开RAM的地方。一种广泛持有的观点认为,交换几乎总是坏消息;交换意见几乎总是坏消息。到Linux系统到交换匿名页面的地步时,性能之争已经失败了。因此,看到Linux系统根本没有交换空间的情况并不少见。相对较差的交换性能是这种态度的原因还是结果,尚有待商matter。不过,变得越来越清楚的是,使用交换的理由越来越强,因此有必要使交换更快。

随着存储设备(尤其是固态存储设备(SSD))的性能提高,交换变得越来越有吸引力。不久之前,将页面移入或移出存储设备是一项极其缓慢的操作,比直接访问内存要花费几个数量级的时间。持久内存设备的出现改变了这一比例,以至于存储速度接近主内存速度。同时,云计算的增长使提供商更有动力过度使用其系统上的主内存。如果交换可以足够快地进行,那么过量使用内存的性能损失将变得微不足道,从而可以更好地利用整个系统。

正如Tim Chen在最近发布的补丁集中指出的那样,内核当前对必须从swap检索页面的页面错误施加了相当大的开销。补丁集通过几种方式增加交换子系统的可伸缩性来解决该问题。

在当前内核中,交换设备(文件系统中的专用分区或特殊文件)由swap_info_struct 结构表示。该结构的许多字段中有swap_map,它是指向字节数组的指针,其中每个字节都包含存储在交换设备上的页面的引用计数。结构看起来像这样:

 

一些交换代码已经很旧了。相当数量的历史可以追溯到Git时代的开始。在早期,内核会尝试将交换文件的使用集中在设备的开头,即上面所示的swap_map数组的左端。当人们转向轮换存储时,这种方法很有意义。将数据保存在交换设备中应该可以最大程度地减少访问数据所需的查找量。由于以下两个原因,它在固态设备上的效果不太好:(1)在此类设备上没有寻道延迟;(2)通过在整个设备上分配流量可以更好地满足SSD的磨损均衡要求。

为了在SSD上实现更好的性能,在2013年针对3.12版本更改了交换代码。当交换子系统知道它正在使用SSD时,它将设备划分为群集,如下所示:

所述percpu_cluster指针指向为系统上的每个CPU不同的群集。通过这种安排,每个CPU都可以从交换设备在其自己的群集中分配页面,结果是这些分配分布在整个设备中。从理论上讲,这种方法还具有更大的可扩展性,只是在当前内核中,尚未实现许多可扩展性。

通常情况下,此问题与锁定有关。CPU没有对任何给定集群(甚至由percpu_cluster指示的集群)的独占访问权,因此它们必须先获取swap_info_struct结构中的锁 spinlock,然后才能进行任何更改。在任何给定的系统上,交换设备通常不多-通常只有一个-因此,当交换很重时,自旋锁就会受到激烈竞争。

自旋锁争用不是实现高可伸缩性的途径。在这种情况下,该争用甚至没有必要。每个群集都是独立的,可以在不接触其他群集的情况下进行分配,因此没有真正的需要等待单个全局锁的等待。因此,补丁程序集中的首要任务是向cluster_info数组中的每个条目添加一个新锁;一位锁用于最大程度地减少增加的内存消耗。现在,任何给定的CPU都可以从其集群中分配页面(或将空闲页面分配到其集群中)而无需与其他CPU竞争。

即使这样,获取锁也会产生开销,并且在访问其他CPU群集中的锁时也可能存在高速缓存行争用(这在释放页面时经常会发生,因为没有什么迫使它们方便地处于释放CPU的当前范围内)簇)。为了最大程度地降低成本,补丁集添加了新的接口以批量分配和释放交换页面。一旦CPU分配了一批交换页,它就可以使用它们而无需获取本地集群锁。释放的交换页将累积在单独的缓存中,并分批返回。有趣的是,释放的页面不会被释放的CPU所重用,希望将它们全部释放将有助于最小化交换空间的碎片。

还有另外一个争论点需要解决。除了swap_info_struct结构外,交换子系统还为每个交换设备维护一个 address_space结构。此结构包含内存中的页面与其交换设备上相应的后备存储之间的映射。在交换分配的改变需要更新的基数树在 address_space结构,而基数树被另一个锁保护。再一次,由于系统中通常只有一个交换设备,因此这是所有CPU都争用的另一全局锁。

这种情况下的解决方案是集群方法的一种变体。该 address_space结构被复制到许多结构,一个用于交换空间每个64MB。如果交换区域的大小为(例如)10GB,则单个address_space将被拆分为160种方式,每种方式都有其自己的锁。显然,这减少了任何单个锁的争用范围。该补丁还注意确保交换群集的初始分配将每个CPU置于单独的address_space中,从而确保一开始就没有争用(尽管一旦系统运行了一段时间,交换模式将变为有效随机)。

根据Chen的说法,当前的内核会为每个页面错误增加大约15µs的开销,而这是通过读取固态交换设备来满足的。他说,这相当于从设备实际读取数据所花费的时间。应用补丁后,该开销降至4µs,这是一个显着的改进。在撰写本文时,对补丁集还没有明确的评论,但是似乎交换子系统需要与现代存储设备良好配合的一种改进。

 

参考材料


[1] https://www.kernel.org/doc/gorman/html/understand/understand014.html

[2] http://sopa.dis.ulpgc.es/ii-dso/leclinux/mm/swap/

[3] http://jake.dothome.co.kr/swap-1/

[4] https://lwn.net/Articles/704478/

[5] https://lwn.net/Articles/704478/

[6] https://blog.csdn.net/qkhhyga2016/article/details/88722458

 

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页