一。Kobject
每个在内核中注册的kobject都对应于sysfs文件系统中的一个目录。
kobject在文件include/linux/kobject中定义
- struct kobject {
- const char *name; //kobject的名称
- struct list_head entry; //kobject结构链表
- struct kobject *parent; //父kobject结构体
- struct kset *kset; //kset集合
- struct kobj_type *ktype; //kobject的类型描述符
- struct sysfs_dirent *sd; //sysfs文件目录
- struct kref kref; //kobject引用计数
- unsigned int state_initialized:1; //kobject是否初始化
- unsigned int state_in_sysfs:1; //是否已经加入sysfs
- unsigned int state_add_uevent_sent:1;
- unsigned int state_remove_uevent_sent:1;
- unsigned int uevent_suppress:1;
- };
sysfs组织结构,进入sysfs目录中。有block bus class dev devices firmware fs kernel module power这些目录。具体代表看名字差不多就可以看出。在层次结构上,假如有一个设备A。将有一个名称为A的目录。A设备是在B总线上。那A设备应该在bus目录下的B总线下。A设备肯定会有设备的属性(ktype),例如是音频设备则应该有音量属性,则音量属性将在A设备目录下有个音量属性文件。在使用设备时,如果要改变音量大小,则可以写属性文件入音量指。得到音量大小时,可以读取属性文件中的音量值。
二。Kobject初始化
初始化一个kobject结构体变量,kobject_init函数(lib/kobject.c),调用此函数前应先将kobject变量成员全部置0
- /**
- * 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 then
- * 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) { //检查kobj变量是否为空
- err_str = "invalid kobject pointer!";
- goto error;
- }
- if (!ktype) { //检查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); //进一步初始化kobj内部成员
- kobj->ktype = ktype; //将参数中传来的ktype变量赋值给kobj的ktype变量。
- return;
- error:
- printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
- dump_stack();
- }
分析kobject_init_internal函数(lib/kobject.c),此函数主要设置一些kobj中的一些变量
- static void kobject_init_internal(struct kobject *kobj)
- {
- if (!kobj) //kobj是否为空
- return;
- kref_init(&kobj->kref); //增加kobject的引用计数,kref_set(kref, 1);
- INIT_LIST_HEAD(&kobj->entry); //初始化kobj的链表
- kobj->state_in_sysfs = 0; //kobject还没有注册到sysfs中
- kobj->state_add_uevent_sent = 0; //
- kobj->state_remove_uevent_sent = 0;
- kobj->state_initialized = 1;
- }
三。kobj_type
对象的属性结构体kobj_type(include/linux/kobject.h)
- struct kobj_type {
- void (*release)(struct kobject *kobj); //释放函数(驱动编写时提供),此函数会被kobject_put函数调用
- struct sysfs_ops *sysfs_ops; //属性文件的操作函数(只有读和写操作)
- struct attribute **default_attrs; //属性数组
- };
1.讨论kobj_type和kobject的关系,就要先说说kobject的引用。引用一个kobject使用函数kobject_get()这个函数会增加kobject的引用并返回kobject的指针。增加其引用是通过其kobject中断哦kref变量完成的。对kobject的引用管理主要是为了知道被引用的情况,如引用不为0就不能销毁kobject对象,引用为0时则调用相应的释放函数等。
- struct kobject *kobject_get(struct kobject *kobj)
- {
- if (kobj)
- kref_get(&kobj->kref);
- return kobj;
- }
- void kref_get(struct kref *kref)
- {
- WARN_ON(!atomic_read(&kref->refcount));
- atomic_inc(&kref->refcount); //将kref中的这个原子变量加1
- smp_mb__after_atomic_inc();
- }
减少一个kobject对象的引用使用函数kobject_put()。当一个kobject对象的引用被减少到0时,程序就应该释放这个kobject相关的资源。所以在减少引用的函数中就应该有调用释放资源的相关代码,在下面内核代码中我们可以看到。
- void kobject_put(struct kobject *kobj)
- {
- if (kobj) {
- if (!kobj->state_initialized) //若kobj没有初始化就不能减少其引用
- 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); //减少kref计数
- }
- }
- int kref_put(struct kref *kref, void (*release)(struct kref *kref))
- {
- WARN_ON(release == NULL); //为空警告
- WARN_ON(release == (void (*)(struct kref *))kfree); //如果release函数就是kfree,则警告(即release函数不能是简单的kfree)
- if (atomic_dec_and_test(&kref->refcount)) { //递减原子变量并检查其值
- release(kref); //回调release函数
- return 1;
- }
- return 0;
- }
那这个release函数在哪里保存呢,这就和kobj_type结构有关系了。上面我们可以看到kobj_type中有一个release函数指针,就是保存在这里。每一个kobject的ktype都指向一个kobj_type,它保存了这个kobject类型的release函数指针。
四。Kset集合
1.Kset是具有相同类型的kobject集合。一个Kset集合可以表示在/sys/drivers/目录下,表示一类驱动程序。kobject则表示一个具体的驱动目录。这样kset则可以将kobject组织成层次化结构。
- /**
- * 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 {
- struct list_head list; //这个链表存放这个kset关联的所有kobject
- spinlock_t list_lock; //维护此链表的锁
- struct kobject kobj; //内嵌的kobject。这样kset本身也是一个kobject也被表现为一个目录
- struct kset_uevent_ops *uevent_ops; //支持热插拔事件的函数集
- };
kset中的kobject对象,所有属于这个kset集合的kobject对象的parent指针,均指向这个内嵌的kobject,也即表示在一个kset集合中的kobject是相同类型的他们有相同的parent对象。kset的引用计数也就是内嵌的kobject的引用计数。
所以kobject和kset的关系简单来讲,就是
1.kset是kobject的一个顶层容器,它包含了相同类型的kobject,kset中有链表成员保存所有的kobject指向。
2.kobject中的kset指针指向了一个kset
3.kset中有kobject对象,表明了kset也可以有kobject相关的操作。
4.kset链表中的kobject对象的parent指针一般都指向kset内嵌的kobject对象。
kset和kobject关系基本如下图,
kset_uevent_ops热插拔事件
热插拔事件是用内核空间发送到用户空间的通知。表明内核中的某些配置已经发生变化。用户空间则会根据这些信息做相应的处理。例如,U盘插入USB系统时,会产生一个热插拔事件,内核会捕捉到这个热插拔事件,然后调用/sbin/hotplug程序,该程序通知加载驱动程序来相应U盘的插入动作。
热插拔函数集的定义在include/linux/koject.h中
- struct kset_uevent_ops {
- int (*filter)(struct kset *kset, struct kobject *kobj); //事件过滤函数
- const char *(*name)(struct kset *kset, struct kobject *kobj); //事件名称函数
- int (*uevent)(struct kset *kset, struct kobject *kobj,struct kobj_uevent_env *env);
- //uevent函数可在热插拔程序执行前,向环境变量写值
- };
详细的热插拔事件先不在这里分析。
2.注册一个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);
- err = kobject_add_internal(&k->kobj); //将kset中的kobject添加进sysfs,函数将在后面讲解
- if (err)
- return err;
- kobject_uevent(&k->kobj, KOBJ_ADD);
- return 0;
- }
- /**
- * kset_init - initialize a kset for use
- * @k: kset
- */
- void kset_init(struct kset *k)
- {
- kobject_init_internal(&k->kobj);
- INIT_LIST_HEAD(&k->list);
- spin_lock_init(&k->list_lock);
- }
我们使用函数kset_create_and_add()还可以一次性将kset创建并注册进sysyfs
- /**
- * kset_create_and_add - create a struct kset dynamically and add it to sysfs
- *
- * @name: the name for the kset
- * @uevent_ops: a struct kset_uevent_ops for the kset
- * @parent_kobj: the parent kobject of this kset, if any.
- *
- * This function creates a kset structure dynamically and registers it
- * with sysfs. When you are finished with this structure, call
- * kset_unregister() and the structure will be dynamically freed when it
- * is no longer being used.
- *
- * If the kset was not able to be created, NULL will be returned.
- */
- struct kset *kset_create_and_add(const char *name,
- 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注册进sysfs,函数在上面已经分析过
- if (error) {
- kfree(kset);
- return NULL;
- }
- return kset;
- }
- /**
- * kset_create - create a struct kset dynamically
- *
- * @name: the name for the kset
- * @uevent_ops: a struct kset_uevent_ops for the kset
- * @parent_kobj: the parent kobject of this kset, if any.
- *
- * This function creates a kset structure dynamically. This structure can
- * then be registered with the system and show up in sysfs with a call to
- * kset_register(). When you are finished with this structure, if
- * kset_register() has been called, call kset_unregister() and the
- * structure will be dynamically freed when it is no longer being used.
- *
- * If the kset was not able to be created, NULL will be returned.
- */
- static struct kset *kset_create(const char *name,
- struct kset_uevent_ops *uevent_ops,
- struct kobject *parent_kobj)
- {
- struct kset *kset;
- int retval;
- kset = kzalloc(sizeof(*kset), GFP_KERNEL);
- if (!kset)
- return NULL;
- retval = kobject_set_name(&kset->kobj, name); //设置kobject名称
- if (retval) {
- kfree(kset);
- return NULL;
- }
- kset->uevent_ops = uevent_ops;
- kset->kobj.parent = parent_kobj; //设置kset的kobject的父对象
- /*
- * 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的kobject的默认属性
- kset->kobj.kset = NULL;
- return kset;
- }
上面这些函数主要包含即调用关系如下,
kset_create_and_add
kset_create
kzalloc
kset_register
kset_init
kobject_add_internal
kset中嵌入了一个kobject,所以还有一些和kobject相似的函数如,
增加kset的引用,实际是调用kobject_get增加kset中的kobject的引用
- static inline struct kset *kset_get(struct kset *k)
- {
- return k ? to_kset(kobject_get(&k->kobj)) : NULL;
- }
减少kset的引用,实际是调用kobject_put减少kset中的kobject的引用
- static inline void kset_put(struct kset *k)
- {
- kobject_put(&k->kobj);
- }
顺便提一下,子系统subsystem,在新的内核中已经没有这个结构了。在原来的内核中它用来表示比kset更高一层的容器,kset应该属于一个子系统,子系统帮助内核在分层结构中定位kset。内核子系统包括 block_subsys(/sys/block 块设备)、 devices_subsys(/sys/devices 核心设备层)。现在subsystem已经被kset代替了。
五,将kobject注册进sysfs系统
我们在看一下如何将kobject注册进sysfs系统中。使用函数kobject_init_and_add()(lib/kobject.c)函数将一个kobject注册进sysfs系统,在/sys中表现为生成一个相应的目录。
- /**
- * 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); //调用初始化函数先初始化kobject变量
- va_start(args, fmt); //解析可变参数列表
- retval = kobject_add_varg(kobj, parent, fmt, args); //给kobject添加参数,并且将其添加到sysfs系统。
- va_end(args); //结束解析参数列表
- return retval;
- }
- static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs)
- {
- int retval;
- retval = kobject_set_name_vargs(kobj, fmt, vargs); //设置kobject的名称
- if (retval) {
- printk(KERN_ERR "kobject: can not set name properly!\n");
- return retval;
- }
- kobj->parent = parent; //设置kobject的父kobject
- return kobject_add_internal(kobj); //添加kobject
- }
- static int kobject_add_internal(struct kobject *kobj)
- {
- int error = 0;
- struct kobject *parent;
- if (!kobj) //检查是否为空
- return -ENOENT;
- if (!kobj->name || !kobj->name[0]) { //kobj是否有名称,如果没有则不能注册,生成目录。
- WARN(1, "kobject: (%p): attempted to be registered with empty "
- "name!\n", kobj);
- return -EINVAL;
- }
- parent = kobject_get(kobj->parent); //获得父kobject,并增加父kobject的引用计数
- /* join kset if set, use it as parent if we do not already have one */
- if (kobj->kset) { //是否有kset集合
- if (!parent) //如果没有父kobject则用kset中的kobject对象
- parent = kobject_get(&kobj->kset->kobj);
- kobj_kset_join(kobj); //将kobject添加进它关联的kset的list链表中。
- kobj->parent = parent; //设置父koject
- }
- pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
- kobject_name(kobj), kobj, __func__,
- parent ? kobject_name(parent) : "",
- kobj->kset ? kobject_name(&kobj->kset->kobj) : "");
- error = create_dir(kobj); //创建kobject的相应目录
-
- if (error) { //创建时出错处理
- kobj_kset_leave(kobj);
- kobject_put(parent);
- kobj->parent = NULL;
- /* be noisy on error issues */
- if (error == -EEXIST)
- printk(KERN_ERR "%s failed for %s with "
- "-EEXIST, don't try to register things with "
- "the same name in the same directory.\n",
- __func__, kobject_name(kobj));
- else
- printk(KERN_ERR "%s failed for %s (%d)\n",
- __func__, kobject_name(kobj), error);
- dump_stack();
- } else
- kobj->state_in_sysfs = 1; //标记为已经注册进sysfs
- return error;
- }
sysfs创建目录函数create_dir,在lib/kobject.c
- static int create_dir(struct kobject *kobj)
- {
- int error = 0;
- if (kobject_name(kobj)) {
- error = sysfs_create_dir(kobj); //在sysfs中创建目录,将来有时间了可以分析下sysfs子系统。
- if (!error) {
- error = populate_dir(kobj);
- if (error)
- sysfs_remove_dir(kobj);
- }
- }
- return error;
- }
以上函数的主要调用关系,如下
kobject_init_and_add
kobject_init
kobject_add_varg
kobject_add_internal
create_dir
还有一个函数kobject_add,也可以添加一个kobject,它只是没有kobject_init这一步。
- 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;
- }
阅读(1171) | 评论(0) | 转发(0) |