linux驱动-设备驱动模型(kset)

文章详细介绍了kset在Linux驱动中的作用,包括kset的创建(kset_create、kset_init、kset_register、kset_create_and_add)和卸载(kset_unregister、kset_put)过程。kset作为kobject的集合,用于管理kobject并提供热插拔功能,其uevent_ops用于处理设备事件。文章还简要提及了热插拔机制的基本流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


该系列文章阅读顺序:

  1. linux驱动-设备驱动模型(属性文件 kobject )
  2. linux驱动-设备驱动模型(kset)
  3. linux驱动-设备驱动模型(bus总线)
  4. linux驱动-设备驱动模型(device设备)
  5. linux驱动-设备驱动模型(driver驱动)
  6. linux驱动-设备驱动模型(class类)
  7. 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值