内存回收
是把已经使用过的的物理页帧重新放回到内核中的buddy
系统(buddy
系统用于申请空闲物理页帧的子系统)管理中,解决内存紧张的问题;内存回收
的页帧包括未修改的文件页帧
、修改且完成同步的文件页帧
、换出到设备的匿名页帧
。回收
过程就是接触这些页帧的使用,归还给buddy
子系统。RAW
内存总量不做严格的约束,当系统负载相对较低的时候,内存大部分被磁盘高速缓存使用,随着系统负载增大,系统进程使用的内存越来越多,磁盘的高速缓存占用的内存就会被缩小,内存页帧的回收必须在消耗所有空闲页帧之前进行,这个是为了安全。如果内核尝试回收后仍然无法获得物理页帧,内核会执行find_bad_process
找到一个进程并且执行OOM Kill
释放这个进程占用的物理页帧。slab allocation
,它用于内核数据结构
的申请。slab
分配器在频繁申请和释放的情况下效率比较高LRU
页帧链表zone
将空闲页帧和已经在使用的页帧分别用buddy
系统和
zone
的LRU
链表管理。struct zone {
// 用于空闲页帧管理的buddy系统
struct free_area free_area[MAX_ORDER];
// 已经在使用的LRU链表管理
struct pglist_data *zone_pgdat;
atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS];
}
// 用于管理已经在使用的内存页帧
typedef struct pglist_data {
spinlock_t lru_lock;
// LRU 链表
struct lruvec lruvec;
atomic_long_t vm_stat[NR_VM_NODE_STAT_ITEMS];
} pg_data_t;
LRU
链表类型种类定义在enum lru_list(最大是NR_LRU_LISTS)
.内核将文件页帧
和匿名页帧
各自分别形成2个LRU
链表。active
和inactive
的文件页帧和匿名页帧
的LRU
链表,最后内核除了这4个LRU
还会有第5个不可换出页的LRU
。pglist_data
有NR_LRU_LISTS
个,每个pglist_data
包含一个LRU
链表。// LRU链表
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
};
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
};
LRU
链表中未使用的
直接回收
和定期回收
。直接回收
是发生在分配页帧时候发现空闲页帧不足的情况下。直接回收
和定期回收
底层都是走相同的逻辑,但是直接回收
是实在紧急情况下进行,必须做到回收到足够的页帧。定期回收
则是内核的守护进程定期发起,尽量回收,保留足够的空闲空间。