kobject和kset
学习bus前需要先了解kobj和kset
kobject
kobj可以理解为内核设备模型中的一个最小对象(所有sysfs中的实体都需要直接或者间接内嵌继承它),其对应一个sysfs中的目录

name---kobject的名字,也就是sysfs中对应目录的名字
entry---作为kset中的节点,让所属的kset串联kobj的时候用
*parent---kobject的父指针,这样就可以支持层级关系了,sysfs创建目录的时候就是按照这个创建的
kset---当前kobject所属的kset
*ktype---这个kobject的"类型",决定syfs属性文件如何读写,释放时会怎么处理等等
*sd---指向kernfs层真实节点,内核维护,用户不填
kref---引用计数,参与kobject的生命周期,即有人还在用,就不会释放这个kobj
release---用于调试:延迟释放,方便捕获使用后释放的bug
最后是五个状态标志位
c
unsigned int state_initialized:1; // 是否已经完成初始化(kobject_init() 或 kobject_init_and_add())
unsigned int state_in_sysfs:1; // 是否已经成功注册到 sysfs 中(目录已创建)
unsigned int state_add_uevent_sent:1; // 是否已经发送过 KOBJ_ADD uevent(创建时通知)
unsigned int state_remove_uevent_sent:1;// 是否已经发送过 KOBJ_REMOVE uevent(删除时通知)
unsigned int uevent_suppress:1; // 是否抑制发送 uevent(用于批量操作时避免刷屏)
kobject_init

对kobject做了一些检查,然后给其内部的一些变量进行了缺省配置
kobject_add

该函数负责将一个已经初始化好的kobject加入到sysfs体系结构中(也就是真正创建sysfs里的目录)
其使用可变参数的方式设置kobject的名字,并且完成添加操作和创建对应目录操作
kset
kset可以理解为对一批kobject的集合,其本身也是内嵌继承了kobject,所以也会有自己的目录
其往往是给一个子系统(bus,class)等使用,可以在kobj假如到kset的时候很方便的设置kobj的parent为自己内嵌的kobj
也更方便子系统访问自己下属的kobj

kset实际上是 内嵌继承kobject的结构体
除了通过list记录自己下面的kobject外,其最重要的作用是统一管理和发送uevent
list:记录属于当前kset的kobjects的链表
lisk_lock:对于list访问的自旋锁,防止遍历过程中节点被修改
kobject:内嵌到当前kset的kobject
uevent_ops:简单说,它是内核用来"定制"某个 kset 下所有 kobject 发出 uevent 时额外携带哪些环境变量的一个回调机制。
uevent 是内核通知用户空间设备事件的机制
kset_init

主要部分体现在kobject_init_internal
初始化了kset的链表和自旋锁
kobject_init_internal
对kobject进行了缺省配置,并初始化了引用计数ref和其插入到链表中的节点entry

kset_register
初始化并且添加一个ketset
其会为当前kset创建一个目录(实际上目录是属于其内嵌的kobject的)

kobject_add_internal
这是对kset内嵌kobject处理的函数

该函数做的事情大概如下:
1.如果该kobject没有指定parent,就把它的父亲指定为其所属kset的内嵌kobj,并且添加该kobj到所属kset的链表中去
2.然后给该kobject创建目录(在parent的kobj对应目录下)
3.修改state_in_sysfs=1,表示该kobj已经是sysfs中的一份子了
kobject_uevent
向用户空间发送一个kobject被添加到内核中的uevent事件,让用户空间程序,如udev知道这个新对象出现了,并且进行处理
这个事件如何理解呢?
比如device_create函数,它可不是自己直接就创建/dev下面的设备节点的,而是也要在创建并注册device所内嵌的kobj到sysfs上,然后使用kobject_uevent(&dev->kobj, KOBJ_ADD);通过用户空间,udev才会去在/dev下面创建新的设备节点
bus_type源码
subsys_private源码
首先bus_type内部定义了一个重要的struct subsys_private类型的指针,该结构体内部成员如下

subsys---为一个kset集合,其中将需要用到的kobject串联起来
devices_kset---为指向设备kset集合的一个指针,即指向代表当前总线下devices目录对应的kset
interfaces---总线支持的接口链表
drivers_kset---为指向设备kset集合的一个指针,即指向代表当前总线下drivers目录对应的kset
klist_devices和klist_drivers这两个变量是为了对注册进bus的device和driver进行记录的链表(该链表提供了引用计数以及锁)
关于这里的kset和klist是否多次一举的理解
kset负责在sysfs中维护用户可见和层次结构
klist通过引用计数+锁的机制 提供了并发安全性
二者并不冲突,kobj提供的引用计数是为了管理device或者driver的生命周期
kilst中knode提供的引用计数是为了提供并发安全的遍历机制
bus_notifier--uevent通知头,热插拔,绑定,解绑等事件会通过这里通知用户空间
drivers_autoprobe---自动匹配并调用驱动中probe的开关,设置为1表示开启
bus_type---为指向总线类型的指针
glue_dirs---为了防止命名空间冲突的一个变量
class---当前总线和某个class绑定的时候用到
bus_type源码

name---总线名字
dev_name---用于枚举总线上设备的变量,比如spi1,spi2等等
dev_root---设置一个device作为总线的根设备
dev_attrs---总线上设备的默认属性
match---如何匹配总线上的driver和device
uevent---影响给用户空间的uevent事件
probe---添加device或者driver时候调用,并且会回调总线上driver中的probe函数来初始化device
remove到resume均为总线上设备的状态发生对应变化的时候会调用的函数
pm---总线上默认的电源管理操作,内部会回调对应的驱动的电源管理函数
iommu_ops---设置总线上的内存映射操作
struct subsys_private*---指向总线的子系统(总线的私有结构体)的指针
综上,bus_type结构体是对总线上设备或者驱动变化的时候,做的对应的操作做了一个规定
而总线上设备在sysfs中的对应结构,需要通过bus_type的私有结构体subsys_private中的成员来实现
bus_register源码



上面提到的subsys.kset指向的kset为在内核加载时就创建好的一个kset,名为bus,并且其内嵌kobj没有(parent),然后在下面的kset_register,会将当前spi_bus对应的subsys内嵌kobj的parent设置为这个名为bus的kset内嵌的kobj,这样就实现了,/sys/bus/下面是spi的目录结构了
由上面的过程引申,kset之间的包含关系是通过内嵌kobj来实现的

这里创建了devices和drivers对应的kset,该kset的内嵌kobj的parent当然就会是当前subsys这个kset的内嵌kobj了

klist_init初始化了两个链表,这两个链表通过独特的引用机制,提供并发安全的对knode所对应结构体(如device或者driver)的访问-----即假如一个进程在移出链表中的元素,需要等待ref为0才可以,这样就不会导致正在链表上迭代的进程的pre或者next指针变成野指针
add_probe_files会添加一个drivers_autoprobe文件到spi_bus的目录下,其值默认为1,即添加device或者driver会自动执行probe函数并在内部回调driver的probe函数