
https://github.com/wdfk-prog/linux-study
list_lru
list_lru
概述
list_lru
是 Linux 内核中的一种通用 LRU(最近最少使用)管理机制,用于高效地管理内存中的对象列表。它通过维护一个链表和相关的计数器,提供了对对象的添加、删除、隔离和遍历等操作。list_lru
的设计目标是优化内存回收过程,尤其是在内存压力下,通过与 shrinker
机制结合,释放不必要的内存资源。
原理
list_lru
的核心原理是通过链表和计数器管理对象的生命周期,并与内存控制组(memcg
)和 NUMA 节点结合,提供灵活的 LRU 管理功能。以下是其主要原理:
链表管理:
- 每个
list_lru
结构维护一个链表(list_head
),用于存储 LRU 对象。 - 对象通过
list_lru_add
添加到链表尾部,通过list_lru_del
从链表中移除。
- 每个
计数器:
- 每个链表都有一个计数器(
nr_items
),用于记录链表中对象的数量。 - 计数器的更新通过原子操作实现,确保线程安全。
- 每个链表都有一个计数器(
NUMA 节点支持:
list_lru
支持 NUMA 节点,每个节点都有独立的链表和计数器。- 通过
page_to_nid
获取对象所在的 NUMA 节点,确保 LRU 操作的本地性。
内存控制组(
memcg
)支持:- 如果启用了
CONFIG_MEMCG
,list_lru
可以与内存控制组结合,支持基于memcg
的 LRU 管理。 - 每个
memcg
都有独立的链表,允许更细粒度的内存回收。
- 如果启用了
与
shrinker
结合:list_lru
与shrinker
机制结合,提供对象的回收功能。- 当内存压力较大时,
shrinker
会调用list_lru_walk
遍历链表并隔离对象。
使用场景
内存回收:
- 在内存压力下,通过
list_lru
和shrinker
机制回收不必要的对象,释放内存资源。
- 在内存压力下,通过
文件系统缓存管理:
- 文件系统使用
list_lru
管理inode
和dentry
的缓存,优化文件系统性能。
- 文件系统使用
NUMA 优化:
- 在 NUMA 系统中,通过
list_lru
的节点支持,确保 LRU 操作的本地性,减少跨节点访问的开销。
- 在 NUMA 系统中,通过
内存控制组(
memcg
)管理:- 在启用了
CONFIG_MEMCG
的系统中,list_lru
提供基于memcg
的 LRU 管理,支持更细粒度的内存回收。
- 在启用了
优点
通用性:
list_lru
是一种通用的 LRU 管理机制,适用于多种场景(如文件系统、内存回收)。
高效性:
- 使用链表和原子计数器管理对象,支持高效的添加、删除和遍历操作。
NUMA 和
memcg
支持:- 支持 NUMA 节点和内存控制组,提供灵活的 LRU 管理功能。
与
shrinker
结合:- 与
shrinker
机制结合,优化内存回收过程,减少内存压力。
- 与
缺点
复杂性:
list_lru
的实现较为复杂,涉及 NUMA 节点、memcg
和shrinker
,可能增加开发和调试的难度。
性能瓶颈:
- 在高并发场景下,链表操作可能成为性能瓶颈,尤其是链表较长时。
内存开销:
- 每个 NUMA 节点和
memcg
都需要维护独立的链表和计数器,可能增加内存开销。
- 每个 NUMA 节点和
其他方案
直接 LRU 链表:
- 使用简单的 LRU 链表管理对象,不支持 NUMA 和
memcg
。 - 优点:实现简单,适用于小规模场景。
- 缺点:缺乏灵活性,性能较低。
- 使用简单的 LRU 链表管理对象,不支持 NUMA 和
基于哈希的缓存管理:
- 使用哈希表管理对象,结合 LRU 算法实现缓存管理。
- 优点:支持快速查找,适用于高性能场景。
- 缺点:实现复杂,内存开销较大。
自定义回收机制:
- 根据具体需求实现自定义的回收机制,替代
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);