Linux设备驱动程序系列(二) 字符设备驱动程序(2)

本文深入探讨Linux 2.6内核中的kobject设备管理机制,它是构建设备模型的核心,与sysfs文件系统关联。kobject通过kset形成层次结构,常用于控制对特定领域对象的访问。文章介绍了kobject的结构、功能以及如何通过container_of宏获取包含kobject的结构体指针。

前几天回家转了转,跟爸妈聊到深夜,嗬嗬,最后爸妈推荐了一部电视剧《青春期撞上更年期》,所以回来以后,电视剧就开看啦,不过我觉得这不算是浪费时间吧,好的电视剧能给人不少感悟、感动和成长。

下面开始继续我的笔记···

 上次写到struct cdev,这次接着从struct kobject开始记录:

Linux 2.6引入新的设备管理机制kobject,通过这个数据结构使所有的设备在底层都具有统一的接口,kobject提供基本的对象管理,是构成Linux2.6设备模型的核心结构,它与sysfs文件系统紧密关联,每个在内核中注册的kobject对象都对应与sysfs文件系统的一个目录。多花一些时间来研究kobject是十分有意义的工作。下面我仔细研究了一下:

Kobject通常通过kset组织成层次化的结构,kset是具有相同类型的kobject的集合。在Linux 2.6.30内核

linux/include/linux/kobject.h中查看struct kobject结构体的实现如下:

   1:  struct kobject {
   2:        const char  *name;   //名称
   3:       struct list_head entry; 
   4:       struct kobject  *parent;//父指针
   5:      struct kset *kset;//kobject对象的集合体
   6:      struct kobj_type *ktype; //kobject对象被关联到的一种特殊的类型
   7:      struct sysfs_dirent *sd; 
   8:      struct kref  kref;  //引用计数通过此结构体实现的
   9:      unsigned int state_initialized:1;
  10:      unsigned int state_in_sysfs:1;
  11:      unsigned int state_add_uevent_sent:1;
  12:      unsigned int state_remove_uevent_sent:1;
  13:      unsigned int uevent_suppress:1;
  14:  };

Kobject 是基础的结构, 它保持设备模型在一起. 初始地它被作为一个简单的引用计数, 但是它的责任已随时间增长, 并且因此有了它自己的战场. struct kobject 所处理的任务和它的支持代码现在包括:

 

对象的引用计数

 

常常, 当一个内核对象被创建, 没有方法知道它会存在多长时间. 一种跟踪这种对象生命周期的方法是通过引用计数. 当没有内核代码持有对给定对象的引用, 那个对象已经完成了它的有用寿命并且可以被删除.

 

sysfs 表示

 

在 sysfs 中出现的每个对象在它的下面都有一个 kobject, 它和内核交互来创建它的可见表示.

 

数据结构粘和

 

设备模型是, 整体来看, 一个极端复杂的由多级组成的数据结构, 各级之间有许多连接. kobject 实现这个结构并且保持它在一起.

 

热插拔事件处理

 

kobject 子系统处理事件的产生, 事件通知用户空间关于系统中硬件的来去.

你可能从前面的列表总结出 kobject 是一个复杂的结构. 这可能是对的. 通过一次看一部分, 但是, 是有可能理解这个结构和它如何工作的.                     摘自 ——《Linux设备驱动程序(第三版)》361页

在我们进入细节前, 值得花些时间理解如何使用 kobjects. 而且过程中会发现很多有意思的东西。如果你看被 kobjects 处理的函数列表, 你会看到它们都是代表其他对象进行的服务. 一个 kobject, 换句话说, 对其自己很少感兴趣; 它存在仅仅为了结合一个高级对象到设备模型。

因此, 对于内核代码它很少(甚至不知道)创建一个孤立的 kobject; 相反, kobject 被用来控制存取更大的, 特定域的对象.。

为此, kobject 被嵌入到其他结构中. 如果你习惯以面向对象的术语考虑事情, kobject 可被看作一个顶级的, 抽象类, 其他的类自它而来. 一个 kobject 实现一系列功能, 这些功能对自己不是特别有用而对其他对象是好的. C 语言不允许直接表达继承, 因此其他的技术 -- 例如将一个结构嵌入另一个。(看到这大家可能有点糊涂,怀疑C语言真的有这么厉害么?其实C语言真的很厉害,以前我看过一本《C算法》(第一卷)的书,很棒,里面有很多实例都是用C语言实现面向对象编程。)

在上一篇记录中有一个十分关键的结构体:

   1:  struct cdev {
   2:              struct kobject kobj;
   3:              struct module *owner;  //所属模块
   4:              const struct file_operations *ops;
   5:          //文件操作结构,在写驱动时,其结构体内的大部分函数要被实现
   6:              struct list_head list;
   7:              dev_t dev;//设备号,int 类型,高12位为主设备号,低20位为次设备号
   8:              unsigned int count;
   9:  };

      

           从中可以看到,里面嵌入了kobject结构体,如果需要使用该结构体,只需要访问kobject成员就能获得嵌入的kobject对象。但是我们在使用kobject代码时经常遇到相反的问题:对于给定的一个kobject指针,如何获取包含它的结构体指针呢?必然要抛弃一些比较简单的想法(比如你假设kobject处于包含其结构体的开始位置),此时怎么办呢?Linux黑客们帮我们写了一个十分给力的宏,也许学C语言的时候,你还没发现宏的好处,这里我们研究一下这个有意思的宏:container_of宏

在linux内核中经常用到container_of,它被定义在:
linux/include/linux/kernel.h中,原型为:

1.    /**
2.    * container_of - cast a member of a structure out to the containing structure
3.    * @ptr: the pointer to the member.
4.    * @type: the type of the container struct this is embedded in.
5.    * @member: the name of the member within the struct.
6.    *
7.    */
8.    #define container_of(ptr, type, member) ({ /
9.     const typeof( ((type *)0)->member ) *__mptr = (ptr);    /
10.     (type *)( (char *)__mptr - offsetof(type,member) );})
 
指针ptr指向结构体type中的成员member;通过指针ptr,返回结构体type的起始地址。
这个宏一看就很厉害吧,下面具体说说实现的原理。

              type

        |-----------------|

        |                 |

        |                 |

        |-----------------|

  ptr-->| member ---------|

        |-----------------|

        |                 |

        |                 |

其中offsetof宏定义在[include/linux/stddef.h]中定义为:

   1:  1.    #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

这里,要说明的是,((size_t) &((TYPE *)0)->MEMBER)把0地址转化为TYPE结构的指针, 然后获取该结构中MEMBER成员的指针,并将其强制转换为size_t类型。于是,由于结构从0地址开始定义,因此,这样求出的MEMBER成员地址,实际上就是它在结构中的偏移量。

分析可知__mptr指向的是一个type结构里typeof(((type *)0)->member)类型member成员的指针,offsetof(type,member)是这个成员在结构中的偏移,单位是字节,所以为了计算type结构的起始地址,__mptr减去它自己的偏移。

至此应该明白这个宏的意思了,它十分关键,以后将常常使用。以后要讲的open函数中就将使用这个宏。

下面看下struct ktype结构体:

   1:  struct kobj_type {
   2:   void (*release)(struct kobject *kobj);  //kobject引用计数减至0时要调用的析构函数
   3:   struct sysfs_ops *sysfs_ops; //描述了sysfs文件读写时的特性
   4:   struct attribute **default_attrs; //定义了该kobject相关的默认属性
   5:  };

struct kset 的实现如下:

   1:  struct kset {
   2:   struct list_head list;  //连接该集合(kset)中所有kobject对象
   3:   spinlock_t list_lock;
   4:   struct kobject kobj;  //该集合的基类
   5:   struct kset_uevent_ops *uevent_ops;//uevent相关操作
   6:  };
 
 

看完主要的kobject的相关结构体,下面接着看kobject的常用的管理与操作:

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

该函数用来初始化kobject,初始化并设置名称后,还需要为它设置kset和ktype字段。

kobject的引用计数:

主要有两个函数:

struct kobject *kobject_get(struct kobject *kobj); //增加一个引用计数
 
void kobject_put(struct kobject *kobj); //减少引用计数

 

回宿舍了  此篇明天接着写·····

 

最后插入一段有意思的东西,大家应该写程序都写很久了,下面我说一个很简单的问题,这个问题就是写文章的时候,教研室的同学问的:

一个char类型的变量A,现在存值为255,用double类型强制转换以后A的值为多少?为什么?

就是提醒大家一下,学多了,也别忘了最基础的知识!

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值