linux学习之道-Kobject、Kset 和 Subsystem

难以理解的一部分——kobject的驱动模型

抽象建立在它之上——没有明显的开始的地方

本文代码查阅是Linux 3.4.0

Kobjects结构体

include/linux/kobject.h

struct kobject {
 const char  *name;    /*指向含有容器名称的字符*/

 struct list_head entry;    /*kobject 之间的双向链表,与所属的kset形成环形链表*/
 struct kobject  *parent;    /*在sysfs分层结构中定位对象,指向上一级kset中的struct kobject kobj*/
 struct kset  *kset;    /*指向所属的kset*/
 struct kobj_type *ktype;    /*负责对该kobject类型进行跟踪的struct kobj_type的指针*/
 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;    /*如果设置了uevent_suppress字段,说明不希望产生事件,忽略事件正确返回*/

}

kobject是struct kobject类型的一个对象。Kobjects有个名字和一个引用计数。一个父指针(允许kobject对象被安排成层次结构)一个特定的类型,通常,表示在sysfs虚拟文件系统。一个kobject对自身并不感兴趣,它存在的意义在于把高级对象连接到设备模型上。因此内核代码很少(甚至不知道)创建一个单独的 kobject;而kobject 被用来控制对大型域(domain)相关对象的访问,所以kobject 被嵌入到其他结构中。kobject 可被看作一个最顶层的基类,其他类都它的派生产物。 kobject 实现了一系列方法,对自身并没有特殊作用,而对其他对象却非常有效。

Kobject初始化:
/**
 * 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) {
  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);
 kobj->ktype = ktype;
 return;
error:
 printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
 dump_stack();
}
static void kobject_init_internal(struct kobject *kobj)
{
 if (!kobj)
  return;
 kref_init(&kobj->kref);
 INIT_LIST_HEAD(&kobj->entry);
 kobj->state_in_sysfs = 0;
 kobj->state_add_uevent_sent = 0;
 kobj->state_remove_uevent_sent = 0;
 kobj->state_initialized = 1;
}
kobj_type:
struct kobj_type {
 void (*release)(struct kobject *kobj);    //释放函数(驱动编写时提供),此函数会被kobject_put函数调用
 struct sysfs_ops *sysfs_ops;    //属性文件的操作函数(只有读和写操作)
 struct attribute **default_attrs;    //属性数组
};
讨论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 {
    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函数可在热插拔程序执行前,向环境变量写值
};


你可能感兴趣的:(Linux)