kref:内核中最基本的引用计数,头文件“linux/kref.h”,其结构体为
struct kref
{
atomic_t refcount;
};
有三个操作函数:
// 初始化
void kref_init(struct kref *kref)
{
atomic_set(&kref->refcount, 1);
smp_mb();
}
// 递增引用计数
void kref_get(struct kref *kref)
{
WARN_ON(!atomic_read(&kref->refcount));
atomic_inc(&kref->refcount);
smp_mb__after_atomic_inc();
}
// 递减引用计数
// 如果引用计数递减后为0,将调用函数release来执行一些释放操作</span>,release函数由用户实现
int kref_put(struct kref *kref, void (*release)(struct kref *kref))
{
WARN_ON(release == NULL);
WARN_ON(release == (void (*)(struct kref *))kfree);
if (atomic_dec_and_test(&kref->refcount)) {
release(kref);
return 1;
}
return 0;
}
kref的用法:一般都是在自定义结构体中增加一个kref成员,为结构体提供引用计数功能。
#include <linux/kref.h>
struct my_data
{
......
struct kref ref;
};
int my_init()
{
struct my_data *pData;
pData = kmalloc(sizeof(*pData), GFP_KERNEL);
if (pData == NULL)
{
return -ENOMEM;
}
kref_init(&pData->ref);
// 把pData交给其他线程之前,计数+1
kref_get(&pData->ref);
task = kthread_run(my_thread_func, data, "more_data_handling");
if (task == ERR_PTR(-ENOMEM))
{
kref_put(&data->ref, my_release);
}
return 0;
}
// 用户自定义结构体内存释放函数,引用计数为0时会被调用
void my_release(struct kref *kref)
{
struct my_data *data = container_of(kref, struct my_data, ref);
kfree(data);
}
在我另外一篇博文usb驱动静态库编译中为了封装,希望能把struct my_data定义成void *:
#include <linux/kref.h>
struct my_data
{
......
void *pref; //struct kref ref;
};
int my_init()
{
struct kref *pRef;
struct my_data *pData;
pData = kmalloc(sizeof(*pData), GFP_KERNEL);
if (pData == NULL)
{
return -ENOMEM;
}
pRef = kmalloc(sizeof(*pRef), GFP_KERNEL);
if (pRef == NULL)
{
kfree(pData);
return -ENOMEM;
}
pData->pref = (void *)pRef;
//kref_init(&pData->ref);
kref_init((struct kref *)pData->pref);
// 把pData交给其他线程之前,计数+1
//kref_get(&pData->ref);
kref_get((struct kref *)pData->pref);
task = kthread_run(my_thread_func, data, "more_data_handling");
if (task == ERR_PTR(-ENOMEM))
{
kref_put(&data->ref, my_release);
}
return 0;
}
// 用户自定义结构体内存释放函数,引用计数为0时会被调用
void my_release(struct kref *kref)
{
//struct my_data *data = container_of(kref, struct my_data, ref);
struct my_data *data = container_of(kref, struct my_data, pref);
kfree(data);
}
看起来这样改就ok了,编译运行。插入usb设备,再拔出,死机。经查证系统就死在my_release里面。
我们来看看container_of,这是一个宏,定义在include/linux/kernel.h中
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
* */
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
显然,其作用就是根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针。
如果我们把my_data中的struct kref ref改成void *pref,那么在my_release中调用container_of(kref, struct my_data, pref)。其中kref就是struct my_data *pData的成员pref的值,而不是它的地址,也就无法获取到pData的地址了。
要想实现我们想要的功能,那就只能重写kref_put了:
int kref_put_new(struct kref **kref, void (*release)(struct kref **kref))
{
WARN_ON(release == NULL);
WARN_ON(release == (void (*)(struct kref **))kfree);
if (atomic_dec_and_test(&&kref->refcount)) {
release(*kref);
return 1;
}
return 0;
}
调用kref_put_new则改为
#include <linux/kref.h>
struct my_data
{
......
void *pref; //struct kref ref;
};
int my_init()
{
struct kref *pRef;
struct my_data *pData;
pData = kmalloc(sizeof(*pData), GFP_KERNEL);
if (pData == NULL)
{
return -ENOMEM;
}
pRef = kmalloc(sizeof(*pRef), GFP_KERNEL);
if (pRef == NULL)
{
kfree(pData);
return -ENOMEM;
}
pData->pref = (void *)pRef;
//kref_init(&pData->ref);
kref_init((struct kref *)pData->pref);
// 把pData交给其他线程之前,计数+1
//kref_get(&pData->ref);
kref_get((struct kref *)pData->pref);
task = kthread_run(my_thread_func, data, "more_data_handling");
if (task == ERR_PTR(-ENOMEM))
{
kref_put_new((struct kref **)&data->pref, my_release);
}
return 0;
}
// 用户自定义结构体内存释放函数,引用计数为0时会被调用
void my_release(struct kref **kref)
{
//struct my_data *data = container_of(kref, struct my_data, ref);
struct my_data *data = container_of(kref, struct my_data, pref);
kfree(data->pref);
kfree(data);
}
(完)