从零到一手写操作系统(九、驱动与设备 2)linux获取设备信息)
如果追忆会荡起涟漪,那么今天的秋红落叶和晴空万里都归你
https://aeneag.xyz
微信公众号:技术乱舞
艾恩凝
手写操作系统目录
9.2)linux获取设备信息
Linux 的驱动模型至少有三个核心数据结构,分别是总线、设备和驱动
9.2.1)数据结构
kobject 和 kset 是构成 /sys 目录下的目录节点和文件节点的核心,也是层次化组织总线、设备、驱动的核心数据结构,kobject、kset 数据结构都能表示一个目录或者文件节点。
1cstruct kobject {
2 const char *name; //名称,反映在sysfs中
3 struct list_head entry; //挂入kset结构的链表
4 struct kobject *parent; //指向父结构
5 struct kset *kset; //指向所属的kset
6 struct kobj_type *ktype;
7 struct kernfs_node *sd; //指向sysfs文件系统目录项
8 struct kref kref; //引用计数器结构
9 unsigned int state_initialized:1;//初始化状态
10 unsigned int state_in_sysfs:1; //是否在sysfs中
11 unsigned int state_add_uevent_sent:1;
12 unsigned int state_remove_uevent_sent:1;
13 unsigned int uevent_suppress:1;
14};
每一个 kobject,都对应着 /sys 目录下(其实是 sysfs 文件系统挂载在 /sys 目录下) 的一个目录或者文件,目录或者文件的名字就是 kobject 结构中的 name。
kobject 结构中可以看出,它挂载在 kset 下,并且指向了 kset
kset 结构中本身又包含一个 kobject 结构,所以它既是 kobject 的容器,同时本身还是一个 kobject。kset 结构代码如下所示。
1struct kset {
2 struct list_head list; //挂载kobject结构的链表
3 spinlock_t list_lock; //自旋锁
4 struct kobject kobj;//自身包含一个kobject结构
5 const struct kset_uevent_ops *uevent_ops;//暂时不关注
6} __randomize_layout;
在 Linux 内核中,至少有两个顶层 kset,代码如下所示。
1struct kset *devices_kset;//管理所有设备
2static struct kset *bus_kset;//管理所有总线
3static struct kset *system_kset;
4int __init devices_init(void)
5{
6 devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);//建立设备kset
7 return 0;
8}
9int __init buses_init(void)
10{
11 bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);//建立总线kset
12 if (!bus_kset)
13 return -ENOMEM;
14 system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);//在设备kset之下建立system的kset
15 if (!system_kset)
16 return -ENOMEM;
17 return 0;
18}
难以理解,一图解惑
9.2.2)总线
总线表示 CPU 与设备的连接,Linux 把总线抽象成 bus_type 结构。
1struct bus_type {
2 const char *name;//总线名称
3 const char *dev_name;//用于列举设备,如("foo%u", dev->id)
4 struct device *dev_root;//父设备
5 const struct attribute_group **bus_groups;//总线的默认属性
6 const struct attribute_group **dev_groups;//总线上设备的默认属性
7 const struct attribute_group **drv_groups;//总线上驱动的默认属性
8 //每当有新的设备或驱动程序被添加到这个总线上时调用
9 int (*match)(struct device *dev, struct device_driver *drv);
10 //当一个设备被添加、移除或其他一些事情时被调用产生uevent来添加环境变量。
11 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
12 //当一个新的设备或驱动程序添加到这个总线时被调用,并回调特定驱动程序探查函数,以初始化匹配的设备
13 int (*probe)(struct device *dev);
14 //将设备状态同步到软件状态时调用
15 void (*sync_state)(struct device *dev);
16 //当一个设备从这个总线上删除时被调用
17 int (*remove)(struct device *dev);
18 //当系统关闭时被调用
19 void (*shutdown)(struct device *dev);
20 //调用以使设备重新上线(在下线后)
21 int (*online)(struct device *dev);
22 //调用以使设备离线,以便热移除。可能会失败。
23 int (*offline)(struct device *dev);
24 //当这个总线上的设备想进入睡眠模式时调用
25 int (*suspend)(struct device *dev, pm_message_t state);
26 //调用以使该总线上的一个设备脱离睡眠模式
27 int (*resume)(struct device *dev);
28 //调用以找出该总线上的一个设备支持多少个虚拟设备功能
29 int (*num_vf)(struct device *dev);
30 //调用以在该总线上的设备配置DMA
31 int (*dma_configure)(struct device *dev);
32 //该总线的电源管理操作,回调特定的设备驱动的pm-ops
33 const struct dev_pm_ops *pm;
34 //此总线的IOMMU具体操作,用于将IOMMU驱动程序实现到总线上
35 const struct iommu_ops *iommu_ops;
36 //驱动核心的私有数据,只有驱动核心能够接触这个
37 struct subsys_private *p;
38 struct lock_class_key lock_key;
39 //当探测或移除该总线上的一个设备时,设备驱动核心应该锁定该设备
40 bool need_parent_lock;
41};
总线不仅仅是组织设备和驱动的容器,还是同类设备的共有功能的抽象层。下面我们来看看 subsys_private,它是总线的驱动核心的私有数据
1//通过kobject找到对应的subsys_private
2#define to_subsys_private(obj) container_of(obj, struct subsys_private, subsys.kobj)
3struct subsys_private {
4 struct kset subsys;//定义这个子系统结构的kset
5 struct kset *devices_kset;//该总线的"设备"目录,包含所有的设备
6 struct list_head interfaces;//总线相关接口的列表
7 struct mutex mutex;//保护设备,和接口列表
8 struct kset *drivers_kset;//该总线的"驱动"目录,包含所有的驱动
9 struct klist klist_devices;//挂载总线上所有设备的可迭代链表
10 struct klist klist_drivers;//挂载总线上所有驱动的可迭代链表
11 struct blocking_notifier_head bus_notifier;
12 unsigned int drivers_autoprobe:1;
13 struct bus_type *bus; //指向所属总线
14 struct kset glue_dirs;
15 struct class *class;//指向这个结构所关联类结构的指针
16};
9.2.3)设备
就是表示一个设备的结构
1struct device {
2 struct kobject kobj;
3 struct device *parent;//指向父设备
4 struct device_private *p;//设备的私有数据
5 const char *init_name; //设备初始化名字
6 const struct device_type *type;//设备类型
7 struct bus_type *bus; //指向设备所属总线
8 struct device_driver *driver;//指向设备的驱动
9 void *platform_data;//设备平台数据
10 void *driver_data;//设备驱动的私有数据
11 struct dev_links_info links;//设备供应商链接
12 struct dev_pm_info power;//用于设备的电源管理
13 struct dev_pm_domain *pm_domain;//提供在系统暂停时执行调用
14#ifdef CONFIG_GENERIC_MSI_IRQ
15 struct list_head msi_list;//主机的MSI描述符链表
16#endif
17 struct dev_archdata archdata;
18 struct device_node *of_node; //用访问设备树节点
19 struct fwnode_handle *fwnode; //设备固件节点
20 dev_t devt; //用于创建sysfs "dev"
21 u32 id; //设备实例id
22 spinlock_t devres_lock;//设备资源链表锁
23 struct list_head devres_head;//设备资源链表
24 struct class *class;//设备的类
25 const struct attribute_group **groups; //可选的属性组
26 void (*release)(struct device *dev);//在所有引用结束后释放设备
27 struct iommu_group *iommu_group;//该设备属于的IOMMU组
28 struct dev_iommu *iommu;//每个设备的通用IOMMU运行时数据
29};
9.2.4)驱动
1struct device_driver {
2 const char *name;//驱动名称
3 struct bus_type *bus;//指向总线
4 struct module *owner;//模块持有者
5 const char *mod_name;//用于内置模块
6 bool suppress_bind_attrs;//禁用通过sysfs的绑定/解绑
7 enum probe_type probe_type;//要使用的探查类型(同步或异步)
8 const struct of_device_id *of_match_table;//开放固件表
9 const struct acpi_device_id *acpi_match_table;//ACPI匹配表
10 //被调用来查询一个特定设备的存在
11 int (*probe) (struct device *dev);
12 //将设备状态同步到软件状态时调用
13 void (*sync_state)(struct device *dev);
14 //当设备被从系统中移除时被调用,以便解除设备与该驱动的绑定
15 int (*remove) (struct device *dev);
16 //关机时调用,使设备停止
17 void (*shutdown) (struct device *dev);
18 //调用以使设备进入睡眠模式,通常是进入一个低功率状态
19 int (*suspend) (struct device *dev, pm_message_t state);
20 //调用以使设备从睡眠模式中恢复
21 int (*resume) (struct device *dev);
22 //默认属性
23 const struct attribute_group **groups;
24 //绑定设备的属性
25 const struct attribute_group **dev_groups;
26 //设备电源操作
27 const struct dev_pm_ops *pm;
28 //当sysfs目录被写入时被调用
29 void (*coredump) (struct device *dev);
30 //驱动程序私有数据
31 struct driver_private *p;
32};
33struct driver_private {
34 struct kobject kobj;
35 struct klist klist_devices;//驱动管理的所有设备的链表
36 struct klist_node knode_bus;//加入bus链表的节点
37 struct module_kobject *mkobj;//指向用kobject管理模块节点
38 struct device_driver *driver;//指向驱动本身
39};
9.2.5)文件操作函数
在 Linux 系统中提供了更为高级的封装,Linux 将设备分成几类分别是:字符设备、块设备、网络设备以及杂项设备
以杂项设备为例子研究一下,Linux 用 miscdevice 结构表示一个杂项设备,代码如下
1struct miscdevice {
2 int minor;//设备号
3 const char *name;//设备名称
4 const struct file_operations *fops;//文件操作函数结构
5 struct list_head list;//链表
6 struct device *parent;//指向父设备的device结构
7 struct device *this_device;//指向本设备的device结构
8 const struct attribute_group **groups;
9 const char *nodename;//节点名字
10 umode_t mode;//访问权限
11};
miscdevice 结构就是一个杂项设备,它一般在驱动程序代码文件中静态定义。我们清楚地看见有个 this_device 指针,它指向下层的、属于这个杂项设备的 device 结构。
驱动示例,便不展示了,写过多次驱动,内核的函数只要熟悉,就可以写驱动了
注:本节于2022年5月9日完成
手写操作系统目录
则移山填海之难,
终有成功之日!
——孙文