文章目录
该系列文章阅读顺序:
- linux驱动-设备驱动模型(属性文件 kobject )
- linux驱动-设备驱动模型(kset)
- linux驱动-设备驱动模型(bus总线)
- linux驱动-设备驱动模型(device设备)
- linux驱动-设备驱动模型(driver驱动)
- linux驱动-设备驱动模型(class类)
- linux驱动-设备驱动模型(platform设备)
kset 本身包含 kobject 结构,因此拥有上述 kobject 的所有特性,除此之外, kset 具有管理 kobject 的功能以及提供热插拔功能
作者: baron
一、kset
kset 的数据结构如下
/* include/linux/kobject.h, line 159 */
struct kset {
struct list_head list; // 该Kset下所有的Kobject都被链接进入该节点
spinlock_t list_lock;
struct kobject kobj; // kset也是kobj的一种,因为c语言无法向面向对象一样继承,因此只能使用这种方式。
/*
* 该kset的uevent操作函数集。
* 当任何Kobject需要上报uevent时,都要调用它所从属的kset的uevent_ops,
* 添加环境变量,或者过滤event(kset可以决定哪些event可以上报)。
* 因此,如果一个kobject不属于任何kset时,是不允许发送uevent的。
*/
const struct kset_uevent_ops *uevent_ops;
}
可以看出主要多出了两个数据结构 list 和 kset_uevent_ops,在前面的 kobject_add 分析中可以知道,只要是 kobject 属于某个 kset 那么都会被链接到所属的 kset 中的 list 链表。 也就是 kset 具有管理 kobject 的功能,举个栗子,例如: 当 kernel 关机时, 会在 device_shutdown 中利用 devices_kset->list 找到该链表上的所有设备,并做相应的操作
void device_shutdown(void)
{
struct device *dev, *parent;
spin_lock(&devices_kset->list_lock);
//通过 kset->list 链表找到链表上的所有设备
while (!list_empty(&devices_kset->list)) {
dev = list_entry(devices_kset->list.prev, struct device, kobj.entry);
...... //对所有的设备做相关操作
}
spin_unlock(&devices_kset->list_lock);
}
我们所熟知的热插拔机制的功能也是由 kset 提供的,热插拔指的是当一个设备加入系统,内核如何通知用户空间。感兴趣可以看看这篇文章:http://bbs.chinaunix.net/thread-3678367-1-1.html
热插拔大致的可以分为两个部分组成,内核部分和用户空间部分,而 kset则负责将事件发送到用户空间,而发送的方式则是 uevent。 大致的流程如下
外设接入设备,设备产生中断
内核响应中断,调用 device_add 添加新设备
在device_add中会调用 kobject_uevent 将事件通知给用户空间
由于热插拔的内容相对复杂可以单独成文,而且也不影响对设备模型的理解,因此这里只提一下它的功能,关于热插拔更加详细的内容后面认真学习之后再整理出一篇文章。
1、 kset的创建与注册
1) kset_create
动态获取一个 kset 内存空间,初始化 uevent_ops ,初始化 parent_kobj ,初始化一个内核默认的 kset_ktype ,初始化 kset 中的 kset 为NULL
static struct kset *kset_create(const char *name,
const struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int retval;
kset = kzalloc(sizeof(*kset), GFP_KERNEL); //动态获得一个 kset 内存空间
if (!kset)
return NULL;
retval = kobject_set_name(&kset->kobj, "%s", name); //初始化 name
if (retval) {
kfree(kset);
return NULL;
}
kset->uevent_ops = uevent_ops; //初始化 uenent_ops
kset->kobj.parent = parent_kobj; //初始化 parent_kobj
/*
* The kobject of this kset will have a type of kset_ktype and belong to
* no kset itself. That way we can properly free it when it is
* finished being used.
*/
kset->kobj.ktype = &kset_ktype; //初始化该 kset 的 ktype
kset->kobj.kset = NULL; //kset 的 kset 为空
return kset;
}
2) kset_init
初始化kset的kobj成员,初始化list链表
/**
* kset_init - initialize a kset for use
* @k: kset
*/
void kset_init(struct kset *k)
{
kobject_init_internal(&k->kobj); //初始化kobj成员
INIT_LIST_HEAD(&k->list); //初始化list链表
spin_lock_init(&k->list_lock);
}
3) kset_register
将 kset 注册进入内核
/**
* kset_register - initialize and add a kset.
* @k: kset.
*/
int kset_register(struct kset *k)
{
int err;
if (!k)
return -EINVAL;
kset_init(k); //调用kset_init初始化kobj成员,初始化list链表
err = kobject_add_internal(&k->kobj); //注册kobj即生成kset对应的目录
if (err)
return err;
kobject_uevent(&k->kobj, KOBJ_ADD); //发送uevent事件,KOBJ_ADD
return 0;
}
3) kset_create_and_add
动态创建一个kset结构,并将其注册,其实就是 kset_create 和 kset_register 的组合
struct kset *kset_create_and_add(const char *name,
const struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int error;
kset = kset_create(name, uevent_ops, parent_kobj); //动态创建 kset
if (!kset)
return NULL;
error = kset_register(kset); //注册kset
if (error) {
kfree(kset);
return NULL;
}
return kset;
}
kset 的创建与注册的函数的选择和前面的kobject一样:
当 kset 需要嵌入到更大的数据结构时使用 kset_register
当 kset 不需嵌入到更大的数据结构时使用 kset_create_and_add
2、卸载kset
1) kset_unregister
从卸载一个keset
/**
* kset_unregister - remove a kset.
* @k: kset.
*/
void kset_unregister(struct kset *k)
{
if (!k)
return;
kobject_del(&k->kobj);
kobject_put(&k->kobj);
}
2) kset_put
减少kset的kobj成员引用计数
static inline void kset_put(struct kset *k)
{
kobject_put(&k->kobj);
}