linux内核字符驱动设备,linux设备管理之字符设备驱动分析(1)

Linux内核大量使用面向对象的设计思想,通过追踪源码,我们甚至可以使用面向对象语言常用的UML类图来分析Linux设备管理的"类"之间的关系。这里以4.8.5内核为例从kobject,kset,kobj_type的分析入手,进而一探内核对于设备的管理方式

3001b4fd7abc8c1ad0b2c2b2e7d62325.png

container_of宏

这个宏几乎是linux数据结构的基础,Linux中的链表与传统的链表不同,其链表的节点本身并不包含任何数据,任何想要插入到链表的数据只需要包含一个事先写好的节点//include/linux/types.h184 struct list_head {

185     struct list_head *next, *prev;

186 };

057945b053ef9849806bf68e02a976b9.png

但是,使用这种通用的链表的第一个问题就是如何根据一个list_head成员来找到相应的数据,Linux社区的大神们早就找到了相应的方法,就是利用下面这个container_of宏,只需要输入成员指针ptr,包含该成员的结构体类型type,以及该成员在结构体中名字name就可以返回包含ptr的type类型的结构首地址,这个宏充分利用了C语言直接操作内存的特性。需要注意的是,如果单纯为了得到地址只需要ptr-&((type* 0)->member),内核的写法其实还利用了编译器的类型检查机制做了一份校验工作,即如果传入的ptr类型和type->member的类型不匹配,会报错,//include/linux/kernel.h14 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

830 #define container_of(ptr, type, member) ({          \

831     const typeof( ((type *)0)->member ) *__mptr = (ptr);    \832     (type *)( (char *)__mptr - offsetof(type,member) );})

kobject结构

Linux内核中有大量的驱动,而这些驱动往往具有类似的结构,根据面向对象的思想,我们就可以将这些共同的部分提取为父类,这个父类就是kobject,也就是驱动编程中使用的.ko文件的由来,下面这张图是我根据内核源码的kobject绘制的简单的UML图,从中可以看出,kobject包含了大量的设备必须的信息,而三大类设备驱动都需要包含这个kobject结构,也就是"继承"自kobject。一个kobject对象就对应sys目录中的一个设备。

内核源码中的kobject结构定义如下//include/linux/kobject.h

63 struct kobject {

64     const char          *name; 65     struct list_head    entry; 66     struct kobject      *parent; 67     struct kset         *kset; 68     struct kobj_type    *ktype; 69     struct kernfs_node  *sd; /* sysfs directory entry */

70     struct kref         kref; 71 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE

72     struct delayed_work release; 73 #endif

74     unsigned int state_initialized:1; 75     unsigned int state_in_sysfs:1; 76     unsigned int state_add_uevent_sent:1; 77     unsigned int state_remove_uevent_sent:1; 78     unsigned int uevent_suppress:1; 79 };

这个结构中,name(64)表示kobject对象的名字,对应sysfs下的一个目录。entry(65)是kobject中插入的head_list结构,parent(66)是指向当前kobject父对象的指针,体现在sys结构中就是包含当前kobject对象的目录对象,kset(67)表示当前kobject对象所属的集合,ktype(68)表示当前kobject的类型。sd(69)用于表示VFS文件系统的目录项,是设备与文件之间的桥梁。kref(70)是对kobject的引用计数,当引用计数为0是,就回调之前注册的release方法释放该对象。state_initialized:1(74)初始化标志位,在对象初始化时被置位,表示对象是否已经被初始化。state_in_sysfs:1(75)表示kobject对象在sysfs中的状态,在对应目录中被创建则置1,否则为0。state_add_uevent_sent:1(76)是添加设备的uevent事件是否发送标志,添加设备时会向用户空间发送uevent事件,请求新增设备。state_remove_uevent_sent:1(76)是删除设备的uevent事件是否发送标志,删除设备时会向用户空间发送uevent事件,请求卸载设备

kobject操作

4.8.5的内核在lib/koject.c等源码中定义了一系列对kobject操作的函数,这里只列出最简单的几个

初始化kobject187 static void kobject_init_internal(struct kobject *kobj)

188 {

189         if (!kobj) 190                 return; 191         kref_init(&kobj->kref); 192         INIT_LIST_HEAD(&kobj->entry); 193         kobj->state_in_sysfs = 0; 194         kobj->state_add_uevent_sent = 0;

195         kobj->state_remove_uevent_sent = 0; 196         kobj->state_initialized = 1; 197 }

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

326 { 327         char *err_str;

... 344         kobject_init_internal(kobj);

345         kobj->ktype = ktype; 346         return;

... 351 }

注册kobject//添加kobject到内核

200 static int kobject_add_internal(struct kobject *kobj) 201 { 202         int error = 0; 203         struct kobject *parent;

... 214         parent = kobject_get(kobj->parent); 215

216         /* join kset if set, use it as parent if we do not already have one */

217         if (kobj->kset) { 218                 if (!parent) 219                         parent = kobject_get(&kobj->kset->kobj); 220                 kobj_kset_join(kobj); 221                 kobj->parent = parent; 222         } 223

224         pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n", 225                  kobject_name(kobj), kobj, __func__, 226                  parent ? kobject_name(parent) : "", 227                  kobj->kset ? kobject_name(&kobj->kset->kobj) : ""); 228

229         error = create_dir(kobj);

... 246         kobj->state_in_sysfs = 1; 247

248         return error; 249 } 354 static __printf(3, 0) int kobject_add_varg(struct kobject *kobj,

355                                            struct kobject *parent, 356                                            const char *fmt, va_list vargs) 357 { 358         int retval;

... 365         kobj->parent = parent; 366         return kobject_add_internal(kobj); 367 } 394 int kobject_add(struct kobject *kobj, struct kobject *parent, 395                 const char *fmt, ...) 396 { 397         va_list args; 398         int retval;

... 410         va_start(args, fmt); 411         retval = kobject_add_varg(kobj, parent, fmt, args); 412         va_end(args); 413

414         return retval; 415 }

初始化并注册kobject429 int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,

430                          struct kobject *parent, const char *fmt, ...)

431 {

432         va_list args;

433         int retval; 434

435         kobject_init(kobj, ktype); 436

437         va_start(args, fmt); 438         retval = kobject_add_varg(kobj, parent, fmt, args); 439         va_end(args); 440

441         return retval; 442 }

注销kobject569 void kobject_del(struct kobject *kobj)

570 {

571         struct kernfs_node *sd;

572

573         if (!kobj) 574                 return;

575

576         sd = kobj->sd;

577         sysfs_remove_dir(kobj);

578         sysfs_put(sd);

579

580         kobj->state_in_sysfs = 0;

581         kobj_kset_leave(kobj);

582         kobject_put(kobj->parent);

583         kobj->parent = NULL;

584 }

计数减一//将kobject对象的引用计数加1,同时返回该对象指针。//include/linux/kref.h

40 static inline void kref_get(struct kref *kref)

41 { 42         /* If refcount was 0 before incrementing then we have a race

43          * condition when this kref is freeing by some other thread right now.

44          * In this case one should use kref_get_unless_zero()

45          */

46         WARN_ON_ONCE(atomic_inc_return(&kref->refcount) 

//lib/kobject.c

591 struct kobject *kobject_get(struct kobject *kobj)

592 { 593         if (kobj) {

... 598                 kref_get(&kobj->kref); 599         } 600         return kobj; 601 }//将kobject对象的引用计数加1,如果减为零就释放//include/linux/kref.h

67 static inline int kref_sub(struct kref *kref, unsigned int count,

68              void (*release)(struct kref *kref)) 69 { 70         WARN_ON(release == NULL); 71

72         if (atomic_sub_and_test((int) count, &kref->refcount)) { 73                 release(kref); 74                 return 1; 75         } 76         return 0; 77 } 96 static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))

97 { 98         return kref_sub(kref, 1, release); 99 }//lib/kobject.c

684 void kobject_put(struct kobject *kobj)

685 { 686         if (kobj) {

... 691                 kref_put(&kobj->kref, kobject_release); 692         } 693 }

kset结构

kset表示一组kobject的集合,这些kobject可以是不同或相同的类型(ktype)。sysfs中的设备组织结构很大程度上都是根据kset进行组织的,比如在平台设备模型中,当我们注册一个设备或驱动到平台总线,其实是将对应的kobject挂接到platform总线的kset上,每种总线都是维护两条链表(两个kset),一条用于链接挂接在上面的驱动(驱动kset),一条用于链接挂接在上面的设备(设备kset)。//include/linux/kobject.h168 struct kset {169     struct list_head list;170     spinlock_t list_lock;171     struct kobject kobj;172     const struct kset_uevent_ops*uevent_ops;

173 };

list_head(169)还是那个用来挂在链表上的结构。kobj(171)是归属于该kset的所有的kobject的共有parent,这个parent就是体现内核设备组织结构的关键

d37d04385bec08eafd73cbc939eadab9.png

kobj_type结构//include/linux/kobject.h116 struct kobj_type {117     void (*release)(struct kobject *kobj);118     const struct sysfs_ops *sysfs_ops;119     struct attribute **default_attrs;

120     const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);121     const void *(*namespace)(struct kobject *kobj);122 };//include/linux/sysfs.h

29 struct attribute { 30     const char      *name; 31     umode_t         mode; 32 #ifdef CONFIG_DEBUG_LOCK_ALLOC

33     bool            ignore_lockdep:1;

34     struct lock_class_key   *key;

35     struct lock_class_key   skey;

36 #endif

37 };

209 struct sysfs_ops {

210     ssize_t (*show)(struct kobject *, struct attribute *, char *);211     ssize_t (*store)(struct kobject *, struct attribute *, const char *, si212 };

这个结构主要是表征kobject的类型,其中,release(117)是一个释放kobject对象的接口,有点像面向对象中的析构。sysfs_ops(118)是操作kobject的方法集。由此可见,对同一类型的kobject操作会回调同一个kobj_type的方法//include/linux/kobject.h197 static inline struct kobj_type *get_ktype(struct kobject *kobj)            198 {199     return kobj->ktype;200 }

从这个函数中可以看出,4.8.5提取kobject的ktype的时候直接提取kobject的,我还测试过3.14版本的,也是这种写法,不过网上还有下面的这种get_ktype的实现,还没找到具体是哪个版本,显然,这个版本中kset中的ktype这个类型优先于 kobject 自身中的 ktype 。因此在典型的应用中, 在 struct kobject 中的 ktype 成员被设为 NULL, 而 kset 中的ktype是实际被使用的。static inline struct kobj_type * get_ktype(struct kobject * k)

{            if (k->kset && k->kset->ktype)                return k->kset->ktype;            else

return k->ktype;

}

结构框图

kobject,kset是Linux设备管理中的基本结构体,但在实际操作中我们几乎不会实际操作这些结构,因为他们本身并不具有针对某一个具体设备或驱动的信息,在Linux内核中,两个结构都是被包含具体的设备结构中,比如cdev,gendisk等,从面向对象的角度考虑,就是每一类设备都可以看作这两个结构的子类。

通过上面的分析,我们可以看出这三者之间的关系,并画出下面的结构框图,sysfs中的上目录结构就是根据kset之间的数据组织方式进行呈现的。

8349253002c67887963026fb36386e72.png

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值