从零到一手写操作系统(九、驱动与设备 2)linux获取设备信息)

image.png

如果追忆会荡起涟漪,那么今天的秋红落叶和晴空万里都归你
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}

难以理解,一图解惑
098000.png

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 将设备分成几类分别是:字符设备、块设备、网络设备以及杂项设备099000.png

以杂项设备为例子研究一下,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日完成

手写操作系统目录


    


公众号'艾恩凝'
个人公众号
个人微信
个人微信
    吾心信其可行,
          则移山填海之难,
                  终有成功之日!
                                  ——孙文
    评论
    0 评论
avatar

取消