内核对象kobject和sysfs(2)——kref分析

时间:2023-03-08 19:48:28

内核对象kobject和sysfs(2)——kref分析


在介绍ref之前,先贴上kref的结构:

struct kref {
atomic_t refcount;
};

可以看到,kref只是包含一个原子量的数而已,并没有别的成员提供自动回收的功能。实际上,kref只是记录了某个模块的引用次数,初始状态为1.当引用计数为0的时候,将调用自定义的释放函数。

下面我们介绍内核提供的kref的操作函数:

kref初始化函数kref_init:

static inline void kref_init(struct kref *kref)
{
atomic_set(&kref->refcount, 1);
}

从函数原型可以看到,实际上kref初始化的时候就被初始化为1了,kref

kref的增加引用的函数kref_get:

static inline void kref_get(struct kref *kref)
{
/* If refcount was 0 before incrementing then we have a race
* condition when this kref is freeing by some other thread right now.
* In this case one should use kref_get_unless_zero()
*/
WARN_ON_ONCE(atomic_inc_return(&kref->refcount) < 2);
}

由于初始化已经是1,所以即使是第一次增加引用,引用计数也不会小于2,如果小于了2,则说明程序出了问题,将给出一个警告。

kref的释放引用的函数:

static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
{
return kref_sub(kref, 1, release);
} static inline int kref_sub(struct kref *kref, unsigned int count,
void (*release)(struct kref *kref))
{
WARN_ON(release == NULL); if (atomic_sub_and_test((int) count, &kref->refcount)) {
release(kref);
return 1;
}
return 0;
}

可以看到,释放函数,有两个参数,其中一个是kref,另外一个是一个函数指针。这个函数,则是自定义的释放函数。在kref_sub函数内,当条件满足,将会调用自定义的release函数。

atomic_sub_and_test函数kref减去count,如果结果是0,则返回真,否则,返回假。

对于初学者,读到这里,其实可以停下来,去写一个自定义的内嵌kref的内核模块进行测试kref的功能。下面给出编程步骤:

  1. 自定义一个结构体,内嵌kref。注意,一定要内嵌;
  2. 初始化函数内申请资源构造自定义的结构体;
  3. 初始化kref;
  4. 在卸载函数内,释放引用函数。

在这样的简单的一个模块里,验证自定义的release函数是否会被调用,是初学者深刻理解kref的必经之路。

但是,这里我想提出一个问题。前面说过,kref是为了自动回收资源来用的,但是上面的例子里,明显回收资源还是需要自己调用kref_put,这不是反而变得麻烦了么?

实际,在实际的驱动中,kref_get可能会出现在各种地方。其实,我们可以注意到,如果不使用kref机制,那么在卸载函数里,我们需要强制回收资源。但是如果使用了kref的功能,那么实际上,在卸载函数里,并不一定真正回收资源。因为此时kref的引用不一定为0.这在热插拔中的应用非常普遍。