目录:
1 设备驱动模型概述
2 设备驱动模型核心数据结构
2.1 kobject结构
2.2 kobj_type结构
2.3 kset结构
1 设备驱动模型概述:
设备驱动模型在2.6内核被引入进来,它充分体现了
分层,分工的思想。设备驱动程序的引入,很大程度上增加了代码的复用率。
设备驱动模型提供了硬件的抽象,内核可以使用这些抽象完成很多硬件重复的工作。这些抽象包括:
(1) 电源管理
电源管理主要目的是为了降低系统的功耗问题。即在设备不需要工作时,以低功耗的形式将设备挂起,待使用时再将其唤醒。
总线上的设备没有都挂起时,总线必须处于唤醒状态,只有所有的设备都挂起时,总线才能被挂起。
(2) 热插拔的支持
系统能够自动的探测设备是否接入系统。
(3) 用户空间sys文件系统通信接口
将内核中的信息通过sys文件系统与用户空间进行交互。
sys文件系统是Linux 2.6内核引入的一个内存文件系统。在sys文件系统中,大多数文件是ASCII文件,这样便于用户空间直接进行访问,也可称之为属性文件,通常每个文件有一个值,称之为属性。
sys文件系统中有三个重要的数据结构:
--------------------------------------------------------------------------------------------------
内核结构 sys结构
--------------------------------------------------------------------------------------------------
kobject (内核对象) 目录
kobj_type(对象属性) 属性文件
对象关联 (对象关系) 符号链接
--------------------------------------------------------------------------------------------------
sys文件系统目录结构由目录,属性文件,符号链接组成。sys文件系统中挂接的目录代表sys文件系统中的子系统。
当设备启动时,设备驱动模型会注册kobject对象,产生相应的目录文件。
(1) block目录
block目录包含了系统中发现的每个块设备的子目录,每个块设备对应一个子目录。在子目录中包含了该块设备的属性文件。
块设备目录中,有一个表示IO调度器的目录,这个目录提供了一些属性文件。可以利用这些属性文件控制块设备,包括改变它们的调度器等。块设备的每个分区分别为块设备的子目录,子目录中包含了分区的信息。
(2) bus目录
bus目录中包含了系统中注册而得到的物理总线的子目录。在子目录中,包含device和driver目录,分别表示该总线上的设备和驱动。
(3) class目录
class目录中的子目录为注册到内核中的设备类。
2 设备驱动模型核心数据结构:
设备驱动模型将设备与驱动分离,主要由以下几个数据结构来进行描述:
kobject、kset、subsystem.
设备驱动模型:
对应sys中的内容:
内核对于数据结构的处理,一般的使用流程即:
定义数据结构对象 --》 初始化数据结构对象 --》注册数据结构对象到内核。
设备驱动模型存在的意义在于把抽象的高级对象链接到设备驱动模型中,通过设备驱动模型来对设备进行管理。
下面就利用这个这个流程来对设备驱动模型核心数据结构进行分析:
2.1 kobject结构:
kobject主要实现以下作用:
1) 对象的引用计数。
2) sysfs空间的表述。
3) 设备驱动模型数据结构的关联。
4) 热插拔事件的处理。
kobject数据结构对象:
-
--include/linux/kobject.h
-
-
struct kobject {
-
const char *name; /* kobject 名称,该名称在sys目录中表现为一个目录的名称 */
-
struct list_head entry; /* kobject链表入口,连接下一个kobject */
-
struct kobject *parent; /* kobject父对象指针,如果存在 */
-
struct kset *kset; /* kobject集合指针 */
-
struct kobj_type *ktype; /* kobject属性指针 ,可以看做sys目录中的一个文件.
-
* 注意:对于sys中的普通文件的读写,都是通过kobject->kobj_type->sysfs_ops
-
* 来完成的。
-
*/
-
struct sysfs_dirent *sd; /* 对应sys的文件目录 */
-
struct kref kref; /* kobject的引用计数,kobjet_get和kobject_put函数分别用于增加/减少引用计数 */
-
unsigned int state_initialized:1; /* 状态位:是否初始化过,1表示初始化完成,0 表示初始化未完成 */
-
unsigned int state_in_sysfs:1; /* 状态位:是否导入sys中 */
-
unsigned int state_add_uevent_sent:1; /* 状态位:是否添加u_event */
-
unsigned int state_remove_uevent_sent:1; /* 状态位:是否移除u_event */
-
unsigned int uevent_suppress:1; /* 状态位:是否抑制u_event */
-
};
kobject由上述的成员组成,创建完kobject后需要对其进行初始化。kobject的初始化工作应该先使用memset来对其进行清零,以避免不必要的错误,具体的初始化工作由kobject_init来完成:
kobject数据结构对象初始化:
(1) void kobject_init(struct kobject *kobj, struct kobj_type *ktype):
-
--lib/kobject.c
-
/**
-
* kobject_init - initialize a kobject structure
-
* @kobj: pointer to the kobject to initialize
-
* @ktype: pointer to the ktype for this kobject.
-
*
-
* This function will properly initialize a kobject such that it can the be passed to the kobject_add() call。After this function is
-
* called, the kobject MUST be cleaned up by a call to kobject_put(), not by a call to kfree directly to ensure that all of the memory
-
* is cleaned up properly.
-
*/
-
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
char *err_str;
if ( !kobj ) { /* 参数检查 */
err_str = "invalid kobject pointer!";
goto error;
}
if ( !ktype ) { /* 参数检查 */
err_str = "must have a ktype to be initialized properly!\n";
goto error;
}
if ( kobj->state_initialized ) { /* 判定是否初始化已经完成 */
/* do not error out as sometimes we can recover */
printk(KERN_ERR "kobject (%p): tried to init an initialized "
"object, something is seriously wrong.\n", kobj);
dump_stack();
}
kobject_init_internal(kobj); /* 初始化kobject内部成员 */
kobj->ktype = ktype; /* 设置kobject属性 */
return;
error:
printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
dump_stack();
}
EXPORT_SYMBOL(kobject_init);
上述初始化过程可以看出,初始化工作大部分是放置在kobject_init_internal函数中来完成的。
static void kobject_init_internal(struct kobject *kobj):
-
--lib/kobject.c
-
-
static void kobject_init_internal(struct kobject *kobj)
-
{
-
if (!kobj) /* 参数检查 */
-
return;
-
kref_init(&kobj->kref); /* 引用计数初始化,初始化为1 */
-
INIT_LIST_HEAD(&kobj->entry); /* 初始化kobject链表 */
-
kobj->state_in_sysfs = 0; /* 状态位: 未导出到sys中 */
-
kobj->state_add_uevent_sent = 0; /* 状态位:未添加uevent */
-
kobj->state_remove_uevent_sent = 0; /* 状态位 : 未移除uevent */
-
kobj->state_initialized = 1; /* 状态位:已完成初始化 */
-
}
-
注意:上述初始化过程未初始化 kobject的name成员,而ktype成员在kobject_init中进行了初始化。
-
上述初始化过程有关于引用计数的问题,引用计数由下列函数来完成:
void kref_init(struct kref *kref)
-
--lib/kref.c
-
-
/**
-
* kref_init - initialize object.
-
* @kref: object in question.
-
*/
-
void kref_init(struct kref *kref)
-
{
-
kref_set(kref, 1);
-
}
-
-
/**
-
* kref_set - initialize object and set refcount to requested number.
-
* @kref: object in question.
-
* @num: initial reference counter(引用计数)
-
*/
-
void kref_set(struct kref *kref, int num)
-
{
-
atomic_set(&kref->refcount, num);
-
smp_mb();
-
}
kobject将被其子对象引用,因此为了控制对其占用的资源是否释放,需对其进行引用计数:
(1)增加kobject的引用计数:
struct kobject *kobject_get(struct kobject *kobj)
-
--lib/kobject.c
-
-
/**
-
* kobject_get - increment refcount for object.
-
* @kobj: object.
-
*/
-
struct kobject *kobject_get(struct kobject *kobj)
-
{
-
if (kobj)
-
kref_get(&kobj->kref);
-
return kobj;
-
}
-
-
-
-
--lib/kref.c
-
-
/**
-
* kref_get - increment refcount for object.
-
* @kref: object.
-
*/
-
void kref_get(struct kref *kref)
-
{
-
WARN_ON(!atomic_read(&kref->refcount));
-
atomic_inc(&kref->refcount);
-
smp_mb__after_atomic_inc();
}
(2) 减少kobject的引用计数:
void kobject_put(struct kobject *kobj)
-
--lib/kobject.c
-
-
-
/**
-
* kobject_put - decrement refcount for object.
-
* @kobj: object.
-
*
-
* Decrement the refcount, and if 0, call kobject_cleanup().
-
*/
-
void kobject_put(struct kobject *kobj)
-
{
-
if (kobj) {
-
if (!kobj->state_initialized)
-
WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "
-
"initialized, yet kobject_put() is being "
-
"called.\n", kobject_name(kobj), kobj);
-
kref_put(&kobj->kref, kobject_release); /* 当引用计数为0时,自动调用kobject_release函数进行资源的释放 */
-
}
-
}
-
-
-
--lib/kref.c
-
-
/**
-
* kref_put - decrement refcount for object.
-
* @kref: object.
-
* @release: pointer to the function that will clean up the object when the
-
* last reference to the object is released.
-
* This pointer is required, and it is not acceptable to pass kfree
-
* in as this function.
-
*
-
* Decrement the refcount, and if 0, call release().
-
* Return 1 if the object was removed, otherwise return 0. Beware, if this
-
* function returns 0, you still can not count on the kref from remaining in
-
* memory. Only use the return value if you want to see if the kref is now
-
* gone, not present.
-
*/
-
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;
-
}
-
-
-
--lib/kobject.c
-
static void kobject_release(struct kref *kref)
-
{
-
kobject_cleanup(container_of(kref, struct kobject, kref));
-
}
-
-
-
-
--lib/kobject.c
-
/**
-
*kobject_cleanup - free kobject resources.
-
* @kobj: object to cleanup
-
*/
-
static void kobject_cleanup(struct kobject *kobj) /* kobject_put函数最终调用了该函数 */
-
{
-
struct kobj_type *t = get_ktype(kobj);
-
const char *name = kobj->name;
-
pr_debug("kobject: '%s' (%p): %s\n",
-
kobject_name(kobj), kobj, __func__);
-
-
if (t && !t->release) /* kobject_release函数最终调用了kobject->ktype->release函数,该函数需要用户自己实现,用于释放资源 */
-
pr_debug("kobject: '%s' (%p): does not have a release() "
-
"function, it is broken and must be fixed.\n",
-
kobject_name(kobj), kobj);
-
-
/* send "remove" if the caller did not do it but sent "add" */
-
if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) {
-
pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event\n",
-
kobject_name(kobj), kobj);
-
kobject_uevent(kobj, KOBJ_REMOVE);
-
}
-
-
/* remove from sysfs if the caller did not do it */
-
if (kobj->state_in_sysfs) {
-
pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n",
-
kobject_name(kobj), kobj);
-
kobject_del(kobj);
-
}
-
-
if (t && t->release) {
-
pr_debug("kobject: '%s' (%p): calling ktype release\n",
-
kobject_name(kobj), kobj);
-
t->release(kobj);
-
}
-
-
/* free name if we allocated it */
-
if (name) {
-
pr_debug("kobject: '%s': free name\n", name);
-
kfree(name);
-
}
-
总结以下kobject的释放流程如下:
kobject注册到内核后,将以其name成员名称,呈现在/sys目录中,以下函数可操作name成员:
int kobject_set_name(struct kobject *kobj, const char *fmt, ...): 直接设置name成员。
-
--lib/kobject.c
-
-
-
/**
-
* kobject_set_name - Set the name of a kobject
-
* @kobj: struct kobject to set the name of
-
* @fmt: format string used to build the name(格式化字符串,与printf形式相同)
-
*
-
* This sets the name of the kobject. If you have already added the
-
* kobject to the system, you must call kobject_rename() in order to
-
* change the name of the kobject.
-
*/
-
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
-
{
-
va_list vargs;
-
int retval;
-
-
va_start(vargs, fmt);
-
retval = kobject_set_name_vargs(kobj, fmt, vargs);
-
va_end(vargs);
-
-
return retval;
-
}
-
EXPORT_SYMBOL(kobject_set_name);
int kobject_rename(struct kobject *kobj, const char *new_name): 重命名,一般在kobject注册到系统后需要更改name时使用。
-
--lib/kobject.c
-
-
-
/**
-
* kobject_rename - change the name of an object
-
* @kobj: object in question.
-
* @new_name: object's new name
-
*
-
* It is the responsibility of the caller to provide mutual
-
* exclusion between two different calls of kobject_rename
-
* on the same kobject and to ensure that new_name is valid and
-
* won't conflict with other kobjects.
-
*/
-
int kobject_rename(struct kobject *kobj, const char *new_name)
-
{
-
int error = 0;
-
const char *devpath = NULL;
-
const char *dup_name = NULL, *name;
-
char *devpath_string = NULL;
-
char *envp[2];
-
-
kobj = kobject_get(kobj);
-
if (!kobj)
-
return -EINVAL;
-
if (!kobj->parent)
-
return -EINVAL;
-
-
devpath = kobject_get_path(kobj, GFP_KERNEL);
-
if (!devpath) {
-
error = -ENOMEM;
-
goto out;
-
}
-
devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL);
-
if (!devpath_string) {
-
error = -ENOMEM;
-
goto out;
-
}
-
sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
-
envp[0] = devpath_string;
-
envp[1] = NULL;
-
-
name = dup_name = kstrdup(new_name, GFP_KERNEL);
-
if (!name) {
-
error = -ENOMEM;
-
goto out;
-
}
-
-
error = sysfs_rename_dir(kobj, new_name);
-
if (error)
-
goto out;
-
-
/* Install the new kobject name */
-
dup_name = kobj->name;
-
kobj->name = name;
-
-
/* This function is mostly/only used for network interface.
-
* Some hotplug package track interfaces by their name and
-
* therefore want to know when the name is changed by the user. */
-
kobject_uevent_env(kobj, KOBJ_MOVE, envp);
-
-
out:
-
kfree(dup_name);
-
kfree(devpath_string);
-
kfree(devpath);
-
kobject_put(kobj);
-
-
return error;
-
}
-
EXPORT_SYMBOL_GPL(kobject_rename);
kobject数据结构对象注册:
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...)
-
--lib/kobject.c
-
-
/**
-
* kobject_add - the main kobject add function
-
* @kobj: the kobject to add
-
* @parent: pointer to the parent of the kobject.
-
* @fmt: format to name the kobject with.
-
*
-
* The kobject name is set and added to the kobject hierarchy in this function.
-
* If @parent is set, then the parent of the @kobj will be set to it.
-
* If @parent is NULL, then the parent of the @kobj will be set to the kobject associted with the kset assigned to this kobject. If no
-
* kset is assigned to the kobject, then the kobject will be located in the root of the sysfs tree.
-
*
-
* If this function returns an error, kobject_put() must be called to properly clean up the memory associated with the object.
-
* Under no instance should the kobject that is passed to this function be directly freed with a call to kfree(), that can leak memory.
-
*
-
* Note, no "add" uevent will be created with this call, the caller should set up all of the necessary sysfs files for the object and then
-
* call kobject_uevent() with the UEVENT_ADD parameter to ensure that userspace is properly notified of this kobject's creation.
-
*/
-
-
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...)
-
{
-
va_list args;
-
int retval;
-
-
if (!kobj)
-
return -EINVAL;
-
-
if (!kobj->state_initialized) {
-
printk(KERN_ERR "kobject '%s' (%p): tried to add an "
-
"uninitialized object, something is seriously wrong.\n",
-
kobject_name(kobj), kobj);
-
dump_stack();
-
return -EINVAL;
-
}
-
va_start(args, fmt);
-
retval = kobject_add_varg(kobj, parent, fmt, args);
-
va_end(args);
-
-
return retval;
-
}
-
EXPORT_SYMBOL(kobject_add);
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...)
-
--lib/kobject.c
-
-
/**
-
* kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
-
* @kobj: pointer to the kobject to initialize
-
* @ktype: pointer to the ktype for this kobject.
-
* @parent: pointer to the parent of this kobject.
-
* @fmt: the name of the kobject.
-
*
-
* This function combines the call to kobject_init() and
-
* kobject_add(). The same type of error handling after a call to
-
* kobject_add() and kobject lifetime rules are the same here.
-
*/
-
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
-
struct kobject *parent, const char *fmt, ...)
-
{
-
va_list args;
-
int retval;
-
-
kobject_init(kobj, ktype);
-
-
va_start(args, fmt);
-
retval = kobject_add_varg(kobj, parent, fmt, args);
-
va_end(args);
-
-
return retval;
-
}
-
EXPORT_SYMBOL_GPL(kobject_init_and_add);
2.2 kobj_type结构:
kobject的属性用kobj_type进行表示。
-
--include/linux/kobject.h
-
-
struct kobj_type {
-
void (*release)(struct kobject *kobj); /* kobject资源释放函数,一般需自己实现*/
-
struct sysfs_ops *sysfs_ops; /* kobject属性数组操作集合 */
-
struct attribute **default_attrs; /* kobject默认属性数组 */
-
};
-
--include/linux/sysfs.h
-
-
-
/* FIXME
-
* The *owner field is no longer used.
-
* x86 tree has been cleaned up. The owner
-
* attribute is still left for other arches.
-
*/
-
struct attribute {
-
const char *name; /* 属性的名字,即sys目录中的文件名称 */
-
struct module *owner; /* 属性的拥有者 */
-
mode_t mode; /* 属性的读写权限,定义在include/linux/stat.h*/
-
};
-
--include/linux/sysfs.h
-
-
struct sysfs_ops { /* 对属性的操作函数 */
-
ssize_t (*show)(struct kobject *, struct attribute *,char *); /* 读属性操作函数 */
-
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); /* 写属性操作函数 */
-
};
上述介绍了kobject的默认属性,除了默认属性外,可以对kobject添加一些非默认属性,用来控制kobject代表的总线,设备,驱动的行为。
添加非默认属性,使用如下函数:
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
-
./fs/sysfs/file.c
-
-
-
/**
-
* sysfs_create_file - create an attribute file for an object.
-
* @kobj: object we're creating for.
-
* @attr: attribute descriptor.
-
*/
-
-
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) /*成功返回0,失败返回错误码 */
-
{
-
BUG_ON(!kobj || !kobj->sd || !attr);
-
-
return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
-
-
}
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
-
./fs/sysfs/file.c
-
-
/**
-
* sysfs_remove_file - remove an object attribute.
-
* @kobj: object we're acting for.
-
* @attr: attribute descriptor.
-
*
-
* Hash the attribute name and kill the victim.
-
*/
-
-
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
-
{
-
sysfs_hash_and_remove(kobj->sd, attr->name);
-
}
[备注]:当属性文件删除后,若用户空间的程序仍然在使用该文件描述符时,会造成错误。
2.3 kset结构:
kobject是通过kset组织成层次化的结构。kset是具有相同类型的kobject的集合,存放在/sys/drivers目录下。
-
--include/linux/kobject.h
-
-
/**
-
* struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
-
*
-
* A kset defines a group of kobjects. They can be individually different "types" but overall these kobjects all want to be grouped
-
* together and operated on in the same manner. ksets are used to define the attribute callbacks and other common events that
-
* happen to a kobject.
-
*
-
* @list: the list of all kobjects for this kset
-
* @list_lock: a lock for iterating over the kobjects
-
* @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
-
* @uevent_ops: the set of uevent operations for this kset. These are called whenever a kobject has something happen to it so
-
* that the kset can add new environment variables, or filter out the uevents if so desired.
-
*/
-
struct kset { /* kset是一个kobject的集合,常用来描述一个子系统 */
-
struct list_head list; /* 链接包含所有kobject的对象的链表(双向环型)首部 */
-
spinlock_t list_lock; /* 维护list链表的自旋锁 */
-
struct kobject kobj; /* 内嵌kobject,表明kset本身也是一个目录 */
-
struct kset_uevent_ops *uevent_ops; /* 热插拔事件 */
-
};
kobject的kobj_type指针指向自身的kobj_type,每一个kobject都有一个单独的kobj_type结构体。另外,kset中也有一个kobject,其也关联一个kobj_type.
注意:kset中的kobject关联的kobj_type的优先级高于其包含的各个kobject关联的kobj_type。如果同时存在,优先调用kset中的函数,如果kset中的kobj_type指针为空,才调用各个kobject自身关联的kobj_type.
ket中的uevent_ops是用于处理热插拔事件的。热插拔事件是从内核空间发送到用户空间的通知,表明系统某些部分的配置已经发生了变化。用户空间接收到内核空间的通知后,会调用相应的程序,处理配置的变化。内核将在什么时候产生热插拔事件呢?当驱动程序将kobject注册到设备驱动模型时,会产生热插拔事件。即内核调用kobject_add()和kobject_del()函数时,会产生热插拔事件。热插拔事件产生时,内核会根据kobject的
kset指针找到所属的kset结构体,执行kset中的uevent_ops包含的热插拔函数:
-
--include/linux/kobject.h
-
-
struct kset_uevent_ops {
-
int (*filter)(struct kset *kset, struct kobject *kobj); /* 用于过滤内核是否向用户空间发送事件信号,
-
*若filter返回0,表示不产生事件;若返回1,表示产生事件
-
*/
-
const char *(*name)(struct kset *kset, struct kobject *kobj);
-
int (*uevent)(struct kset *kset, struct kobject *kobj,
-
struct kobj_uevent_env *env);
-
};
kset与kobject,kobj_type的关系图示:
kset相关数据结构关系已经叙述了,下面根据内核操作数据结构对象的流程来看看kset的操作函数:
kset初始化函数:
void kset_init(struct kset *k)
-
--lib/kobjet.c
-
-
/**
-
* kset_init - initialize a kset for use
-
* @k: kset
-
*/
-
void kset_init(struct kset *k) /* 初始化kset中的kobject, list, spinlock */
-
{
-
kobject_init_internal(&k->kobj); /* 调用kobject初始化函数,初始化kset->kobject */
-
INIT_LIST_HEAD(&k->list); /* 初始化kset中的kobject链表 */
-
spin_lock_init(&k->list_lock); /* 初始化kobject 链表的自旋锁 */
-
}
kset注册函数:
int kset_register(struct kset *k)
-
--lib/kobjet.c
-
-
/**
-
* kset_register - initialize and add a kset.
-
* @k: kset.
-
*/
-
int kset_register(struct kset *k)
-
{
-
int err;
-
-
if (!k)
-
return -EINVAL;
-
-
kset_init(k);
-
err = kobject_add_internal(&k->kobj);
-
if (err)
-
return err;
-
kobject_uevent(&k->kobj, KOBJ_ADD);
-
return 0;
-
}
kset注销函数:
void kset_unregister(struct kset *k)
-
--lib/kobject.c
-
-
/**
-
* kset_unregister - remove a kset.
-
* @k: kset.
-
*/
-
void kset_unregister(struct kset *k)
-
{
-
if (!k)
-
return;
-
kobject_put(&k->kobj);
-
}
kset引用计数:
static inline struct kset *kset_get(struct kset *k) 增加引用计数
static inline void kset_put(struct kset *k) 减少引用计数
-
--include/linux/kobject.h
-
static inline struct kset *kset_get(struct kset *k)
-
{
-
return k ? to_kset(kobject_get(&k->kobj)) : NULL;
-
}
-
-
static inline void kset_put(struct kset *k)
-
{
-
kobject_put(&k->kobj);
-
}
阅读(2200) | 评论(0) | 转发(1) |