> Linux教程 > linux基础 >

kset_example 初探

LDM以kobject和kset为根本,初步了解了kobject后,继续趁热打铁,对kset做下了解,并对kobject和kest的关系做下了解。

 

内核空间与用户空间的映射关系如下表所示:

内核空间(internel) 用户空间(externel)
内核对象
(kernel objects)
目录
(directories)
对象属性
(object attributes)
普通文件
(regular files)
对象关系
(object relationshiops)
符号链接
(symbolic links)

struct kobject {
 const char  *name;
 struct kref  kref;
 struct list_head entry;
 struct kobject  *parent;
 struct kset  *kset;    //包含kset,网上说kset是kobject的容器,没懂什么意思。。。
 struct kobj_type *ktype;
 struct sysfs_dirent *sd;
 unsigned int state_initialized:1;
 unsigned int state_in_sysfs:1;
 unsigned int state_add_uevent_sent:1;
 unsigned int state_remove_uevent_sent:1;
};

 

 

struct kset {
 struct list_head list;
 spinlock_t list_lock;
 struct kobject kobj;//包含kobject
 struct kset_uevent_ops *uevent_ops;
};

 

先看下kset_example,说不定就明白了

 

依旧是从module_init(example_init);开始:

 

static int example_init(void)
{
 /*
  * Create a kset with the name of "kset_example",
  * located under /sys/kernel/
  */
 example_kset = kset_create_and_add("kset_example", NULL, kernel_kobj);
 if (!example_kset)
  return -ENOMEM;

 /*
  * Create three objects and register them with our kset
  */
 foo_obj = create_foo_obj("foo");
 if (!foo_obj)
  goto foo_error;

 bar_obj = create_foo_obj("bar");
 if (!bar_obj)
  goto bar_error;

 baz_obj = create_foo_obj("baz");
 if (!baz_obj)
  goto baz_error;

 return 0;

baz_error:
 destroy_foo_obj(bar_obj);
bar_error:
 destroy_foo_obj(foo_obj);
foo_error:
 return -EINVAL;
}

首先看第一步:

example_kset = kset_create_and_add("kset_example", NULL, kernel_kobj);//这里的kernel_kobj和kobject_example中的一样,还是sys/kernel目录。可参见ksysfs_init函数。

 

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);
 if (!kset)
  return NULL;
 error = kset_register(kset);
 if (error) {
  kfree(kset);
  return NULL;
 }
 return kset;
}

 

第一步是kset = kset_create(name, uevent_ops, parent_kobj);

第二步是error = kset_register(kset);

生成kset:

static struct kset *kset_create(const char *name,
    struct kset_uevent_ops *uevent_ops,
    struct kobject *parent_kobj)
{
 struct kset *kset;

 kset = kzalloc(sizeof(*kset), GFP_KERNEL);
 if (!kset)
  return NULL;
 kobject_set_name(&kset->kobj, name);
 kset->uevent_ops = uevent_ops;
 kset->kobj.parent = 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->kobj.kset = NULL;

 return kset;
}

 

1、给kset分配空间

2、将kset结构体中的kobject的名字设为“kset_example”.

3、将kset->uevent_ops = uevent_ops;
 kset->kobj.parent = parent_kobj;

 

4、 kset->kobj.ktype = &kset_ktype;

static struct kobj_type kset_ktype = {
 .sysfs_ops = &kobj_sysfs_ops,
 .release = kset_release,
};
5、 kset->kobj.kset = NULL;

 

int kset_register(struct kset *k)
{
 int err;

 if (!k)
  return -EINVAL;

 kset_init(k);
 err = kobject_add_internal(&k->kobj);
 if (err)
  return err;
 kobject_uevent(&k->kobj, KOBJ_ADD);
 return 0;
}

 

1、kset_init(k);

void kset_init(struct kset *k)
{
 kobject_init_internal(&k->kobj);
 INIT_LIST_HEAD(&k->list);
 spin_lock_init(&k->list_lock);
}

kset的初始化包括初始化kset中的kobject,将kset的list初始化。

2、 err = kobject_add_internal(&k->kobj);
将kset结构体中kobject做操作,这个操作和kobject_example中的不大一样:

 

static int kobject_add_internal(struct kobject *kobj)
{
 int error = 0;
 struct kobject *parent;

 if (!kobj)
  return -ENOENT;

 if (!kobj->name || !kobj->name[0]) {
  pr_debug("kobject: (%p): attempted to be registered with empty "
    "name!/n", kobj);
  WARN_ON(1);
  return -EINVAL;
 }

 parent = kobject_get(kobj->parent);

 /* join kset if set, use it as parent if we do not already have one */
 if (kobj->kset) {
  if (!parent)
   parent = kobject_get(&kobj->kset->kobj);
  kobj_kset_join(kobj);
  kobj->parent = parent;
 }

 pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'/n",
   kobject_name(kobj), kobj, __FUNCTION__,
   parent ? kobject_name(parent) : "<NULL>",
   kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");

 error = create_dir(kobj);
 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",
          __FUNCTION__, kobject_name(kobj));
  else
   printk(KERN_ERR "%s failed for %s (%d)/n",
          __FUNCTION__, kobject_name(kobj), error);
  dump_stack();
 } else
  kobj->state_in_sysfs = 1;

 return error;
}

1、parent = kobject_get(kobj->parent);得到kset中的kobject的parent,所以parent指向name为“kernel”的kobject。

2、

 if (kobj->kset) {
  if (!parent)
   parent = kobject_get(&kobj->kset->kobj);
  kobj_kset_join(kobj);
  kobj->parent = parent;
 }

在kobject_example中不用看这个if,在kset_example也不需要看,因为在kset_create中有kset->kobj.kset = NULL;

3、error = create_dir(kobj);//以“kset_example”为名字建立一个目录

4、kobj->state_in_sysfs = 1;//表明该kset中的kobj在sysfs中了

 

最后,kobject_uevent(&k->kobj, KOBJ_ADD);

int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
 return kobject_uevent_env(kobj, action, NULL);
}

//这个目前看不懂,大致理解为如下:

调用kobject_uevent()把这个(添加新设备的)事件,以及相关信息(包括设备的VendorID,DeviceID等信息。)通过netlink发送到用户态中。在用户态的udevd检测到这个事件,就可以根据这些信息,打开/lib/modules/uname-r/modules.alias文件。

 

 

接着是

create_foo_obj("foo");

create_foo_obj("bar");

create_foo_obj("baz");

 

以create_foo_obj("foo");为例:

 

struct foo_obj {
 struct kobject kobj;
 int foo;
 int baz;
 int bar;
};

 

static struct foo_obj *foo_obj;
static struct foo_obj *bar_obj;
static struct foo_obj *baz_obj;

 

static struct foo_obj *create_foo_obj(const char *name)
{
 struct foo_obj *foo;
 int retval;

 /* allocate the memory for the whole object */
 foo = kzalloc(sizeof(*foo), GFP_KERNEL);
 if (!foo)
  return NULL;

 /*
  * As we have a kset for this kobject, we need to set it before calling
  * the kobject core.
  */
 foo->kobj.kset = example_kset;

 /*
  * Initialize and add the kobject to the kernel.  All the default files
  * will be created here.  As we have already specified a kset for this
  * kobject, we don't have to set a parent for the kobject, the kobject
  * will be placed beneath that kset automatically.
  */
 retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);
 if (retval) {
  kfree(foo);
  return NULL;
 }

 /*
  * We are always responsible for sending the uevent that the kobject
  * was added to the system.
  */
 kobject_uevent(&foo->kobj, KOBJ_ADD);

 return foo;
}

 

1、 struct foo_obj *foo;
 foo = kzalloc(sizeof(*foo), GFP_KERNEL);

给新定义的foo_obj分配空间

2、foo->kobj.kset = example_kset;

3、retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);
其中

static struct kobj_type foo_ktype = {
 .sysfs_ops = &foo_sysfs_ops,
 .release = foo_release,
 .default_attrs = foo_default_attrs,
};

 

static struct attribute *foo_default_attrs[] = {
 &foo_attribute.attr,
 &baz_attribute.attr,
 &bar_attribute.attr,
 NULL, /* need to NULL terminate the list of attributes */
};

 

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);

 va_start(args, fmt);
 retval = kobject_add_varg(kobj, parent, fmt, args);
 va_end(args);

 return retval;
}

1、kobject_init(kobj, ktype);---->kobject_init(foo->kobj, foo_ktype)//初始化foo->kobj

2、retval = kobject_add_varg(kobj, parent, fmt, args);

 因为retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);中第三个参数为NULL,所以

parent为NULL。

但是在内核中有这样的注释:

As we have already specified a kset for this
 kobject, we don't have to set a parent for the kobject, the kobject
 will be placed beneath that kset automatically.

 kobject_add_varg中kobj->parent = parent;那就是kobj->parent为NULL

这个时候的kobject_add_varg有些不一样,首先调用kobject_set_name_vargs将名字设为"foo",然后调用

kobject_add_internal(struct kobject *kobj)

 1、parent = kobject_get(kobj->parent);

 2、

 /* join kset if set, use it as parent if we do not already have one */
 if (kobj->kset) {
  if (!parent)
   parent = kobject_get(&kobj->kset->kobj);
  kobj_kset_join(kobj);
  kobj->parent = parent;
 }

 这里,这个if就要跑进去了

既然跑进去了,而前面的kobj->parent为NULL,所以

要跑parent = kobject_get(&kobj->kset->kobj);
所以真正的parent还是被赋值为kset_example这个kset结构体包含的kobject中。这个正好印证了上面的英文注释:

As we have already specified a kset for this
 kobject, we don't have to set a parent for the kobject, the kobject
 will be placed beneath that kset automatically.

 

 所以后面的kobj->parent = parent;就将foo中的kobject和kset_example中的kobject作为父子关系连接起来。

 还有一个重要的一环:

kobj_kset_join(kobj);


/* add the kobject to its kset's list */
static void kobj_kset_join(struct kobject *kobj)
{
 if (!kobj->kset)
  return;

 kset_get(kobj->kset);//计数加1
 spin_lock(&kobj->kset->list_lock);
 list_add_tail(&kobj->entry, &kobj->kset->list);
 spin_unlock(&kobj->kset->list_lock);
}

 

 list_add_tail(&kobj->entry, &kobj->kset->list);  //就是把kobj自身加入到自身的kset的链表里面

 

 /**
 * list_add_tail - add a new entry
 * @new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.

 */
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
 __list_add(new, head->prev, head);
}

 将kobj插入所属的kset的list_head这个双向链表中。

 

 

 

 kobject&kset

 

 

 这样就像上图那样,不过kset child list应该就是list_head这个双向链表,就所有的kobject都串起来。

 

所以会生成下面的目录:

sys/kset_example下有三个目录:

foo、bar和baz

而每个目录下都有三个文件:foo、bar和baz。

 

 

 show函数和kobect_example中的是一样的。

 

kobect和kset有了初步的认识,但对sysfs认识不够深,文件夹和文件都是怎么建立的要慢慢了解。

 

(责任编辑:IT)