简介
Linux操作系统在kernel 2.6版本中引入了设备驱动模型,简化驱动程序的编写。
Linux的设备驱动模型是为了支持设备的即插即用功能,为了减少冲突,需要具备以下条件:
- 自动检测在系统中添加/删除设备
- 资源管理(地址,IRQ中断,DMA通道,内存区域)
- 操作系统可以自动加载新设备的驱动程序
- 当设备和总线允许时,不需要进行系统重启(热插拔)
驱动模型主要包括如下基本结构
- 设备 (struct device)
- 总线 (struct bus_type)
- 类 (struct class)
- 驱动 (struct driver)
- 子系统(struct sub_system)
sysfs文件系统
sysfs目录下主要分为如下划分
- block 系统中有各种块设备(磁盘,分区)
- bus 物理设备链接到总线上(pci, ide, usb)
- class 系统中可用的驱动程序类别(net, block, tty, rtc, graphics)
- dev 链接到系统的设备的层次结构
- firmare 来自系统固件的信息
- fs 已经挂载的文件系统信息
- hypervisor 虚拟化信息
- kernel 内核状态信息(已经登陆用户信息,热插拔)
- module当前内核已经加载的内核模块
- power 与电源管理子系统的相关的信息
kset and kobject
参考文档:The zen of kobjects
源码文件:
| include/linux/kobject.h include/linux/kref.h lib/kobject.c |
|---|
kobject的结构体如下所示
struct kobject {
const char *name; // 挂载sysfs中对应的目录名称
struct list_head entry; // 属于某个kset, kset中有这个列表
struct kobject *parent; // kobject的父亲结构,形成层次结构,在sysfs中形成目录关系
struct kset *kset; // 当前的kobject属于那个kset
struct kobj_type *ktype; // kobject的类型,主要是命名空间,操作函数
struct kernfs_node *sd; /* sysfs directory entry */ // 对应的sysfs对象,在3.14以后sysfs基于kernelfs实现
struct kref kref; // 引用计数
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work release;
#endif
unsigned int state_initialized:1; // 是否初始化
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消息
};
Kset的结构体代码如下所示
struct kset {
struct list_head list; // 列表,主要用于存储属于这个kset的kobject.
spinlock_t list_lock; // 列表访问锁
struct kobject kobj; // kobject kset也可以被当作一个kset
const struct kset_uevent_ops *uevent_ops; // kset uevent操作接口
} __randomize_layout;
从上方的结构中我们可以看到kset是一个用于存储kobject的容器,但是kset本身也是一个kobject.
思考
为什么kset中也需要一个kobject类型?
Linux内核模型中主要将设备与sysfs相结合,kset充当的更多的类型目录的职能,kobject更多的是文件的功能.
为什么在kobject中还需要提供一个parent指针?
上方的kset的管理职能更多的是面向用户访问的sysfs的文件职能,但是kobject在内核中需要有自己的管理结构,利用parent指针,构造kobject按层级访问的方式,能够有效的完成kobject的管理.
parent的管理方式带来了什么样的便利?
parent可以用于管理属于class的关系,后面的device和class的关系我们会详细描述
kobject 和kset模型结构如下所示
kset中有部分的uevent操作
struct kset_uevent_ops {
int (* const filter)(struct kset *kset, struct kobject *kobj);
const char *(* const name)(struct kset *kset, struct kobject *kobj);
int (* const uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
在进行uevent操作时,上方提供的函数接口可能会被执行
- filter 主要用于过滤部分的kobject,不需要进行uevent操作的上报
- name 将字符串传递给用户空间的热插拔程序处理
- uevent 将用户空间需要的参数转换成环境变量
uevent的常见操作:
总线 class设备与驱动
参考文档: linux-kernel-labs.github.io/refs/pull/1…
核心代码driver/base分析 Linux下为什么需要设计出总线,设备与驱动这三者之间的一个抽象存在,目的是什么?
首先聊一下驱动与设备的分离,实际上这个是符合Unix设计原则中的机制与策略分离的原则,只不过这里的机制是指驱动,也就是提供的各种操作接口,策略就是指设备,也就是我们的各种操作设备,也就是各种被操作的实体(如寄存器,中断,内存地址等)。
为什么又引入了总线的概念?
抛开计算机的设计本来就有总线的概念,设备都是挂载在总线不同的总线上的,利用总线可以有效的将设备进行分类(但是针对目前的设计,需要引入总线的概念还是远远不用的,因为驱动和设备已经是可以逐一配对的)。这里又需要考虑到Linux的另外两种机制:1. 模块机制 2. 设备热插拔机制,因为模块机制允许驱动被后来加载,也就是设备可能已经挂载在系统上,但是由于没有驱动,设备无法运转,这时需要驱动与设备进行配对,配对完成后,驱动设备正常工作。热插拔机制是允许设备动态加载,本来驱动已经在系统中,设备后续加载后,需要驱动与设备进行配对,配对完成后,驱动设备正常工作。综上所述,那么需要管理者来管理系统中所有注册的驱动和设备,那么这个管理者正好又根据计算机的总线模型(设备都挂载在不同的总线下)对设备进行分类,有效的减少全局匹配驱动与设备的次数。
为什么需要加入class的设计?
参考上方的设计,目前已经满足了驱动的功能要求,但是目前的实现对用户不太友好,因为通常我们期望可以通过功能找到相关的设备,如我们期望找到硬盘时,我们并不关心硬盘下层是挂载在SATA,mSATA, m.2, PCIe的总线上,我们期望的是根据功能找到相关的设备,这时候class应运而出。正好与sysfs文件系统结合,给上层用户提供相关的操作接口。
为什么需要引入platform_bus和platform_device?
在操作系统中可能存在某些设备不是通过总线CPU相连,肯恩是直接挂载在某些GPIO(相当于一个pin脚)下,所以系统中会存在一些不存在总线的bus和设备。
加入了总线的概念,那么我们了解一下系统中都有哪些总线,我们可以在/sys/bus目录下找到 通过命令tree -L 1获取到以下信息
整体图如下,之前搜集的时候忘了是从哪位同仁中copy过来,如有侵权,请联系我删除。