加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

创建sysfs节点(三)之kobject_init_and_add

(2019-01-28 22:35:53)
标签:

it

分类: Linux操作系统
转载:kobject和kset的一些学习心得

#include "linux/module.h"
#include "linux/kernel.h"
#include "linux/kobject.h"
#include "linux/sysfs.h"
#include "linux/slab.h"

static struct kobject * parent;
static struct kobject *child;
static struct kset *c_kset;

static unsigned long flag= 1;

static ssize_t att_show(struct kobject *kobj,struct attribute *attr,char *buf)
{
size_t count = 0;

count += sprintf(&buf[count],"%lu\n",flag);
return count;
}

static ssize_t att_store(struct kobject *kobj, struct attribute *attr, const char *buf,size_t count)
{
flag = buf[0]-'0';

//通过kobject_uevent来将内核对象kobj的状态变化通知用户程序
switch(flag)
{
case 0:
kobject_uevent(kobj,KOBJ_ADD);
break;

case 1:
kobject_uevent(kobj,KOBJ_REMOVE);
break;

case 2:
kobject_uevent(kobj,KOBJ_CHANGE);
break;

case 3:
kobject_uevent(kobj,KOBJ_MOVE);
break;

case 4:
kobject_uevent(kobj,KOBJ_ONLINE);
break;

case 5:
kobject_uevent(kobj,KOBJ_OFFLINE);
break;
}
return count;
}

static struct attribute cld_att = {
.name = "cldatt",
.mode = 0777,
};

static const struct sysfs_ops att_ops = {
.show = att_show,
.store = att_store,
};

static struct kobj_type cld_ktype = {
.sysfs_ops = &att_ops,
};

static int kobj_demo_init(void)
{
int err;

parent = kobject_create_and_add("pa_obj",NULL);

child = kzalloc(sizeof(*child),GFP_KERNEL);
if(!child)
return PTR_ERR(child);

//一个能够通知用户空间状态变化的kobject必须隶属于某一个kset,也就是所谓的
//subsystem,所以此处给内核对象child创建一个kset对象c_kset

c_kset = kset_create_and_add("c_kset",NULL,parent);
if(!c_kset)
return -1;

child->kset = c_kset;

err = kobject_init_and_add(child,&cld_ktype,NULL,"cld_obj");
if(err)
return err;

err = sysfs_create_file(child,&cld_att);

return err;
}

static void kobj_demo_exit(void)
{
sysfs_remove_file(child,&cld_att);
kset_unregister(c_kset);
kobject_del(child);
kobject_del(parent);
}

module_init(kobj_demo_init);
module_exit(kobj_demo_exit);
MODULE_LICENSE("GPL");

kobject_create_and_add这个函数首先会调用kobject_create来分配并初始化一个kobject对象,然后调用kobject_add函数在sysfs文件系统中为新生成的kobject对象建立一个新的目录。那么这个目录建立在
sysfs文件系统中的哪个位置呢?kobject_add最后是通过sysfs_craete_dir函数来创建一个目录的,看一下这个函数的关键代码便能知道

//  fs/sysfs/dir.c
int sysfs_create_dir(struct kobject *kobj)
{
...
if(kobj->parent)
parent_sd = kobj->parent->sd;
else
parent_sd = &sysfs_root;
...

error =create_dir(kobj,parent_sd,type,ns,kobject_name(kobj),&sd);
if(!error)
kobj->sd = sd;

return error;
}

可以看到,如果kobj->parent字段为空,那么该函数就会调用create_dir在sysfs文件树的根目录下为kobj创建一个新的目录,否则就在parent的目录下为该kobj创建一个新的目录。
kobject_add首先会将参数parent赋值给kobj的parent成员 kobj->parent =parent,然后调用kobject_add_internal(kobj)函数,在kobject_add_internal函数内部,如果调用kobject_add时parent是一个NULL指针,那么要看该kobj是否在一个kset对象中,如果是就把该kset中的kobject成员作为kobj的parent;否则kobj的parent值仍然为NULL,那么在接下来调用sysfs_create_dir的时候,该kobj就会在/sys目录创建一个新的文件夹。
// struct kobject * kobject_create_and_add(const char * name,struct kobject *parent)

例如上面的kobject_create_and_add("pa_obj",NULL);这个函数传入的parent为空,那么他将会在/sys下创建一个目录名为pa_obj的新目录。
// struct kset *kset_create_and_add(const char *name,struct kset_uevent_ops *uevent_ops,struct kobject *parent_kobj)

再看下面的kset_create_and_add("c_kset",NULL,parent);kset里面也有一个内嵌的kobject,所以传入的第一个和第三个参数是给这个内嵌的kobject用的,第二个参数是给kset用的。

看一下kset的结构体原形:
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
struct kset_uevent_ops *uevent_ops;
};

多么简单,其实kset在sysfs文件系统中没有实体表现的,他不像kobject,一个kobject对应一个sysfs文件系统下的一个目录,kset的存在无法就是为了方便管理kobject。
kset_create_and_add最终也会调用kobject_create_and_add,传入的参数肯定是""c_kset"和parent,那么将会在/sys/pa_obj的目录下面创建一个目录名为c_kset的新目录.

再来看下面的:
child->kset = c_kset;

err = kobject_init_and_add(child,&cld_ktype,NULL,"cld_obj");

kobject_init_and_add与kobject_create_and_add的其中一个区别便是前者可以使用自己定义的kobj_type,而后者内核会提供一个默认的kobj_type,(读者自己深入函数内部就会看到这个默认的kobj_type)。

这个函数原型为:int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,struct kobject *parent, const char *fmt, ...)

上面的程序在调用这个函数的时候把parent设置为NULL,由于在调用这个函数之前有这么一行:child->kset = c_kset,如上所分析的那样,由于设置了child所属的kset,那么在kobject_init_and_add内部会把child->parent设置为c_kset->kobj,这意味着将会在/sys/pa_obj/c_kset目录下
创建一个目录名为cld_obj的新目录,如果你把这kobject_init_and_add参数中的NULL改为parent(parent = kobject_create_and_add("pa_obj",NULL);

这个parent就是pa_obj的对象kobj),那么这么一改,cld_obj目录就和c_kset目录一样都是在/sys/pa_obj目录之下了。

再下面的sysfs_create_file是在相应的目录下面创建一个属性文件(注意,现在是创建文件而不是目录了)上面几个函数是构建linux设备模型框架的最基本的函数。。如果说linux设备模型是一栋摩天大厦,那么这几个函数就是把这个大厦建立起来的最底层的建筑工人。
   
=============================================================

首先看了下/Documentation/kobject.txt文档中的说明,然后结合sample/kobject/kobject-example.c

对kobject做一些初步了解。

在内核配置中将kobject-example配置成model编译成.ko加载到内核之中。

在insmod kobject-example.ko时会用到

module_init(example_init);

而example_init函数如下:


static int example_init(void)
{
 int retval;


  * Create a simple kobject with the name of "kobject_example",
  * located under /sys/kernel/
  *
  * As this is a simple directory, no uevent will be sent to
  * userspace.  That is why this function should not be used for
  * any type of dynamic kobjects, where the name and number are
  * not known ahead of time.

 example_kobj = kobject_create_and_add ("kobject_example", kernel_kobj);
 if (!example_kobj)
  return -ENOMEM;


 // Create the files associated with this kobject 
 retval = sysfs_create_group (example_kobj, &attr_group);
 if (retval)
  kobject_put (example_kobj);

 return retval;
}

kobject_create_and_add ("kobject_example", kernel_kobj);
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
 struct kobject *kobj;
 int retval;

 kobj = kobject_create();
 if (!kobj)
  return NULL;

 retval = kobject_add(kobj, parent, "%s", name);
 if (retval) {
  printk(KERN_WARNING "%s: kobject_add error: %d/n",
         __FUNCTION__, retval);
  kobject_put(kobj);
  kobj = NULL;
 }
 return kobj;
}

 

在kobject_create_and_add函数中,

首先创建一个kobject: kobj = kobject_create();

 

struct kobject *kobject_create(void)
{
    struct kobject *kobj;
    kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
    if (!kobj)
        return NULL;

    kobject_init(kobj, &dynamic_kobj_ktype);
    return kobj;
}

在kobject_create函数中首先给kobj分配空间,然后用kobject_init函数初始化kobj,最后返回kobj。

而在kobject_init函数中,

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

 先判断kobj和ktype不为null,如果kobj->state_initialized==1(已经被初始化过了),进行错误处理。

最关键的是调用kobject_init_internal(kobj)和将kobj->ktype = ktype;

这里ktype是dynamic_kobj_ktype,我们将kobj->ktype 赋为dynamic_kobj_ktype

 

static struct kobj_type dynamic_kobj_ktype = {
 .release = dynamic_kobj_release,
 .sysfs_ops = &kobj_sysfs_ops,
};

 

struct sysfs_ops kobj_sysfs_ops = {
 .show = kobj_attr_show,
 .store = kobj_attr_store,
};

 

一个release方法用于释放kobject占用的资源;一个sysfs ops指针指向sysfs操作表和一个sysfs文件系统缺省属性列表。Sysfs操作表包括两个函数store()和show()。当用户态读取属性时,show()函数被调用,该函数编码指定属性值存入buffer中返回给用户态;而store()函数用于存储用户态传入的属性值。

 

以static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
 struct kobj_attribute *kattr;
 ssize_t ret = -EIO;

 kattr = container_of(attr, struct kobj_attribute, attr);
 if (kattr->show)
  ret = kattr->show(kobj, kattr, buf);
 return ret;
}

为例子,当cat foo、bar、baz时候,调用kobj_attr_show函数,这里,kobj_attr_show就会调用foo_show函数。
//这个是kobj_type定义的,与文章最后相关的补充一下。

//特别补充一下container_of的介绍,这个在kernel中用的相当多:

container_of(ptr, type, member)

kattr = container_of(attr, struct kobj_attribute, attr);
通过结构中的某个变量获取结构本身的指针

在这里,是通过kobj_attribute结构体的attr变量获得结构体本身的指针:kattr。

 

在kobject_init_internal(kobj)中,

 

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;//这个地方就是前面的是否被初始化的判断
}

kobject的create动作完成后,我们再回到kobject_create_and_add函数中的retval = kobject_add(kobj, parent, "%s", name);

其中name这个字符串指针指着字符串“kobject_example”,而parent是指向kernel_kobj这个kobject的指针,其实kernel_kobj就应该是

sysfs下的kernel目录。

kobject_add->kobject_add_varg->1、kobject_set_name_vargs;2、kobj->parent = parent;3kobject_add_internal(kobj);

第一步是设置kobject的名字,即为kobj->name = name;将“kobject_example”这个名字赋给我们的kobject;

第二步将kobj->parent = parent,可以理解为kobject_example的上级为kernel目录。

第三步仔细看下:

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;
    //坚持kobject的name

 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) : "",
   kobj->kset ? kobject_name(&kobj->kset->kobj) : "");

 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);得到新建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;
 }

//这里没有kset,先不看这里,在kset_example文件中再分析这里。

3、error = create_dir(kobj);//以“kobject_example”为名字建立一个目录 //这个函数没懂,以后再看。

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

 

那么,到这里,kobject_example就被加到sysfs中了,

下面一步就是:

sysfs_create_group(example_kobj, &attr_group);//这个函数还不完全懂

简单的记录一下:

attribute_group 的定义:

struct attribute_group {
 const char  *name;
 int   (*is_visible)(struct kobject *,
           struct attribute *, int);
 struct attribute **attrs;
};

static struct attribute_group attr_group = {
 .attrs = attrs,
};

 

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

 


static struct kobj_attribute foo_attribute =
 __ATTR(foo, 0666, foo_show, foo_store);

static struct kobj_attribute baz_attribute =
 __ATTR(baz, 0666, b_show, b_store);
static struct kobj_attribute bar_attribute =
 __ATTR(bar, 0666, b_show, b_store);

定义了foo_attribute    baz_attribute   bar_attribute

因为

#define __ATTR(_name,_mode,_show,_store) { /
 .attr = {.name = __stringify(_name), .mode = _mode }, /
 .show = _show,     /
 .store = _store,     /
}
所以

foo_attribute这个结构体中的attr结构体的name成员为foo,mode成员为0666,foo_attribute的成员函数

show是foo_show,成员函数store是foo_store。

 

在sysfs_create_group中会添加三个文件,在kobject_example目录中有foo,baz和bar三个文件。

对文件作echo 1 > foo 和 cat foo的操作,就会调用foo_show和foo_store函数。

  

总结:在kobject_example 模块注册后,sys/kernel/kobject_example目录创建了,里面有三个文件foo、baz和bar。

这三个文件时属性文件。

对三个文件进行echo 1 > foo 和 cat foo的操作,会调用想用的show、store操作。

疑问在于:create_file、create_dir这些函数的具体实现是怎么样的?这个以后循序渐进去理解。


==============================================================
#include "linux/init.h"
#include "linux/module.h"
#include "linux/kobject.h"
#include "linux/sysfs.h"
#include "linux/string.h"

static int hello_value;

static ssize_t hello_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
   return sprintf(buf, "%d\n", hello_value);
}

static ssize_t hello_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
   sscanf(buf, "%du", &hello_value);
   return count;
}

static struct kobj_attribute hello_value_attribute = __ATTR(hello_value, 0666, hello_show, hello_store);

static struct kobject *hellowold_kobj;

static int __init helloworld_init(void)
{
   int retval;

   helloworld_kobj = kobject_create_and_add("helloworld", kernel_kobj);
   if (!helloworld_kobj)
return -ENOMEM;

   retval = sysfs_create_file(helloworld_kobj, &hello_value_attribute);
   if (retval)
      kobject_put(helloworld_kobj);

   return retval;
}

static void __exit helloworld_exit(void)
{
   kobject_put(helloworld_kobj);
}

module_init(helloworld_init);
module_exit(helloworld_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHER(doublenian.xie@gmail.com);

一、运行结果:

root@thiz:/sys/kernel/hellowrold# ls
hello_value
root@thiz:/sys/kernel/helloworld# echo 1 > hello_value
root@thiz:/sys/kernel/hellowrold# cat hello_value
1

二、分析
1、目录项(/sys/kernel/helloworld):通过函数kobject_create_and_add("helloworld", kernel_kobj)可以在/sys下建立一个helloworld目录项。
2、属性文件(hello_value):通过函数sysfs_create_file(helloworld_kobj, &hello_value_attribute)建立。这个也同时建立了文件与操作之间的联系和对应。
3、操作(hello_show、hello_store):在sys系统中对文件的操作有2个函数,一个是show,一个是store,这两个函数和普通文件的read和write函数有点类似,是他们的精简版。对于sprintf和sscanf是对copy_from_user和copy_to_user函数的封装。
4、如果一组属性则可以如下
static struct attribute *attrs [] = {
   &foo_attribute.attr,
   &baz_attribuet.attr,
   &bar_attribute.attr,
   NULL,      // need to NULL terminate the list of attributes
};

static struct attribute_group attr_group = {
   .attrs = attrs,
};

retval = sysfs_create_group(example_kobj, &attr_group);



0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有