内存回收

本文深入探讨了Linux系统中的内存管理与回收机制,包括直接内存回收、内存规整、周期内存回收、OOMkiller以及swap交换分区等核心概念,解析了内存回收的时机与反向映射(RMAP)的作用,阐述了LRU链表在内存回收中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

内存回收是内存管理最为复杂的机制,本文主要在广义上介绍回收方法,而不拘泥于细节。随着系统的运行,内存会逐渐被消耗,而内存主要占用的部分有两个,分别是高速缓存以及用户进程,内核高速缓存包括磁盘高速缓存,slab缓存等。

首先介绍内核中内存回收的几种方式:

  • 直接内存回收
    当我们调用内存分配函数,比如alloc_pages时,发现此时对应zone管理区中的watermark低于min值,就会触发内存紧缺回收,如果该内存分配的上下文允许休眠,那么会进行休眠,并执行内存回收操作,同时触发周期回收。
  • 内存规整
    内存规整是要先于直接内存回收的,如果内存不足,会用优先进行内存规整,所谓内存规整,就是把使用过的内存迁移到另一个位置,尽量把空闲内存合并成连续的内存,这样减少了内存碎片化,也就能够增加内存分配成功的概率。如果内存规整后依然无法满足内存需求,那就需要进一步执行内存回收了。当然也并不是所有的已用内存页都支持内存迁移,有一些UNMOVEABLE的页是不能被迁移的。目前只有匿名页和page cache支持内存迁移。
  • 周期内存回收(kswapd)
    前面也提到过,当内存低于low watermark,会启动一个线程kswapd,周期性的对内存进行回收,直到内存高于high watermark才停止。为什么需要周期回收,因为有些情况申请内存是不允许休眠等待的,也就不能执行直接回收操作,所以此时内存回收只能交给周期回收。
  • OOM killer
    如果内存规整/内存回收都无法满足系统需要,说明内存已经极度紧缺了,内存耗尽将导致系统无法继续运行下去,所以到此时系统也只能够“断臂求生”了,内核会选择一个process进程(select_bad_process),这个进程是需要满足内核的选择标准才会被选出来,需要注意的是swapper/init和其他内核线程是不允许被kill的。
  • swap交换分区
    交换分区的时候,经常在安装ubuntu或者其他Linux发行版的时候,我们在分区划分时,都会选择一个/swap分区,它就是我们所说的交换分区,当执行内存回收时,有一些特殊的页,比如用户匿名页,他们没有对应的文件可以写回,但是可以被置换一个特定的分区上,从而把内存释放出来被回收使用。交换分区带来一个问题,如果反复执行换入/换出操作,那么带来的系统开销是得不偿失的,因此,和其他文件系统缓存一样,swap分区也实现了对应swap高速缓存机制,因为磁盘操作是比较耗时的,为了防止并发操作引起的问题,在同时申请换出换入时,实际上是在交换缓冲区中取入取出,而没有实际操作磁盘。

内存回收的时机

  • 内存紧缺时回收
  • 周期性回收(kswapd/reap_work)
  • 系统睡眠时回收

反向映射(RMAP)

我们知道对于内存页的回收,根据页的不同,需要做的操作也不同,针对用户态匿名页,内核需要执行的回收操作是把它换出到交换区(swap分区),而针对用户空间映射页,内核需要做的是把该物理页的内容同步到磁盘上文件中。但是除了这些操作后,对于这两种页,内存回收还有一个重要的步骤,那就是需要定位到页page所对应的所有页表项,内核只有把所有的页表项都清除后才能允许对该页被回收重新使用。而反向映射介绍的就是如何根据page来反向的查找到对应的页表,特别是对于共享页,很可能会对应多个页表,通过反向映射,我们需要找到所有的相关页表项。

  • 匿名页反向映射
    匿名页一般是几个进程共享的,比如父进程克隆一个子进程,父子进程共享地址空间和页框,依赖与一个结构体:struct anon_vma.
    include/linux/mm_types.h:
struct page {
...
struct anon_vma *anon_vma;  /* Serialized by page_table_lock */
...
};

根据这个anon_vma可以找到与之匹配的vm_area_struct,再根据此结构找到mm_struct进程管理结构体,可能有多个匹配的进程,进而找到进程对应的用户空间页表,从而找到页表项。

  • 映射页反向映射
    为什么要把映射页的映射和匿名页分开来处理,那是因为系统中的映射页存在的会比较多,最终页表项的匹配是需要扫描进程页表的,如果每个进程都扫描一遍,效率会很低,所以这里更换了另一个算法来提高效率,那就是PST优先搜索树算法来查找物理页page对应的所有页表项。

LRU链表

LRU(Least Recently Used)链表,就是最近最少访问链表,分为active和inactive两种类型的链表,最近访问的page放到active链表,这些不用被回收算法回收,如果长时间未访问的page则放到inactive链表,根据页是否被访问,该page可能在两个链表中移动。在每个memory node结构体中都存在对应的LRU链表结构:

/* Fields commonly accessed by the page reclaim scanner */
spinlock_t      lru_lock;
struct lruvec       lruvec;

对应的lruvec结构体:

struct lruvec {
    struct list_head lists[NR_LRU_LISTS];
    struct zone_reclaim_stat reclaim_stat;
#ifdef CONFIG_MEMCG
    struct zone *zone;
#endif
};
  • 从inactive链表移动到active链表
    对于inactive中的page,会有两次访问才会移动到active链表,如果一次访问后间隔一定时间没有继续访问第二次,那么该page访问次数会被重置。
  • 从active链表移动到inactive链表
    refill_inactive_zone函数是做该项工作的,在内存回收的时候会调用shrink_zone,该函数会调用到refill_inactive_zone函数来填充inactive链表,然后从inactive链表中去回收内存页。因此refill_inactive_zone函数至关重要,
    它的实现决定了哪些page可以被回收。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值