Linux设备驱动模型之底层数据结构

本文简单介绍构成Linux设备驱动模型的最底层的数据结构:kobject、kset和kobj_type等等。kobject是组成设备模型的基本结构,在sysfs中显示的每一个对象,都对应着一个kobject,而kset是一组具有某种相似性的kobject的合集。每个kobject都需要一个相应的kobj_type结构。

 

一、kobject

    不多废话,首先看kobject的定义( <linux/kobject.h> ):

 

 struct kobject {
 const char  *name;
 struct list_head entry;
 struct kobject  *parent;
 struct kset  *kset;
 struct kobj_type *ktype;
 struct sysfs_dirent *sd;
 struct kref  kref;
 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;
 unsigned int uevent_suppress:1;
};

 

    捡几个相对来说比较重要的成员来说明下:

@name:kobject对象的名字,显示在sysfs下

@entry:内核用该字段将属于相同kset的不同的kobject链接起来

@parent:kobject层次依靠此字段得以实现,parent指向当前kobject的上一层

@kset:指向所属的kset

@ktype:指向kobject的类型,每个kobject都必须有一个类型

@kref:对象的引用计数依靠此字段得以实现

 

1、kobject的初始化
    一个结构体只有被正确地初始化之后才能正确地使用它。kobject也不例外,要使用kobject之前,必须对其进行初始化,内核提供kobject_init函数来初始化kobject:


        void kobject_init(struct kobject *kobj, struct kobj_type *ktype);


    该函数初始化设置kobject的引用计数为1、将kobject的所属类型指向参数ktype(kobj->ktype = ktype)
         

2、对引用计数的操作

    操作kobject的一个重要的函数是为包含它的对象设置引用计数,只要对象的引用计数不为0,那么对象就不能被释放。底层控制kobject引用计数的函数有:

 

        struct kobject *kobject_get(struct kobject *kobj);

        void kobject_put(struct kobject *kobj);

 

    对kobject_get的成功调用将增加kobject的引用计数(+1),并返回指向kobject的指针。当不需要使用kobject的时候,调用

kobject_put来减少其引用计数(-1),并且当引用计数为0时,kobject_put还会调用kobject_cleanup来释放该对象。另外,注意

kobject_init初始化时设置kobject的引用计数为1。

 

3、kobject的添加、删除、移动

    内核提供三个函数用于将一个kobject添加到kobject层次中去:

 

   int kobject_add(struct kobject *kobj, struct kobject *parent,const char *fmt, ...);
   int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...);
   struct kobject *kobject_create_and_add(const char *name, struct kobject *parent);

 

    在正确地初始化kobject之后(即成功调用kobject_init之后),就可以调用函数kobject_add将kobject加入到kobject层次中去,参数的意思也很明显,这里就不浪费时间说明参数了。

    如果参数parent != NULL,那么kobj->parent = parent;如果parent == NULL,那么kobj->parent将被设置为该kobj所属的kset的kobject,如果该kobj还未确定所属kset,则该kobj将被置于sysfs树的根部。还是看一下下面的代码片段,以加深理解:
        ...
        kobj->parent = parent;//设置kobj的parent为参数parent
        ...
        struct kobject *parent;
        parent = kobject_get(kobj->parent);//增加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加入到其所属kset的list链表中
      kobj->parent = parent;
     }
     ...

    kobject_create_and_add动态创建了一个kobject结构体,将其初始化,将其加入到kobject层次中,并最终返回所创建的kobject的指针,当然如果函数执行失败,则返回NULL;而在调用kobject_init_and_add之前,kobject结构体必须已经创建好,动态创建或者静态声明均可。这两个函数可以理解为kobject_init和kobject_add的包裹函数。

 

    内核还提供一个kobject_del函数用于将一个kobject从kobject层次中移除,即从sysfs中移除:

 

        void kobject_del(struct kobject *kobj);

 

    该函数将kobject从sysfs中移除,设置kobj->state_in_sysfs为0(表明kobj不在sysfs中),调用kobj_kset_leave将kobj与相应的kset脱离关联(即将kobj从其对应的kset的list链表中删除),对kobj的parent调用kobject_put以减少对其parent的引用计数,最后将

kobj的parent置为NULL。

 

    内核提供kobject_move函数用于将一个kobject从一个层次移动到另一个层次:

 

        int kobject_move(struct kobject *kobj, struct kobject *new_parent);

 

    参数意义也很明显,需要注意的是,new_parent可以为NULL。关于该函数,这里暂不详细看了。

 

二、kobj_type

    kobject是隐藏在sysfs虚拟文件系统背后的机制,sysfs中的每个目录,都对应着一个kobject,每个kobject都输出若干属性,这些属性在sysfs中表现为文件,是sysfs树的叶子节点。那么如何理解kobject和sysfs之间的关系?

1、sysfs中的一个目录对应一个kobject。当成功调用kobject_add时,将在sysfs中创建一个目录。通常这个目录下包含若干属性,即包含若干文件;

2、分配给kobject的名字(kobject->name)就是sysfs中目录的名字;

3、LDD3 P369 这条楞没看懂啥意思......

 

    说了这么多,也该看看kobj_type的定义了:

        struct kobj_type {
         void (*release)(struct kobject *kobj);
         struct sysfs_ops *sysfs_ops;
         struct attribute **default_attrs;
        };

    当kobject的引用计数为0时,就会释放该kobject的资源。那么,如何释放、调用哪个函数进行释放?答案就是调用    kobj_type结构的release成员。因此,每个kobject都必须有一个release方法,并且需要将其保存在kobj_type的release成员中。

    另外,当创建kobject的时候,都会给每个kobject一系列默认属性。这些属性保存在kobj_type结构中。

    default_attrs指向该类型kobject的默认属性列表,先看下attribute的定义:
        struct attribute {
          char * name;
          struct module * owner;
          mode_t mode;
        };

    attribute,即属性,它以文件的形式输出到sysfs的目录当中,在kobject对应的目录下面,是sysfs目录树的叶子节点,文件
名就是name。

    default_attrs数组说明了都有哪些属性,但是没有告诉sysfs如何实现这些属性,这个任务交给了sysfs_ops成员,它指向的结构定义如下:

struct sysfs_ops {
 ssize_t (*show)(struct kobject *, struct attribute *,char *);
 ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};

    sysfs_ops指针指向sysfs操作表,sysfs操作表包括两个函数store()和show()。当用户态读取属性时,show()函数被调用,该函数编码指定属性值存入buffer中返回给用户态,而store()函数用于存储用户态传入的属性值。

 

    下面举个kobject.c中的例子来说明kobject和kobj_type如何关联起来的。从kobject_init函数的参数就可以看出,对一个kobject进行初始化时,需要提供此kobject所属的类型,即提供kobj_type结构,因此首先要准备kobj_type结构,说白了就是要准备release函数、sysfs_ops结构等。

 

/* default kobject attribute operations */
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;
}

static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
          const char *buf, size_t count)
{
 struct kobj_attribute *kattr;
 ssize_t ret = -EIO;

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

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

 

static void dynamic_kobj_release(struct kobject *kobj)
{
 pr_debug("kobject: (%p): %s/n", kobj, __func__);
 kfree(kobj);
}

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

 

struct kobject *kobject_create(void)
{
 struct kobject *kobj;

 kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
 if (!kobj)
  return NULL;

 kobject_init(kobj, &dynamic_kobj_ktype);//将kobj与kobj_type关联起来
 return kobj;
}
   

    从上述代码中可以看出,如果是动态创建的kobject,那么该kobject的类型就指向dynamic_kobj_ktype。对该结构的理解还有待加深,以后再慢慢看吧......


三、kset
   kset是一组kobject的集合,其内的每个kobject都可以有不同的类型,即有不同的kobj_type,但是一个kset内的所有kobject总是希望以同样的方式被操作。看下kset的定义:

 

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

 

@list:kset包含的kobject的链表

@list_lock:操作list所需的锁

@kobj:一个kset其实也是一个kobject

@uevent_ops:该kset对应的热插拔操作集合。对于热插拔相关的,此处不多介绍,因为无碍主要结构的把握,并且我也不是很懂......

 

    先看一下kset和kobject的关系,如下图:


    kset总是在sysfs中出现,一旦设置了kset并把它添加到系统中,将在sysfs中创建一个目录。kset中的每一个kobject成员(kset->list链表上的成员)都将在sysfs中得到表述。       

 

    kset拥有与kobject类似的初始化和设置接口,如下:

void kset_init(struct kset *k);

int kset_register(struct kset *k);

void kset_unregister(struct kset *k);

struct kset *kset_create_and_add(const char *name,
     struct kset_uevent_ops *uevent_ops,
     struct kobject *parent_kobj);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值