当前位置: 首页 > news >正文

统一设备模型(1)——bus、subsys_interface、class、class_interface分析

# 统一设备模型(1)——bus、subsys_interface、class、class_interface分析

在这一篇系列中,将着重分析内核是如何使用内核模块的。内核模块的详细分析请参考上一系列文章《内核对象kobject和sysfs》。

在统一设备模型里,内核利用kobj以及kset建立sysfs内的目录结构,并根据业务的不同,抽象出device、driver、bus、class等模型。几乎所有的内核驱动子系统都用到这一类模型。在本篇,先着重分析提供服务的较低层次的模型:bus、subsys_interface、class、class_interface。
在具体介绍之前,先大致用文字描述一下该模型的原理。bus是内核模拟出来的一条虚拟总线,device是逻辑设备,driver是驱动。每一个device和driver都需要向bus去注册,每次注册都会引发device和driver的匹配。如果匹配成功,那么将成功绑定,从而执行driver内自定义的或者bus内定义的probe函数对device进行各种初始化,进而使物理设备正常工作。subsys_interface是向bus中注册的其他接口,class_interface是向class中注册的其他接口。

不管是device还是driver,都必须要注册到bus上。因此我们先分析bus的相关实现,首先先看看bus的结构:

 108 struct bus_type {
 109         const char              *name; // 该bus的名称
 110         const char              *dev_name; // 
 111         struct device           *dev_root;
 112         struct device_attribute *dev_attrs;     /* use dev_groups instead */
 113         const struct attribute_group **bus_groups;
 114         const struct attribute_group **dev_groups;
 115         const struct attribute_group **drv_groups;
 116 
 117         int (*match)(struct device *dev, struct device_driver *drv);
 118         int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
 119         int (*probe)(struct device *dev);
 120         int (*remove)(struct device *dev);
 121         void (*shutdown)(struct device *dev);
 122 
 123         int (*online)(struct device *dev);
 124         int (*offline)(struct device *dev);
 125 
 126         int (*suspend)(struct device *dev, pm_message_t state);
 127         int (*resume)(struct device *dev);
 128 
 129         const struct dev_pm_ops *pm;
 130 
 131         const struct iommu_ops *iommu_ops;
 132 
 133         struct subsys_private *p;
 134         struct lock_class_key lock_key;
 135 };

对于复杂的结构体,我们往往只关注其核心成员。目前我学疏才浅,在我看来,bus_type的核心是109~121行之间的内容。成员意义已经标注在结构体中。
bus_type的操作函数也不外乎是那几个:注册、注销。下面我们分别来看。

 887 int bus_register(struct bus_type *bus)
 888 {
 889         int retval;                                                                                                                                                                                         
 890         struct subsys_private *priv;
 891         struct lock_class_key *key = &bus->lock_key;
 892 
 893         priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
 894         if (!priv)
 895                 return -ENOMEM;
 896 
 897         priv->bus = bus;
 898         bus->p = priv;
 899 
 900         BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
 901         
 902         retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
 903         if (retval)
 904                 goto out;
 905                          
 906         priv->subsys.kobj.kset = bus_kset;
 907         priv->subsys.kobj.ktype = &bus_ktype;
 908         priv->drivers_autoprobe = 1;
 909 
 910         retval = kset_register(&priv->subsys);
 911         if (retval)
 912                 goto out;
 913 
 914         retval = bus_create_file(bus, &bus_attr_uevent);
 915         if (retval)
 916                 goto bus_uevent_fail;
 917 
 918         priv->devices_kset = kset_create_and_add("devices", NULL,
 919                                                  &priv->subsys.kobj);
 920         if (!priv->devices_kset) {
 921                 retval = -ENOMEM;
 922                 goto bus_devices_fail;
 923         }
 924 
 925         priv->drivers_kset = kset_create_and_add("drivers", NULL,                                                                                                                                           
 926                                                  &priv->subsys.kobj);
 927         if (!priv->drivers_kset) {
 928                 retval = -ENOMEM;
 929                 goto bus_drivers_fail;
 930         }
 931 
 932         INIT_LIST_HEAD(&priv->interfaces);
 933         __mutex_init(&priv->mutex, "subsys mutex", key);
 934         klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
 935         klist_init(&priv->klist_drivers, NULL, NULL);
 936 
 937         retval = add_probe_files(bus);
 938         if (retval)
 939                 goto bus_probe_files_fail;
 940 
 941         retval = bus_add_groups(bus, bus->bus_groups);
 942         if (retval)
 943                 goto bus_groups_fail;
 944 
 945         pr_debug("bus: '%s': registered\n", bus->name);
 946         return 0;
 947 
 948 bus_groups_fail:
 949         remove_probe_files(bus);
 950 bus_probe_files_fail:
 951         kset_unregister(bus->p->drivers_kset);
 952 bus_drivers_fail:
 953         kset_unregister(bus->p->devices_kset);
 954 bus_devices_fail:
 955         bus_remove_file(bus, &bus_attr_uevent);
 956 bus_uevent_fail:
 957         kset_unregister(&bus->p->subsys);
 958 out:
 959         kfree(bus->p);
 960         bus->p = NULL;
 961         return retval;                                                                                                                                                                                      
 962 }
 963 EXPORT_SYMBOL_GPL(bus_register);

893行分配了subsys_private这么一个结构。这个结构是用来记录在内核中关于bus的动态信息的,和bus_type结构相对应。这个结构也贴出如下,并将关键成员注释。

 28 struct subsys_private {                                                                                                                                                                                      
 29         struct kset subsys; // 该子系统的内嵌kset
 30         struct kset *devices_kset; // 该子系统管理的设备kset
 31         struct list_head interfaces; 
 32         struct mutex mutex;
 33 
 34         struct kset *drivers_kset; // 该子系统管理的驱动kset
 35         struct klist klist_devices; // 该子系统下的所有设备的链表头
 36         struct klist klist_drivers; // 该子系统下的所有驱动的链表头
 37         struct blocking_notifier_head bus_notifier;
 38         unsigned int drivers_autoprobe:1;
 39         struct bus_type *bus; // 与该子系统关联的bus
 40 
 41         struct kset glue_dirs;
 42         struct class *class; 
 43 };

906行和910行可以看到,bus_type类通过内嵌的kset在sysfs中有层次的目录,当然,归根结底,是使用kobj来形成这种层次。918行和925行,可以看到,我们知道,比如在/sys/bus/pci 下,一定有devices和drivers这两个目录。这就是这个函数的功劳。把这个函数展开:

 937 struct kset *kset_create_and_add(const char *name,                                                                                                                                                          
 938                                  const struct kset_uevent_ops *uevent_ops,
 939                                  struct kobject *parent_kobj)
 940 {       
 941         struct kset *kset;
 942         int error;
 943         
 944         kset = kset_create(name, uevent_ops, parent_kobj);
 945         if (!kset)
 946                 return NULL;                     
 947         error = kset_register(kset);
 948         if (error) {
 949                 kfree(kset);
 950                 return NULL;
 951         }
 952         return kset;
 953 }
 894 static struct kset *kset_create(const char *name,
 895                                 const struct kset_uevent_ops *uevent_ops,
 896                                 struct kobject *parent_kobj)                                                                                                                                                
 897 {
 898         struct kset *kset;
 899         int retval;
 900 
 901         kset = kzalloc(sizeof(*kset), GFP_KERNEL);
 902         if (!kset)
 903                 return NULL;
 904         retval = kobject_set_name(&kset->kobj, "%s", name);
 905         if (retval) {
 906                 kfree(kset);
 907                 return NULL;
 908         }
 909         kset->uevent_ops = uevent_ops;
 910         kset->kobj.parent = parent_kobj;
 911 
 912         /*
 913          * The kobject of this kset will have a type of kset_ktype and belong to
 914          * no kset itself.  That way we can properly free it when it is
 915          * finished being used.
 916          */
 917         kset->kobj.ktype = &kset_ktype;
 918         kset->kobj.kset = NULL;
 919 
 920         return kset;
 921 }

其实我们发现,这个函数只不过是比kset_register多了个分配kset内存的过程。我们之前的博文有说过,在使用kset_register之前,一定要为kset内嵌的kobj内的parent指针初始化指向,否则该kset会创建在/sys下。之前我们是手动初始化的,现在这个操作被包含到kset_create中了。910行体现了这一点,这也证明了devices目录会建立在pci这些具体的bus目录下。

我们注意到subsys_private结构里,在第31行,有interfaces成员。该成员即是提供给该bus的一个管理接口。一个bus可以有多个管理接口,这些接口通过interfaces连在一起。interfaces实际也只是一个链表头。当然,该接口一般不是用作控制设备,而是作为管理class或者bus的一个功能。

 343 struct subsys_interface {
 344         const char *name;
 345         struct bus_type *subsys;
 346         struct list_head node;
 347         int (*add_dev)(struct device *dev, struct subsys_interface *sif);
 348         void (*remove_dev)(struct device *dev, struct subsys_interface *sif);
 349 };

1125 int subsys_interface_register(struct subsys_interface *sif)                                                                                                                                                 
1126 {       
1127         struct bus_type *subsys;
1128         struct subsys_dev_iter iter;
1129         struct device *dev;
1130 
1131         if (!sif || !sif->subsys)
1132                 return -ENODEV;
1133 
1134         subsys = bus_get(sif->subsys);
1135         if (!subsys)
1136                 return -EINVAL;
1137 
1138         mutex_lock(&subsys->p->mutex);
1139         list_add_tail(&sif->node, &subsys->p->interfaces);
1140         if (sif->add_dev) {
1141                 subsys_dev_iter_init(&iter, subsys, NULL, NULL);
1142                 while ((dev = subsys_dev_iter_next(&iter)))
1143                         sif->add_dev(dev, sif);
1144                 subsys_dev_iter_exit(&iter);
1145         }
1146         mutex_unlock(&subsys->p->mutex);
1147 
1148         return 0;
1149 }

调用subsys_interface_register向一个bus注册该接口。1139行可以看到,注册的实质就是将自身链到interfaces的链表尾部。1140~1145行为了保证在接口注册之前就已经注册进bus的设备,也能使用该接口的add_dev函数。

在新版本的linux内核代码里,bus是一条虚拟的设备,但是无从体现。在新版本linxu内核里,增加了子系统这么一个概念。因此,大家可以看到,注册bus进内核,生成的动态结构名称都是subsys_private。此外,新版本中的bus还有设备的抽象概念。为此,linux提供了subsys_system_register函数:

int subsys_system_register(struct bus_type *subsys,                                                                                                                                                         
1243                            const struct attribute_group **groups)
1244 {
1245         return subsys_register(subsys, groups, &system_kset->kobj);
1246 }

1182 static int subsys_register(struct bus_type *subsys,
1183                            const struct attribute_group **groups,
1184                            struct kobject *parent_of_root)
1185 {
1186         struct device *dev;
1187         int err;                                                                                                                                                                                            
1188 
1189         err = bus_register(subsys);
1190         if (err < 0)
1191                 return err;
1192 
1193         dev = kzalloc(sizeof(struct device), GFP_KERNEL);
1194         if (!dev) {
1195                 err = -ENOMEM;
1196                 goto err_dev;
1197         }
1198 
1199         err = dev_set_name(dev, "%s", subsys->name);
1200         if (err < 0)
1201                 goto err_name;
1202 
1203         dev->kobj.parent = parent_of_root;
1204         dev->groups = groups;
1205         dev->release = system_root_device_release;
1206 
1207         err = device_register(dev);
1208         if (err < 0)
1209                 goto err_dev_reg;
1210 
1211         subsys->dev_root = dev;
1212         return 0;
1213 
1214 err_dev_reg:
1215         put_device(dev);
1216         dev = NULL;
1217 err_name:
1218         kfree(dev);
1219 err_dev:
1220         bus_unregister(subsys);
1221         return err;
1222 }

可以看到1193之后,在bus_register之后,还有额外的事情去做。把总线也作为一个设备,支持注册到别的总线上。这对于开发者来说,可以更加便利的实现对多级设备的分类统一管理。关于device的介绍见下一篇。

 387 struct class {
 388         const char              *name;
 389         struct module           *owner;
 390 
 391         struct class_attribute          *class_attrs;
 392         const struct attribute_group    **dev_groups;
 393         struct kobject                  *dev_kobj;
 394 
 395         int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
 396         char *(*devnode)(struct device *dev, umode_t *mode);
 397 
 398         void (*class_release)(struct class *class);
 399         void (*dev_release)(struct device *dev);
 400 
 401         int (*suspend)(struct device *dev, pm_message_t state);
 402         int (*resume)(struct device *dev);
 403 
 404         const struct kobj_ns_type_operations *ns_type;
 405         const void *(*namespace)(struct device *dev);
 406 
 407         const struct dev_pm_ops *pm;
 408 
 409         struct subsys_private *p;
 410 };

class的结构如上所示。乍一看和bus_type很相似。其实,个人理解,把bus的绑定device和driver功能删除,bus就成了class。而在很多子系统中,class也正是作为一个bus的补充。例如,一个SCSI设备,需要注册到SCSI总线上,那么在对该设备的操作,都需要对应于SCSI生成的动态结构。这个结构将被SCSI上层驱动(例如磁盘驱动)识别获取和绑定。那么对一个磁盘的操作,将绕不开磁盘驱动。class提供了一个相当于第三方的接口。在SCSI子系统中,内核把每一个SCSI设备不但注册进了SCSI总线,还注册进了sg类。这样,用户可以有多重选择操作设备。
class的操作不外乎也就是注册注销。

166 int __class_register(struct class *cls, struct lock_class_key *key)
167 {
168         struct subsys_private *cp;
169         int error;
170 
171         pr_debug("device class '%s': registering\n", cls->name);
172 
173         cp = kzalloc(sizeof(*cp), GFP_KERNEL);
174         if (!cp)
175                 return -ENOMEM;
176         klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
177         INIT_LIST_HEAD(&cp->interfaces);
178         kset_init(&cp->glue_dirs);
179         __mutex_init(&cp->mutex, "subsys mutex", key);
180         error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
181         if (error) {
182                 kfree(cp);
183                 return error;
184         }
185 
186         /* set the default /sys/dev directory for devices of this class */
187         if (!cls->dev_kobj)
188                 cls->dev_kobj = sysfs_dev_char_kobj;
189 
190 #if defined(CONFIG_BLOCK)
191         /* let the block class directory show up in the root of sysfs */
192         if (!sysfs_deprecated || cls != &block_class)
193                 cp->subsys.kobj.kset = class_kset;
194 #else                                                                                                                                                                                                        
195         cp->subsys.kobj.kset = class_kset;
196 #endif
197         cp->subsys.kobj.ktype = &class_ktype;
198         cp->class = cls;
199         cls->p = cp;
200 
201         error = kset_register(&cp->subsys);
202         if (error) {
203                 kfree(cp);
204                 return error;
205         }
206         error = add_class_attrs(class_get(cls));
207         class_put(cls);
208         return error;
209 }

正因为class和bus的相似之处,在新版本linxu内核里,将对class和bus采用相同的动态结构。即subsys_private。代码中也可以看出,注册的过程和bus几乎一样,这里不再赘述。
当然,class中也有类似bus中提供的接口。这个接口如下:

 503 struct class_interface {                                                                                                                                                                                    
 504         struct list_head        node;
 505         struct class            *class;
 506 
 507         int (*add_dev)          (struct device *, struct class_interface *);
 508         void (*remove_dev)      (struct device *, struct class_interface *);
 509 };

可以看到,和bus的接口在结构上,几乎一样。这里分析也留给读者。
那么,到这里为止,内核提供的这些为驱动编程打造的基础设施就介绍完毕了。下面我们用一个图来表示他们之间的关系。
1187168-20170801224928365-1960600712.png

转载于:https://www.cnblogs.com/wyk930511/p/7271462.html

相关文章:

  • IIS7.5 错误代码0x8007007e HTTP 错误 500.19 - Internal Server Error
  • 竖排主菜单鼠标滑动角度判断显示子分类
  • [NOIP2015] 运输计划
  • 【搜索】POJ-3669 BFS
  • Django model字段类型参考列表
  • 拓扑排序的原理及其实现
  • oracle11g 在azure云中使用rman进行实例迁移
  • Python三种排序算法
  • 最纯粹的直播技术实战02-Camera的处理以及推流
  • 一、MyBatis基本用法——3-Mapper XML映射文件
  • @Autowired和@Resource装配
  • ES6--ArrayBuffer
  • HDU 6078 Wavel Sequence
  • java io
  • 1.spring、mybatis、mysql整合需要的包
  • [deviceone开发]-do_Webview的基本示例
  • 【刷算法】求1+2+3+...+n
  • Android Volley源码解析
  • AWS实战 - 利用IAM对S3做访问控制
  • C学习-枚举(九)
  • Git学习与使用心得(1)—— 初始化
  • JavaSE小实践1:Java爬取斗图网站的所有表情包
  • Java面向对象及其三大特征
  • leetcode98. Validate Binary Search Tree
  • Python socket服务器端、客户端传送信息
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 推荐一款sublime text 3 支持JSX和es201x 代码格式化的插件
  • 小程序开发之路(一)
  • 1.Ext JS 建立web开发工程
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • NLPIR智能语义技术让大数据挖掘更简单
  • ​LeetCode解法汇总2696. 删除子串后的字符串最小长度
  • ​如何在iOS手机上查看应用日志
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • (C)一些题4
  • (附源码)php新闻发布平台 毕业设计 141646
  • (接口自动化)Python3操作MySQL数据库
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (一)使用IDEA创建Maven项目和Maven使用入门(配图详解)
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • ***linux下安装xampp,XAMPP目录结构(阿里云安装xampp)
  • .L0CK3D来袭:如何保护您的数据免受致命攻击
  • .NET Framework 和 .NET Core 在默认情况下垃圾回收(GC)机制的不同(局部变量部分)
  • .Net 路由处理厉害了
  • @Not - Empty-Null-Blank
  • [ HTML + CSS + Javascript ] 复盘尝试制作 2048 小游戏时遇到的问题
  • [2023-年度总结]凡是过往,皆为序章
  • [Avalon] Avalon中的Conditional Formatting.
  • [c++] 单例模式 + cyberrt TimingWheel 单例分析
  • [C语言]——分支和循环(4)
  • [Docker]三.Docker 部署nginx,以及映射端口,挂载数据卷
  • [Editor]Unity Editor类常用方法
  • [EFI]Dell Latitude-7400电脑 Hackintosh 黑苹果efi引导文件
  • [GDMEC-无人机遥感研究小组]无人机遥感小组-000-数据集制备
  • [Google Guava] 1.1-使用和避免null