页回收机制

申请分配物理页的时候,页分配器首先尝试使用低水线分配页;如果使用低水线分配失败,说明内存轻微不足,页分配器将会唤醒内存节点的页回收内核线程,异步回收页,然后尝试使用最低水线分配页;如果使用最低水线分配失败,说明内存严重不足,页分配器将会直接回收页
针对不同的物理页,采用不同的回收策略;物理页根据是否有存储设备支持分为两类:
1)可以被交换的页:没有存储设备支持的物理页,包括匿名页,以及tmpfs文件系统(内存中的文件系统)的文件页和进程在修改私有的文件映射时复制生成的匿名页;
2)存储设备支持的文件页;

针对不同的物理页,采用不同的回收策略;
1)可以被交换的页:采用页交换的方法,先把页的数据写到交换区,然后释放物理页;
2)存储设备支持的文件页;

针对不同得物理页,采用不同得回收策略:
1)可以被交换的页:采用页交换的方法,先把页的数据写到交换区,然后释放物理页;
2)存储设备支持的文件页:如果是干净的页,即把文件从存储设备读到内存以后没有修改过,可以直接释放页;如果是脏页,即把文件从存储设备读到后修改过,那么先写回到存储设备,然后释放物理页;
页回收算法还会回收slab缓存;使用专用slab缓存的内核模块可以使用函数register_shrinker注册收缩器,页回收算法调用所有收缩器的函数以释放对象;

**根据什么原则选择待回收的物理页?**内核使用LRU(Least Recently Used,最近最少使用)算法选择最近最少使用的物理页,来进行回收;
回收物理页的时候,如果物理页被映射到进程的虚拟地址空间,那么需要从页表中删除虚拟页到物理页的映射;怎么知道物理页被映射到哪些虚拟页?需要通过反向映射的数据结构,虚拟页映射到物理页是正向映射,物理页映射到虚拟页是反向映射;

数据结构

LRU链表

页回收算法使用LRU算法选择待回收的物理页;如下图所示,每个内存节点的pglist_data实例有一个成员lruvec,成为LRU向量;


enum lru_list {
	LRU_INACTIVE_ANON = LRU_BASE,
	LRU_ACTIVE_ANON = LRU_BASE + LRU_ACTIVE,
	LRU_INACTIVE_FILE = LRU_BASE + LRU_FILE,
	LRU_ACTIVE_FILE = LRU_BASE + LRU_FILE + LRU_ACTIVE,
	LRU_UNEVICTABLE,
	NR_LRU_LISTS
};

struct lruvec {
	struct list_head		lists[NR_LRU_LISTS];
	struct zone_reclaim_stat	reclaim_stat;
	/* Evictions & activations on the inactive file list */
	atomic_long_t			inactive_age;
	/* Refaults at the time of last reclaim cycle */
	unsigned long			refaults;
#ifdef CONFIG_MEMCG
	struct pglist_data *pgdat;
#endif
};

typedef struct pglist_data {
	struct zone node_zones[MAX_NR_ZONES];
	struct zonelist node_zonelists[MAX_ZONELISTS];
	int nr_zones;
#ifdef CONFIG_FLAT_NODE_MEM_MAP	/* means !SPARSEMEM */
	struct page *node_mem_map;
    ......
    /* Fields commonly accessed by the page reclaim scanner */
	struct lruvec		lruvec;
    ......
}

LRU向量包含5条LRU链表;

  • 不活动匿名页LRU链表,用来链接不活动的匿名页,即最近访问频率低的匿名页;
  • 活动匿名页LRU链表,用来链接活动的匿名页,即最近访问频率高的匿名页;
  • 不活动文件页LRU链表,用来链接不活动的文件页,即最近访问频率低的文件页;
  • 活动文件页LRU链表,用来链接活动的文件页,即最近访问频率高的文件页;
  • 不可回收LRU链表,用来链接使用mlock锁定在内存中、不允许回收的物理页;

在LRU链表中,物理页的页描述符(struct page)的flags成员会被设置如下:

  • flags被设置PG_lru标志位,表示物理页在LRU链表中;
  • 页描述符通过lru成员加入到LRU链表;
  • 如果是可以被交换的物理页,flags会被设置PG_swapbacked标志位;
  • 如果是活动的物理页,flags会被设置PG_active标志位;
  • 如果是不可回收的物理页,flags会被设置PG_unevictable标志位;

每条LRU链表中的物理页按访问时间从小到大排序,链表头节点的物理页的访问时间离当前最近,物理页从LRU链表的头部插入;页回收算法从不活动LRU链表的尾部取物理页进行回收,从活动LRU链表的尾部取物理页并移动到不活动LRU链表中;
image.png

如何确定页的活动程度?

  • 如果是页表映射的匿名页或文件页,根据页表项中的访问标志位确定页的活动程度;
  • 如果是没有页表映射的文件页,进程通过文件系统read或write访问文件,文件系统在文件的页缓存中查找文件页,为文件页的页描述符设置访问标志位(PG_referenced);

反向映射

回收页表映射的匿名页或文件页时,需要从页表中删除映射,内核需要知道物理页被映射到哪些进程的虚拟地址空间,需要实现物理页到虚拟页的反向映射;

页描述符中和反向映射相关的成员如下:

struct page {
    ...
	union {
		struct address_space *mapping;	/* If low bit clear, points to
						 * inode address_space, or NULL.
						 * If page mapped as anonymous
						 * memory, low bit is set, and
						 * it points to anon_vma object:
						 * see PAGE_MAPPING_ANON below.
						 */
        ...
	};
	/* Second double word */
	union {
		pgoff_t index;		/* Our offset within mapping. */
        ...
	};
	union {
        ...
		struct {

			union {
				/*
				 * Count of ptes mapped in mms, to show when
				 * page is mapped & limit reverse map searches.
				 *
				 * Extra information about page type may be
				 * stored here for pages that are never mapped,
				 * in which case the value MUST BE <= -2.
				 * See page-flags.h for more details.
				 */
				atomic_t _mapcount;

                ...
			};
            ...
		};
	};
    ...
};

mapping,最低两位用来作用页映射标志,PAGE_MAPPING_ANON表示匿名页;
index,是在映射里面的偏移,单位是页;如果是匿名映射,那么index是物理页对应的虚拟页在虚拟内存中的页偏移;如果是文件映射,那么index是物理页存储的数据在文件页中的页偏移;
mapcount,映射计数,反映物理页物理页被映射到多少个虚拟内存区域;初始值是-1,加上1以后才是真实的映射计数;
本文中只介绍匿名页的反向映射;文件页的反向映射请查阅相关书籍;

匿名页的反向映射

匿名页的反向映射的数据结构如下图:
image.png
1)struct page的成员mapping指向一个anon_vma实例,并设置了PAGE_MAPPING_ANON标志位;
2)struct anon_vma用来组织匿名页被映射到的所有虚拟内存区域;
3)anon_vma_chain用来关联anon_vma实例和vm_area_struct实例;
4)一个匿名页可能被映射到多个虚拟内存区域,比如fork子进程;anon_vma实例通过中介anon_vma_chain把所有vm_area_struct实例和anon_vma实例加入到链表中,关联起来;

页回收过程

如下图所示,申请分配页的时候,页分配器首先尝试使用低水线分配页;如果使用低水线分配失败,说明内存轻微不足,页分配器将会唤醒所有符合分配条件的内存节点的页回收线程,异步回收页,然后尝试使用最低水线分配页;如果分配失败,说明内存严重不足,页分配器将会直接回收页;如果直接回收页失败,那么判断是否应该重新尝试页回收;
image.png
异步回收页和直接回收页,最后都是调用函数shrink_node来回收内存节点的页;

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值