在 Linux
驱动开发中,register_shrinker
是内核中与内存管理相关的函数,通常用于注册内存回收机制。它允许驱动或模块向内核注册一个缩减器(shrinker
),用于释放可回收的内存资源,例如缓存或缓冲区。
函数定义
在内核代码中,register_shrinker
的定义如下:
int register_shrinker(struct shrinker *shrinker);
// 如果内核版本大于6.0
int register_shrinker(struct shrinker *shrinker, const char *fmt, ...)
参数:
shrinker
: 一个指向 struct shrinker
的指针,描述了缩减器的属性和行为。
返回值:
成功时返回 0。
如果注册失败,则返回负值错误代码。
结构体定义
struct shrinker
是定义缩减器行为的核心结构。它的定义位于 include/linux/shrinker.h
中。
struct shrinker {
struct list_head list; // 用于将 shrinker 添加到全局 shrinker 链表
long (*count_objects)(struct shrinker *); // 计算可缩减对象的数量
long (*scan_objects)(struct shrinker *, struct shrink_control *); // 执行实际的内存释放
int seeks; // I/O 操作的开销权重,影响缩减比例
unsigned long batch; // 每次释放操作的对象数量
struct rcu_head rcu; // 用于延迟删除的 RCU 头
};
关键字段:
count_objects
: 一个回调函数,返回当前可缩减对象的数量。
scan_objects
: 一个回调函数,执行实际的内存释放操作。
seeks
: 决定扫描比例的权重值。
batch
: 定义每次缩减操作中释放的对象数量。
工作原理
注册阶段:
驱动程序调用 register_shrinker
将一个 shrinker
实例注册到内核。
内存压力:
当系统检测到内存不足时,内核会调用所有注册的 shrinker
,尝试释放内存。
回调执行:
首先调用 count_objects
,获取可释放对象的数量。
然后调用 scan_objects
,根据需求实际释放内存。
函数使用示例
假设我们需要为一个缓存实现一个 shrinker
:
#include <linux/shrinker.h>
#include <linux/slab.h>
// 自定义缓存
static struct kmem_cache *my_cache;
// count_objects 回调函数
static long my_count_objects(struct shrinker *shrink, struct shrink_control *sc)
{
// 返回缓存中的对象数量
return kmem_cache_size(my_cache) / sizeof(struct my_object);
}
// scan_objects 回调函数
static long my_scan_objects(struct shrinker *shrink, struct shrink_control *sc)
{
long freed = 0;
struct my_object *obj;
// 模拟释放对象
while ((obj = kmem_cache_alloc(my_cache, GFP_KERNEL)) != NULL) {
kmem_cache_free(my_cache, obj);
freed++;
}
return freed;
}
// 定义 shrinker
static struct shrinker my_shrinker = {
.count_objects = my_count_objects,
.scan_objects = my_scan_objects,
.seeks = DEFAULT_SEEKS,
};
static int __init my_driver_init(void)
{
int ret;
// 初始化缓存
my_cache = kmem_cache_create("my_cache", sizeof(struct my_object), 0, SLAB_HWCACHE_ALIGN, NULL);
if (!my_cache)
return -ENOMEM;
// 注册 shrinker
ret = register_shrinker(&my_shrinker);
if (ret) {
kmem_cache_destroy(my_cache);
return ret;
}
return 0;
}
static void __exit my_driver_exit(void)
{
// 注销 shrinker
unregister_shrinker(&my_shrinker);
// 销毁缓存
kmem_cache_destroy(my_cache);
}
module_init(my_driver_init);
module_exit(my_driver_exit);
关键点总结
register_shrinker
用于注册缩减器,它会与内核的内存回收机制集成。
缩减器需要提供两个关键回调函数:
count_objects
:用于计算当前可释放的对象数量。
scan_objects
:用于执行实际的释放操作。
需要在模块卸载时调用 unregister_shrinker
,以避免内核泄漏。
通过 register_shrinker
,开发者可以实现灵活的内存管理,特别适用于缓存、缓冲区等需要动态回收的场景。