• linux设备模型:kset及设备驱动抽象类(class)分析


      上一篇<>中分析了ksysfs子系统是以kobj结构对象为基础,并在基础上扩展出各种特征,如关联kobj_ktype结构,用于对文件的权限、读写操作等,而文件以kernfs结构为基础生成(以attribute相关的属性结构关联/绑定)等内容,本篇继续分析设备模型kobject的容器kset及设备驱动抽象类(class)。

    kset属于kobject属于特定子系统的一组特定类型的kobject,与其说是一组特定类型,倒不如说用kset表示一组特定类型的kobject更方便与管理、遍历等操作,实际场景中如果需要以某个根为目标的多组特定类型的kobject,可以编写复合型kset容器,以根kset容器为基础(实际还是根kobject),在它之后继续关联多个不同的kset容器,当然,这也是linux内核中设备驱动的现状。

      kset容器相对于kobject来说,主要多了kobjs的uevent操作,用于记录、过滤用户层通知事件等内容,uevent内部通过netlink与用户通信,它采用net通信体系。

    class属于设备驱动抽象框架,它内部由subsys_private(用于将private保存到bustype/class结构的驱动程序核心部分),attribute_group(类的默认属性、设备的默认属性等),dev_uevent(当设备被添加、从此类中移除或其他一些生成uevent以添加环境变量的事情时调用)等核心组件构成。

      class_kset用于类(class)的热插拔事件转到类子系统(如/sys/class/目录下,加载(显示)某个class的kobj文件或释放等等)。

      从某种意义上来说,class的出现主要用于解决设备、驱动、设备电源等核心组件之间的代码冗余问题,它通过融合方式对设备、驱动、设备电源等核心组件进行关联,提供一个统一的class结构(接口),实际还是回到了以kobject(内核对象)为基础的目的,并且大大增加了不同组件之间交互的灵活性。除此之外,class延续使用kobject的属性、类型(ktype)等概念,也降低了class的分析及使用难度。


    目录


    1. 函数分析

    1.1 classes_init

    1.2 class_create

    2. 源码结构

    3. 部分结构定义

    4. 扩展函数/变量


    1. 函数分析

    1.1 classes_init

      创建class_kset容器结构对象,设置根kobj名称,关联kobjs的uevent操作(如果存在)等,然后通过kset_register函数 初始化sysfs对象的引用计数、链表对象及一些变量标志,kobj对象关联父指针(这里它是根节点(NULL),通过发送uevent通知用户空间(如果设置了uevent),kobj向用户空间发送环境缓冲内容

      class_kset 用于类的热插拔事件转到类子系统(如/sys/class/目录下,加载(显示)某个class的kobj文件或释放等等)

    int __init classes_init(void)
    {
            class_kset = kset_create_and_add("class", NULL, NULL); // 创建kset容器结构对象,设置根kobj名称,关联kobjs的uevent操作(如果存在)等,然后通过kset_register函数 初始化sysfs对象的引用计数、链表对象及一些变量标志,kobj对象关联父指针(这里它是根节点(NULL),通过发送uevent通知用户空间(如果设置了uevent),kobj向用户空间发送环境缓冲内容
            if (!class_kset)
                    return -ENOMEM;
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    kset_create_and_add


    1.2 class_create

      class_create 创建class,owner可以设置THIS_MODULE(代表这个模块,自己),也可以设置父class表示它的子级

      class实际上是一种抽象框架,它内部由subsys_private(用于将private保存到bustype/class结构的驱动程序核心部分),attribute_group(类的默认属性、设备的默认属性等),dev_uevent(当设备被添加、从此类中移除或其他一些生成uevent以添加环境变量的事情时调用)等核心组件构成

    #define class_create(owner, name)		\  // owner可以设置THIS_MODULE(代表这个模块,自己),也可以设置父class表示它的子级
    ({						\
    	static struct lock_class_key __key;	\
    	__class_create(owner, name, &__key);	\
    })
    ||
    \/
    struct class *__class_create(struct module *owner, const char *name,
                                 struct lock_class_key *key)
    {
            struct class *cls;
            int retval;
    
            cls = kzalloc(sizeof(*cls), GFP_KERNEL); // 为class分配对象 cls
            if (!cls) {
                    retval = -ENOMEM;
                    goto error;
            }
    
            cls->name = name; // class 名称
            cls->owner = owner; // class根(或父class)
            cls->class_release = class_create_release; // 类释放函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    class

     retval = __class_register(cls, key); // 注册class,class实际上是一种抽象框架,它内部由subsys_private(用于将private保存到bustype/class结构的驱动程序核心部分),attribute_group(类的默认属性、设备的默认属性等),dev_uevent(当设备被添加、从此类中移除或其他一些生成uevent以添加环境变量的事情时调用)等核心组件构成
            if (retval)
                    goto error;
    
            return cls;
    
    error:
            kfree(cls);
            return ERR_PTR(retval);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    __class_register


    2. 源码结构

      kset_ktype 动态注册的sysfs的ktype(用于释放对象,kobj访问sysfs文件等等)

    tatic struct kobj_type kset_ktype = {
            .sysfs_ops      = &kobj_sysfs_ops, // sysfs操作结构对象
            // 读取(显示,kobj_attr_show)、写入(存储,kobj_attr_store)默认函数
            
            .release        = kset_release, // 释放kobject对象
            .get_ownership  = kset_get_ownership, // 获取kobj的父级对象的sysfs所有权数据
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    kset_get_ownership

      kobject_actions kobj状态

    static const char *kobject_actions[] = {
            [KOBJ_ADD] =            "add",
            [KOBJ_REMOVE] =         "remove",
            [KOBJ_CHANGE] =         "change",
            [KOBJ_MOVE] =           "move",
            [KOBJ_ONLINE] =         "online",
            [KOBJ_OFFLINE] =        "offline",
            [KOBJ_BIND] =           "bind",
            [KOBJ_UNBIND] =         "unbind",
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

      class_ktype 动态注册的sysfs的ktype(用于释放对象,访问sysfs文件等等)

    static struct kobj_type class_ktype = {
            .sysfs_ops      = &class_sysfs_ops, // 读取(显示)、写入(存储)函数
            .release        = class_release, // 释放class对象
            .child_ns_type  = class_child_ns_type, // 通过kobj对象指针经过偏移计算,获得指向的class对象指针指向的命令空间类型指针
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    class_child_ns_type


    3. 部分结构定义

      kset_uevent_ops kset与它相关的kobjects的uevent操作

    struct kset_uevent_ops {
    	int (* const filter)(struct kobject *kobj); // 允许kset阻止一个特定kobject的uevent被发送到用户空间
    	// 如果该函数返回0,该uevent将不会被发送出去
    	
    	const char *(* const name)(struct kobject *kobj); // 覆盖uevent发送到用户空间的kset的默认名称
    	// 默认情况下,该名称将与kset本身相同
    	
    	int (* const uevent)(struct kobject *kobj, struct kobj_uevent_env *env); // 当uevent即将被发送至用户空间时,uevent函数将被调用
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

      kset 属于特定子系统的一组特定类型的kobject

    /*
     * kset定义了一组kobject
     * 它们可以是单独不同的“类型”,但总体而言,
     * 这些kobject都希望分组在一起,并以相同的方式进行操作
     * kset用于定义发生在kobject上的属性回调和其他常见事件
     * /
    struct kset {
    	struct list_head list; // 此kset的所有kobject的列表
    	spinlock_t list_lock; // 用于迭代kobjects的锁
    	struct kobject kobj; // 这个kset的嵌入式kobject,递归
    	const struct kset_uevent_ops *uevent_ops; // kset的uevent操作
    	// 只要kobject发生了什么事情,就会调用这些函数,
    	// 以便kset可以添加新的环境变量,或者根据需要过滤掉uevent
    } __randomize_layout;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

      uevent_sock 用户空间事件套接字

    struct uevent_sock {
    	struct list_head list; // 链表
    	struct sock *sk; // 套接字
    };
    
    • 1
    • 2
    • 3
    • 4

      class 设备类

    struct class {
    	const char		*name; // 名称
    	struct module		*owner; // 模块所有者
    
    	const struct attribute_group	**class_groups; // 该类的默认属性
    	const struct attribute_group	**dev_groups; // 该类的设备的默认属性
    	struct kobject			*dev_kobj; // 表示该类并将其链接到层次结构中的kobject
    
    	int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env); // 当设备被添加、从此类中移除或其他一些生成uevent以添加环境变量的事情时调用
    	char *(*devnode)(struct device *dev, umode_t *mode); // 回调以提供devtmpfs
    
    	void (*class_release)(struct class *class); // 调用以释放该类
    	void (*dev_release)(struct device *dev); // 调用以释放设备
    
    	int (*shutdown_pre)(struct device *dev); // 在驱动程序关闭前的关闭时间调用
    
    	const struct kobj_ns_type_operations *ns_type; // 回调,以便sysfs可以确定命名空间
    	const void *(*namespace)(struct device *dev); // 设备的命名空间
    
    	void (*get_ownership)(struct device *dev, kuid_t *uid, kgid_t *gid); // 允许类为属于该类的设备指定sysfs目录的uid/gid
    	// 通常绑定到设备的命名空间
    
    	const struct dev_pm_ops *pm; // 此类的默认设备电源管理操作
    
    	struct subsys_private *p; // 驱动核心的私有数据,除了驱动核心之外,任何人都不能碰这个
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

      subsys_private 用于将private保存到bustype/class结构的驱动程序核心部分

    struct subsys_private {
    	struct kset subsys; // 此子系统的结构kset
    	struct kset *devices_kset; // 子系统的“设备”目录
    	struct list_head interfaces; // 关联的子系统接口列表
    	struct mutex mutex; // 保护设备和接口列表
    
    	struct kset *drivers_kset; // 关联的驱动程序列表
    	struct klist klist_devices; // 在devices_kset对象上迭代的klist
    	struct klist klist_drivers; // 在drivers_kset对象上迭代的klist
    	struct blocking_notifier_head bus_notifier; // 总线通知程序列表,用于显示任何与此总线上的事情有关的内容
    	
    	unsigned int drivers_autoprobe:1; // 驱动默认自动探索
    	struct bus_type *bus; // 此结构关联的结构bus_type的指针
    
    	struct kset glue_dirs; // 此结构关联的结构bus_type的指针
    	struct class *class; // 此结构关联的结构类的指针
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4. 扩展函数/变量

      kset_create_and_add 创建kset容器结构对象,设置根kobj名称,关联kobjs的uevent操作(如果存在)等,然后通过kset_register函数 初始化sysfs对象的引用计数、链表对象及一些变量标志,kobj对象关联父指针(这里它是根节点(NULL),通过发送uevent通知用户空间(如果设置了uevent),kobj向用户空间发送环境缓冲内容

    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容器结构对象,设置根kobj名称,关联kobjs的uevent操作(如果存在)等
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    kset_create

    error = kset_register(kset); // 初始化sysfs对象的引用计数、链表对象及一些变量标志,kobj对象关联父指针(这里它是根节点(NULL),通过发送uevent通知用户空间(如果设置了uevent),kobj向用户空间发送环境缓冲内容
            if (error) {
                    kfree(kset);
                    return NULL;
            }
            return kset;
    }
    EXPORT_SYMBOL_GPL(kset_create_and_add);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    kset_register

      kset_create 创建kset容器结构对象,设置根kobj名称,关联kobjs的uevent操作(如果存在)等

      kset容器也可以按根节点、子节点形式进行多个kset容器关联,每个kset可以表示一组特定类型的kobj,组成一种复合型kset容器,当然它们将以根kobj代表kset进行管理

    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);  为kset容器中的根kobj设置名称,如"class"
            if (retval) {
                    kfree(kset);
                    return NULL;
            }
            kset->uevent_ops = uevent_ops; // kset与它相关的kobjects的uevent操作
            kset->kobj.parent = parent_kobj; // 如果存在,为根kobj赋值父kobj,它可能用于多个kset容器关联
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    kset_uevent_ops
    kset

    		kset->kobj.ktype = &kset_ktype;
            kset->kobj.kset = NULL;
    
            return kset;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    kset_ktype

      kset_get_ownership 获取kobj的父级对象的sysfs所有权数据

    static void kset_get_ownership(struct kobject *kobj, kuid_t *uid, kgid_t *gid)
    {
            if (kobj->parent)
                    kobject_get_ownership(kobj->parent, uid, gid);
    }
    ||
    \/ 
    void kobject_get_ownership(struct kobject *kobj, kuid_t *uid, kgid_t *gid) // 获取kobj对象的sysfs所有权数据
    {
            *uid = GLOBAL_ROOT_UID; // 0
            *gid = GLOBAL_ROOT_GID; // 0
    
            if (kobj->ktype->get_ownership)
                    kobj->ktype->get_ownership(kobj, uid, gid);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

      kset_register 初始化sysfs对象的引用计数、链表对象及一些变量标志,kobj对象关联父指针(这里它是根节点(NULL),通过发送uevent通知用户空间(如果设置了uevent),kobj向用户空间发送环境缓冲内容

    int kset_register(struct kset *k)
    {
            int err;
    
            if (!k)
                    return -EINVAL;
    
            kset_init(k); // 初始化sysfs对象的引用计数、链表对象及一些变量标志
            err = kobject_add_internal(&k->kobj); //  kobj对象关联父指针(这里它是根节点(NULL),如果与其它kset容器关键,应该会有父指针),并加入到kset容器(链表)中
            // 然后通create_dir 获取kobj对象的sysfs所有权数据,分配kernfs节点(父级kn,如目录),加入安全策略(父节点存在的情况下),将kn链接到同级rbtree,更新哈希值及时间戳,并激活这个节点......
    
    		 kobject_uevent(&k->kobj, KOBJ_ADD); // 通过发送uevent通知用户空间(如果设置了uevent),kobj向用户空间发送环境缓冲内容
            return 0;
    }
    EXPORT_SYMBOL(kset_register);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    kobject_uevent

      kobject_uevent kobj向用户空间发送环境缓冲内容

    int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                           char *envp_ext[])
    {
            struct kobj_uevent_env *env;
            const char *action_string = kobject_actions[action]; // kobj状态对应的字符串
            const char *devpath = NULL;
            const char *subsystem;
            struct kobject *top_kobj;
            struct kset *kset;
            const struct kset_uevent_ops *uevent_ops;
            int i = 0;
            int retval = 0;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    kobject_actions

    		/*
    		 * 不管结果如何,将“移除”事件标记为已完成,
    		 * 因为某些子系统不希望通过自动清理重新触发“移除”
    		 * /
    		if (action == KOBJ_REMOVE)
                    kobj->state_remove_uevent_sent = 1;
    
            pr_debug("kobject: '%s' (%p): %s\n",
                     kobject_name(kobj), kobj, __func__);
    
    		/* 搜索我们所属的kset */
    		top_kobj = kobj;
            while (!top_kobj->kset && top_kobj->parent)
                    top_kobj = top_kobj->parent;
    
    		kset = top_kobj->kset;
            uevent_ops = kset->uevent_ops;
    
    		/* 如果设置了uevent_suppress,则跳过事件 */
    		if (kobj->uevent_suppress) {
                    pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
                                     "caused the event to drop!\n",
                                     kobject_name(kobj), kobj, __func__);
                    return 0;
            }
    
    		/* 如果过滤器返回零,则跳过该事件 */
    		if (uevent_ops && uevent_ops->filter)
                    if (!uevent_ops->filter(kobj)) {
                            pr_debug("kobject: '%s' (%p): %s: filter function "
                                     "caused the event to drop!\n",
                                     kobject_name(kobj), kobj, __func__);
                            return 0;
                    }
    
    		/* 始发子系统 */
    		if (uevent_ops && uevent_ops->name)
                    subsystem = uevent_ops->name(kobj);
            else
                    subsystem = kobject_name(&kset->kobj);
            if (!subsystem) {
                    pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
                             "event to drop!\n", kobject_name(kobj), kobj,
                             __func__);
                    return 0;
            }
    
    		/* 环境缓冲区 */
            env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
    
    		/* 完整对象路径 */
            devpath = kobject_get_path(kobj, GFP_KERNEL);
            // 从当前kobj对象记录路径,向父级kobj递归,得到完整路径,如/test -> /class/test -> /sys/class/test
    
    		/* default keys */
            retval = add_uevent_var(env, "ACTION=%s", action_string); // 将键值字符串添加到环境缓冲区
            if (retval)
                    goto exit;
            retval = add_uevent_var(env, "DEVPATH=%s", devpath);
            if (retval)
                    goto exit;
            retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
            if (retval)
                    goto exit;
    
    		/* keys passed in from the caller */
            if (envp_ext) {
                    for (i = 0; envp_ext[i]; i++) {
                            retval = add_uevent_var(env, "%s", envp_ext[i]);
                            if (retval)
                                    goto exit;
                    }
            }
    
    		/* 让特定于kset的函数添加它的环境缓冲区 */
            if (uevent_ops && uevent_ops->uevent) {
                    retval = uevent_ops->uevent(kobj, env);
                    if (retval) {
                            pr_debug("kobject: '%s' (%p): %s: uevent() returned "
                                     "%d\n", kobject_name(kobj), kobj,
                                     __func__, retval);
                            goto exit;
                    }
            }
    
    		switch (action) {
            case KOBJ_ADD:
                    /*
                     * 标记“添加”事件,这样我们可以确保在自动清理期间将“删除”事件传递给用户空间
                     * 如果对象确实发送了一个“add”事件,那么如果调用方尚未完成,
                     * 那么核心将自动生成“remove”
                     */
                    kobj->state_add_uevent_sent = 1;
                    break;
    				
    		case KOBJ_UNBIND:
                    zap_modalias_env(env);
                    break;
    
            default:
                    break;
            }
    
    		mutex_lock(&uevent_sock_mutex);
            /* 我们将发送一个事件,因此请求一个新的序列号 */
            retval = add_uevent_var(env, "SEQNUM=%llu", ++uevent_seqnum);
            if (retval) {
                    mutex_unlock(&uevent_sock_mutex);
                    goto exit;
            }
    
    		retval = kobject_uevent_net_broadcast(kobj, env, action_string,
                                                  devpath); // kobj发送广播内容,环境缓冲内容
            mutex_unlock(&uevent_sock_mutex);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114

    kobject_uevent_net_broadcast

    ...
    exit:
            kfree(devpath);
            kfree(env);
            return retval;
    }
    EXPORT_SYMBOL_GPL(kobject_uevent_env);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

      kobject_uevent_net_broadcast kobj发送广播内容,环境缓冲内容

    static int kobject_uevent_net_broadcast(struct kobject *kobj,
                                            struct kobj_uevent_env *env,
                                            const char *action_string,
                                            const char *devpath)
    {
            int ret = 0;
    
    #ifdef CONFIG_NET
    		...
    		/* 
    		 * kobjects目前只携带网络名称空间标签,并且它们是这里唯一相关的标签,
    		 * 因为我们想决定将uevent广播到哪个网络名称空间.
             */
            if (ops && ops->netlink_ns && kobj->ktype->namespace)
                    if (ops->type == KOBJ_NS_TYPE_NET)
                            net = kobj->ktype->namespace(kobj); // 网络命名空间
    
    		if (!net)
                    ret = uevent_net_broadcast_untagged(env, action_string,
                                                        devpath); // 发送未加标签的广播(组ID=1)
                    // 从nl_table[sk->sk_protocol]数组中获取对应协议类型的监听对象(如果存在),
                    // 然后向sk_buff对象指针中拷贝环境缓冲内容,
                    之后赋值到netlink_skb_parms对象指针中,通过广播形式(netlink)发送
            else
                    ret = uevent_net_broadcast_tagged(net->uevent_sock->sk, env,
                                                      action_string, devpath); // 发送到指定用户空间的广播
    #endif
    
            return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

      __class_register 注册class,class实际上是一种抽象框架,它内部由subsys_private(用于将private保存到bustype/class结构的驱动程序核心部分),attribute_group(类的默认属性、设备的默认属性等),dev_uevent(当设备被添加、从此类中移除或其他一些生成uevent以添加环境变量的事情时调用)等核心组件构成

    int __class_register(struct class *cls, struct lock_class_key *key)
    {
            struct subsys_private *cp;
            int error;
    
            pr_debug("device class '%s': registering\n", cls->name);
    
            cp = kzalloc(sizeof(*cp), GFP_KERNEL); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    subsys_private

    		klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put); // 初始化klist结构,get/put函数用于初始化获取和释放klist_node结构中记录的设备
            INIT_LIST_HEAD(&cp->interfaces); // 初始化关联的子系统接口列表
            kset_init(&cp->glue_dirs); //  初始化kset对象,此结构关联的结构bus_type的指针
            __mutex_init(&cp->mutex, "subsys mutex", key); // 初始化互斥锁,保护设备和接口列表
            error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name); // 为kset容器中的根kobj设置名称,如"class"
    
    • 1
    • 2
    • 3
    • 4
    • 5

    klist_init
    klist_class_dev_get
    klist_class_dev_put

    /* 为此类设备设置默认的/sys/dev目录 */
            if (!cls->dev_kobj)
                    cls->dev_kobj = sysfs_dev_char_kobj; // 字符设备kobj
    
    #if defined(CONFIG_BLOCK)
            /* 让块类目录显示在sysfs的根目录中 */
            if (!sysfs_deprecated || cls != &block_class)
                    cp->subsys.kobj.kset = class_kset; // 关联class_kset,用于更新类的热插拔事件(如加载(显示)某个class的kobj文件或释放等等)
    #else
            cp->subsys.kobj.kset = class_kset;
    #endif
    		cp->subsys.kobj.ktype = &class_ktype; // 关联class_ktype,动态注册的sysfs的ktype(用于释放对象,访问sysfs文件等等)
            cp->class = cls; // 关联class
            cls->p = cp; // 关联subsys_private,用于将private保存到bustype/class结构的驱动程序核心部分
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    class_ktype

    error = kset_register(&cp->subsys); // 初始化并添加kset
    // kobj对象关联父指针,并加入到kset容器(链表)中,然后通create_dir 获取kobj对象的sysfs所有权数据,分配kernfs节点(父级kn,如目录)...,通过发送uevent通知用户空间(如果设置了uevent),kobj向用户空间发送环境缓冲内容
    
    error = class_add_groups(class_get(cls), cls->class_groups); // 给定一个目录kobj,创建一组属性组
    // 获取kobj对象的sysfs所有权数据,创建kernfs_node节点及命名空间 (父级kn,如目录),然后为属性组->属性列表中的属性分配kernfs_node节点及初始化(子级kn,如目录/文件),包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree,更新哈希值及时间戳,并激活这个节点(属性列表中的节点)
    
    class_put(cls); // 减少class对象的引用计数
    return error;
    }
    EXPORT_SYMBOL_GPL(__class_register);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

      klist_init 初始化klist结构,get/put函数用于初始化获取和释放klist_node结构中记录的设备

    /*
     * 嵌入对象的get函数(如果没有,则为NULL)
     * 嵌入对象的put函数(如果没有,则为NULL)
     *
     * 初始化klist结构
     * 如果klist_node结构将嵌入到refcounted对象中(安全删除所必需的),
     * 那么get/put参数将用于初始化获取和释放嵌入对象上的引用的函数
     */             
    void klist_init(struct klist *k, void (*get)(struct klist_node *),
                    void (*put)(struct klist_node *))
    {
            INIT_LIST_HEAD(&k->k_list);
            spin_lock_init(&k->k_lock);
            k->get = get;
            k->put = put;
    }
    EXPORT_SYMBOL_GPL(klist_init);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

      klist_class_dev_get klist_node中获取保存的设备,并且递增设备的引用计数

    static void klist_class_dev_get(struct klist_node *n)
    {
            struct device *dev = klist_class_to_dev(n); // klist_node中获取保存的设备(to_device_private_class(n)->device)
    
            get_device(dev); // 递增设备的引用计数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      klist_class_dev_put klist_node中获取保存的设备,并且递减设备的引用计数

    static void klist_class_dev_put(struct klist_node *n)
    {                       
            struct device *dev = klist_class_to_dev(n); // klist_node中获取保存的设备(to_device_private_class(n)->device)
                                 
            put_device(dev); // 递减引用计数
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      class_child_ns_type 通过kobj对象指针经过偏移计算,获得指向的class对象指针指向的命令空间类型指针

    static const struct kobj_ns_type_operations *class_child_ns_type(struct kobject *kobj)
    {
            struct subsys_private *cp = to_subsys_private(kobj); // 通过kobj指针偏移计算出subsys_private结构对象指针
            // #define to_subsys_private(obj) container_of(obj, struct subsys_private, subsys.kobj)
    		// container_of 函数相当于从subsys_private结构的 subsys.kobj(成员) <=> obj对象指针,地址偏移到subsys_private结构对象 cp
    		// obj 实际的对象指针
    		// struct subsys_private 逻辑的结构(不存在实体),也是要获取的结构对象指针
    		// subsys.kobj 逻辑的结构(不存在实体),它是struct subsys_private的成员subsys(struct kset subsys),struct kset的成员kobj(struct kobject kobj) <=> 表示obj的逻辑对象
    		// 这里有一点需要注意,container_of的第一个参数需要比第三个参数的指针等级高一介
    		// 如container_of的第三个逻辑参数是一级指针,而第一个实体参数需要二级指针
    		
            struct class *class = cp->class; 
    
            return class->ns_type; // 从class对象中获取命令空间类型指针
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
  • 相关阅读:
    169. 多数元素
    Docker启动mysql服务
    为什么独立站卖家都开始做社交媒体营销?原来客户转化率能提高这么多!
    OSG编程指南<二十三>:基于OSG+ImGui制作模型编辑器,实现三轴方向的实时平移、旋转和缩放变化
    BUUCTF test_your_nc
    IB幼儿课程怎么理解?
    postgresql 配置文件 与 修改配置如何启用
    HtmlCss光标(插入符caret)透明隐藏光标 221106笔记
    css基本样式之背景样式
    第十三届蓝桥杯c++b组2022年国赛决赛题解
  • 原文地址:https://blog.csdn.net/a29562268/article/details/127679057