当前位置: 首页 > 科技观察

图-Linux内存回收之LRU算法

时间:2023-03-15 10:46:52 科技观察

内存是操作系统非常重要的资源。操作系统要运行一个程序,首先要将程序代码段的指令和数据段的变量从硬盘加载到内存中,然后才能使用运行。如下图所示:但是内存资源是有限的,随着系统中运行的进程越来越多,系统中可用的内存会越来越少。那么,当可用内存不足时,Linux内核是如何处理的呢?本文将介绍当可用内存不足时,Linux内核是如何处理的。1、内存不足怎么处理大家想一想。当系统可用内存不足时,进程继续申请内存会怎样?当系统的可用内存不足时,内核会使用内存来保证进程有足够的可用内存。进行回收。内存回收工作主要包括以下步骤:为了加快某些操作(如文件I/O),内核会缓存操作的结果(如文件页缓存),以及缓存使用的内存可以回收。因此,当可用内存不足时,首先回收内核中的缓存。如果回收内核缓存,系统可用内存仍然不足。然后,内核会触发交换机制。交换机制会将一些进程占用的内存交换(写入)到硬盘,然后释放内存,使系统有更多的可用内存。本文将重点介绍交换机制。如果触发swap机制后系统的可用内存不能满足系统要求,就会触发OOM(OutOfMemory)机制。OOM机制会选择一些进程并杀死它们以获得更多可用内存。由于回收内存的方式有3种,本文主要以swap机制为分析对象,介绍当内存不足时内核是如何回收内存的。2.交换机制的原理在分析交换机制的实现之前,我们先介绍一下交换机制的原理。本文使用Linux-2.6.23版本内核。交换这个词的意思是交换。顾名思义,它将某些进程占用的内存交换(写入)到硬盘中,然后将内存释放给操作系统,使操作系统有更多的可用内存。如下图所示:swap机制的本质是将进程占用的内存写入硬盘,然后释放内存。那么,就涉及到哪些进程的内存应该交换到硬盘上了。每个进程都不希望自己占用的内存被交换到硬盘上,因为内存交换到硬盘后,进程要使用内存,必须先从硬盘加载内存到继续使用之前的内存。这个过程的性能会大大降低。出于这个原因,内核必须提供一个最佳解决方案来选择一些内存交换到磁盘,同时对进程性能的影响最小。由于进程的内存空间分为多个段,如代码段、数据段、mmap段、堆段和堆栈段等,那么,哪些内存段会被交换到硬盘上呢?答案是:所有的内存段都可能被交换到硬盘上。但是对于与文件有映射关系的内存区域,比如code段和mmap段,只需要将数据写回文件即可(因为code段的内容不会改变,所以有无需回信)。对于数据段、堆段、栈段中的内存页,由于没有映射文件(称为匿名内存页),内核必须提供一个文件(或硬盘分区)来存放这些内存页的数据.这个文件(或硬盘分区)称为交换分区。从上面的分析可以得出两个重要的信息:匿名内存页:没有映射到任何文件的内存页。交换分区:用于存储匿名内存页面数据的文件或硬盘分区。下面主要介绍当系统内存不足时,内核如何将进程的匿名内存页写入swap分区,并回收这些匿名内存页。1、LRU内存淘汰算法当系统内存不足,触发swap机制时,内核应该选择哪些匿名内存页写入swap分区?如果随机选择一些匿名内存页写入交换分区,可能会出现以下问题问题:进程的匿名内存页写入交换分区后,进程立即访问内存页,然后读取内存页从交换分区进入内存。这样只会增加系统的负载,并不能解决系统内存不足的问题。为了解决这个问题,Linux内核引入了LRU内存淘汰算法。用过Memcached或者Redis的同学应该知道LRU算法。当系统内存不足时,Memcached和Redis都采用LRU算法淘汰内存。LRU(LeastRecentlyUsed)中文翻译是最近最少使用的意思。其原理是:当内存不足时,系统中使用最少的内存将被淘汰,使系统性能损失最小。为了实现LRU算法,内核维护了两个双向链表:active_list和inactive_list。下面分别介绍这两个链表的作用:active_list:活动内存页链表。也就是说,进程会经常访问这个链表中的内存页,所以在进行内存淘汰的时候,不要淘汰这个链表中的内存页。inactive_list:非活动内存页的链表。也就是说,进程很少访问这个链表中的内存页,所以在进行内存淘汰时,主要是淘汰这个链表中的内存页。在Linux内核中,每个内存区(zone)都维护着一个active_list和一个inactive_list。内存区域是内存管理中的一个对象。为了更清楚的描述,我们暂时假设内核中只有一个内存区域,也就是说内核中暂时只维护一个active_list和一个inactive_list。如下图所示:另外,每个内存页都有一个PG_referenced标志,表示该内存页是否被访问过,这个标志在内存回收过程中起着至关重要的作用。当进程申请匿名内存页时,内核会将此内存页添加到活动内存页列表(active_list)中,并将PG_referenced标志置0。如下图所示:一个进程,根据内存页所在的LRU链表进行不同的操作:如果内存页原本在active链表中,那么这个内存页的PG_referenced会被设置为1。如果内存页原本在inactive链表中,且PG_referenced为0,则将内存页的PG_referenced标志位置1。如果内存页原本在inactive链表中,且PG_referenced为1,则内存页page将从inactivelist移动??到activelist,PG_referenced置0。下图为上述情况的流程:当系统内存不足时,需要进行内存淘汰处理。内存页淘汰过程与上述过程正好相反。下面介绍内存页淘汰过程。当内存被淘汰时,只能从inactive链表中淘汰。淘汰过程如下:内存从inactive链表的尾部开始淘汰。如果该内存页的PG_referenced标志位为1,则该内存页将被跳过,并将该内存页的PG_referenced标志位设置为0。如果该内存页的PG_referenced标志位为0,则将该内存页写入swap分区,解除与该内存页的所有映射关系,然后释放该内存页。上述过程由shrink_inactive_list函数完成,如下图所示:另外,active链表中的内存页也有一个收缩过程,收缩过程如下:如果内存的PG_referenced标志位page为1,那么shrinking进程会将内存页的PG_referenced标志设置为0。如果内存页的PG_referenced标志为0,decay进程会将内存页移动到inactivelist。以上过程由shrink_active_list函数完成,如下图所示:2.LRU算法状态流我们最后用一张状态流图来描述LRU算法的过程:3.总结本文主要介绍LRU中使用的LRULinux内核内存回收过程算法原理,在下一篇文章中,我们将介绍Linux内核是如何实现内存回收的,感兴趣的朋友敬请期待。