[Linux]学习笔记系列 --[mm][list_lru]

发布于:2025-08-19 ⋅ 阅读:(16) ⋅ 点赞:(0)


在这里插入图片描述

https://github.com/wdfk-prog/linux-study

list_lru

list_lru 概述

list_lru 是 Linux 内核中的一种通用 LRU(最近最少使用)管理机制,用于高效地管理内存中的对象列表。它通过维护一个链表和相关的计数器,提供了对对象的添加、删除、隔离和遍历等操作。list_lru 的设计目标是优化内存回收过程,尤其是在内存压力下,通过与 shrinker 机制结合,释放不必要的内存资源。


原理

list_lru 的核心原理是通过链表和计数器管理对象的生命周期,并与内存控制组(memcg)和 NUMA 节点结合,提供灵活的 LRU 管理功能。以下是其主要原理:

  1. 链表管理

    • 每个 list_lru 结构维护一个链表(list_head),用于存储 LRU 对象。
    • 对象通过 list_lru_add 添加到链表尾部,通过 list_lru_del 从链表中移除。
  2. 计数器

    • 每个链表都有一个计数器(nr_items),用于记录链表中对象的数量。
    • 计数器的更新通过原子操作实现,确保线程安全。
  3. NUMA 节点支持

    • list_lru 支持 NUMA 节点,每个节点都有独立的链表和计数器。
    • 通过 page_to_nid 获取对象所在的 NUMA 节点,确保 LRU 操作的本地性。
  4. 内存控制组(memcg)支持

    • 如果启用了 CONFIG_MEMCGlist_lru 可以与内存控制组结合,支持基于 memcg 的 LRU 管理。
    • 每个 memcg 都有独立的链表,允许更细粒度的内存回收。
  5. shrinker 结合

    • list_lrushrinker 机制结合,提供对象的回收功能。
    • 当内存压力较大时,shrinker 会调用 list_lru_walk 遍历链表并隔离对象。

使用场景

  1. 内存回收

    • 在内存压力下,通过 list_lrushrinker 机制回收不必要的对象,释放内存资源。
  2. 文件系统缓存管理

    • 文件系统使用 list_lru 管理 inodedentry 的缓存,优化文件系统性能。
  3. NUMA 优化

    • 在 NUMA 系统中,通过 list_lru 的节点支持,确保 LRU 操作的本地性,减少跨节点访问的开销。
  4. 内存控制组(memcg)管理

    • 在启用了 CONFIG_MEMCG 的系统中,list_lru 提供基于 memcg 的 LRU 管理,支持更细粒度的内存回收。

优点

  1. 通用性

    • list_lru 是一种通用的 LRU 管理机制,适用于多种场景(如文件系统、内存回收)。
  2. 高效性

    • 使用链表和原子计数器管理对象,支持高效的添加、删除和遍历操作。
  3. NUMA 和 memcg 支持

    • 支持 NUMA 节点和内存控制组,提供灵活的 LRU 管理功能。
  4. shrinker 结合

    • shrinker 机制结合,优化内存回收过程,减少内存压力。

缺点

  1. 复杂性

    • list_lru 的实现较为复杂,涉及 NUMA 节点、memcgshrinker,可能增加开发和调试的难度。
  2. 性能瓶颈

    • 在高并发场景下,链表操作可能成为性能瓶颈,尤其是链表较长时。
  3. 内存开销

    • 每个 NUMA 节点和 memcg 都需要维护独立的链表和计数器,可能增加内存开销。

其他方案

  1. 直接 LRU 链表

    • 使用简单的 LRU 链表管理对象,不支持 NUMA 和 memcg
    • 优点:实现简单,适用于小规模场景。
    • 缺点:缺乏灵活性,性能较低。
  2. 基于哈希的缓存管理

    • 使用哈希表管理对象,结合 LRU 算法实现缓存管理。
    • 优点:支持快速查找,适用于高性能场景。
    • 缺点:实现复杂,内存开销较大。
  3. 自定义回收机制

    • 根据具体需求实现自定义的回收机制,替代 list_lru
    • 优点:针对性强,适合特定场景。
    • 缺点:增加开发复杂性。

总结

list_lru 是 Linux 内核中的一种通用 LRU 管理机制,提供了高效、灵活的对象管理功能。它通过链表和计数器管理对象,并支持 NUMA 节点和内存控制组,适用于文件系统缓存管理和内存回收等场景。尽管 list_lru 的实现较为复杂,但其通用性和高效性使其成为内核中重要的内存管理工具。在特定场景下,可以考虑使用直接 LRU 链表、哈希缓存或自定义回收机制作为替代方案。

include/linux/list_lru.h

list_lru_init

#define list_lru_init(lru)				\
	__list_lru_init((lru), false, NULL)
#define list_lru_init_memcg(lru, shrinker)		\
	__list_lru_init((lru), true, shrinker)

mm/list_lru.c

list_lru_add 将对象(item)添加到 LRU(最近最少使用)列表中

/* 调用方必须确保 memcg 生存期。 */
bool list_lru_add(struct list_lru *lru, struct list_head *item, int nid,
		  struct mem_cgroup *memcg)
{
	struct list_lru_node *nlru = &lru->node[nid];
	struct list_lru_one *l;

    /* 锁定与内存控制组(memcg)关联的 LRU 列表 */
	l = lock_list_lru_of_memcg(lru, nid, memcg, false, false);
    /* 如果无法锁定(例如 memcg 生命周期已结束),函数返回 false,表示添加失败 */
	if (!l)
		return false;
    /* 如果对象为空,表示它可以安全地添加到 LRU 列表中 */
	if (list_empty(item)) {
        /* 将对象添加到 LRU 列表的尾部 */
		list_add_tail(item, &l->list);
		/* 如果添加了第一个元素,则设置 shrinker bit */
		if (!l->nr_items++)
            /* Shrinker 位用于标记 LRU 列表需要被回收器(Shrinker)处理 */
			set_shrinker_bit(memcg, nid, lru_shrinker_id(lru));
		unlock_list_lru(l, false);
		atomic_long_inc(&nlru->nr_items);
		return true;
	}
	unlock_list_lru(l, false);
	return false;
}

list_lru_add_obj 将对象(item)添加到 LRU(最近最少使用)列表中

  • lRU 是一种缓存管理机制,优先保留最近使用的对象并逐步移除较少使用的对象。该函数支持内存控制组(Memory Control Groups,memcg)的感知功能,以优化内存管理
bool list_lru_add_obj(struct list_lru *lru, struct list_head *item)
{
	bool ret;
	int nid = page_to_nid(virt_to_page(item));

    /* return false; */
	if (list_lru_memcg_aware(lru)) {
		rcu_read_lock();
		ret = list_lru_add(lru, item, nid, mem_cgroup_from_slab_obj(item));
		rcu_read_unlock();
	} else {
		ret = list_lru_add(lru, item, nid, NULL);
	}

	return ret;
}
EXPORT_SYMBOL_GPL(list_lru_add_obj);

list_lru_del 从 LRU(最近最少使用)列表中删除指定的对象(item)

/* 调用方必须确保 memcg 生存期。 */
bool list_lru_del(struct list_lru *lru, struct list_head *item, int nid,
		  struct mem_cgroup *memcg)
{
	struct list_lru_node *nlru = &lru->node[nid];
	struct list_lru_one *l;
	l = lock_list_lru_of_memcg(lru, nid, memcg, false, false);
	if (!l)
		return false;
	if (!list_empty(item)) {
		list_del_init(item);
		l->nr_items--;
		unlock_list_lru(l, false);
		atomic_long_dec(&nlru->nr_items);
		return true;
	}
	unlock_list_lru(l, false);
	return false;
}

list_lru_del_obj 从 LRU(最近最少使用)列表中删除指定的对象(item

bool list_lru_del_obj(struct list_lru *lru, struct list_head *item)
{
	bool ret;
	int nid = page_to_nid(virt_to_page(item));

	if (list_lru_memcg_aware(lru)) {
		rcu_read_lock();
		ret = list_lru_del(lru, item, nid, mem_cgroup_from_slab_obj(item));
		rcu_read_unlock();
	} else {
		ret = list_lru_del(lru, item, nid, NULL);
	}

	return ret;
}
EXPORT_SYMBOL_GPL(list_lru_del_obj);

init_one_lru 初始化一个 list_lru_one 结构

static void init_one_lru(struct list_lru *lru, struct list_lru_one *l)
{
	INIT_LIST_HEAD(&l->list);
	spin_lock_init(&l->lock);
	l->nr_items = 0;
#ifdef CONFIG_LOCKDEP
	if (lru->key)
		lockdep_set_class(&l->lock, lru->key);
#endif
}

__list_lru_init 初始化一个 list_lru 结构

int __list_lru_init(struct list_lru *lru, bool memcg_aware, struct shrinker *shrinker)
{
	int i;

	lru->node = kcalloc(nr_node_ids, sizeof(*lru->node), GFP_KERNEL);
	if (!lru->node)
		return -ENOMEM;

	for_each_node(i)
		init_one_lru(lru, &lru->node[i].lru);

	// memcg_init_list_lru(lru, memcg_aware);
	// list_lru_register(lru);

	return 0;
}
EXPORT_SYMBOL_GPL(__list_lru_init);

网站公告

今日签到

点亮在社区的每一天
去签到