zoukankan      html  css  js  c++  java
  • Linux设备管理(一)_kobject, kset,ktype分析

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

    container_of宏

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

    //include/linux/types.h
    184 struct list_head {                                                         
    185     struct list_head *next, *prev;
    186 };
    

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

    //include/linux/kernel.h
    
    14 #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 };
    

    这个结构中,

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

    kobject操作

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

    初始化kobject

     187 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'
    ",
     225                  kobject_name(kobj), kobj, __func__,
     226                  parent ? kobject_name(parent) : "<NULL>",
     227                  kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
     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 }
    
    

    初始化并注册kobject

     429 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 }
    
    

    注销kobject

     569 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计数加一

    //lib/kobject.c    
     591 struct kobject *kobject_get(struct kobject *kobj)
     592 {
     593         if (kobj) {
     594                 if (!kobj->state_initialized)
     595                         WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "
     596                                "initialized, yet kobject_get() is being "
     597                                "called.
    ", kobject_name(kobj), kobj);
     598                 kref_get(&kobj->kref);
     599         }
     600         return kobj;
     601 }
    

    kobject计数减一

    //将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) < 2);
     47 }
     
    //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通过kset组织成层次化的结构,所有属于该ksetkobjetc结构的parent指针指向kset包含的kobject对象,构成一个父子层次关系这些kobject可以是不同或相同的类型(kobj_type)。sysfs中的设备组织结构很大程度上都是根据kset进行组织的,比如"/sys/drivers"目录就是一个kset对象,包含系统中的驱动程序对应的目录,驱动程序的目录又kobject表示。比如在平台设备模型中,当我们注册一个设备或驱动到平台总线,其实是将对应的kobject挂接到platform总线的kset上,每种总线都是维护两条链表(两个kset),一条用于链接挂接在上面的驱动(驱动kset),一条用于链接挂接在上面的设备(设备kset)。

    //include/linux/kobject.h
    168 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 };  
    

    下面简单分析一下其中的成员

    struct kset
    --169-->list_head还是那个用来挂在链表上的结构,包含在一个kset的所有kobject构成了一个双向循环链表list_head就是这个链表的头部,这个链表用来连接第一个和最后一个kobject对象,第一个kobjetc使用entry连接kset集合以及第二个kobject对象,第二个kobject对象使用entry连接第一个kobject对象和第三个kobject对象,依次类推,最终形成一个kobject对象的链表
    --171-->kobj(171)是归属于该kset的所有的kobject的共有parent,这个parent就是体现内核设备组织结构的关键,同时,kset的引用计数就是内嵌的kobject对象的引用次数。

    kset操作

    下面是几个关于kset的基础操作方法

    初始化kset

    //lib/kobject.c
     187 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 }
    
     767 void kset_init(struct kset *k)        
     768 {
     769         kobject_init_internal(&k->kobj);
     770         INIT_LIST_HEAD(&k->list);
     771         spin_lock_init(&k->list_lock);
     772 }
    

    注册kset

    //lib/kobject.c
     809 int kset_register(struct kset *k) 
     810 {
     811         int err;
     812 
     813         if (!k)
     814                 return -EINVAL;
     815 
     816         kset_init(k);
     817         err = kobject_add_internal(&k->kobj);
     818         if (err)
     819                 return err;
     820         kobject_uevent(&k->kobj, KOBJ_ADD);
     821         return 0;
     822 }
    

    注销kset

    //lib/kobject.c
     829 void kset_unregister(struct kset *k)
     830 {
     831         if (!k)
     832                 return;
     833         kobject_del(&k->kobj);
     834         kobject_put(&k->kobj);
     835 }
    

    kset计数加一

    //include/linux/kobject.h
    187 static inline struct kset *kset_get(struct kset *k) 
    188 {
    189         return k ? to_kset(kobject_get(&k->kobj)) : NULL;
    190 }
    

    kset计数减一

    192 static inline void kset_put(struct kset *k)
    193 {
    194         kobject_put(&k->kobj);
    195 }
    

    kobj_type结构

    //include/linux/kobject.h
    116 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 };
    

    这个结构主要是表征kobject的类型,

    struct ktype
    --117-->release是一个释放kobject对象的接口,有点像面向对象中的析构。
    --118-->sysfs_ops是操作kobject的方法集,

    //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 *, si
    212 };
    

    struct sysfs_ops
    --210-->我们在使用cat echo等工具(read()/write()系统调用)进行读写sysfs中相应驱动的属性时,其实就是回调驱动的show()store()。由此可见,对同一类型的kobject操作会回调同一个kobj_type的方法

    //include/linux/kobject.h
    197 static inline struct kobj_type *get_ktype(struct kobject *kobj)            
    198 {
    199     return kobj->ktype;
    200 }
    

    从这个函数中可以看出,4.8.5提取kobject的kobj_type的时候直接提取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之间的数据组织方式进行呈现的。

  • 相关阅读:
    wpf之依赖属性
    wpf之布局控件
    WPF之绑定
    wpf之触发器
    wpf之样式
    wpf之TreeView
    wpf(五)
    【Javaweb】poi实现通过上传excel表格批量导入数据到数据库
    Java读取批量Excel文件
    Centos上通过yum命令删除有关MySQL
  • 原文地址:https://www.cnblogs.com/xiaojiang1025/p/6193959.html
Copyright © 2011-2022 走看看