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

怎么自己做三个一网站wordpress怎么弄垂直分类

怎么自己做三个一网站,wordpress怎么弄垂直分类,都兰县公司网站建设,wordpress等模版比较第八十章 设备模型基本框架-kobject 和 kset 80.1 什么是设备模型 设备模型使Linux内核处理复杂设备更高效。 字符设备驱动适用于简单设备#xff0c;但对于电源管理和热插拔#xff0c;不够灵活。 设备模型允许开发人员以高级方式描述硬件及关系#xff0c;提供API处理设备…第八十章 设备模型基本框架-kobject 和 kset 80.1 什么是设备模型 设备模型使Linux内核处理复杂设备更高效。 字符设备驱动适用于简单设备但对于电源管理和热插拔不够灵活。 设备模型允许开发人员以高级方式描述硬件及关系提供API处理设备注册、热插拔和电源管理。这样开发人员可依赖内核处理底层功能减少重复工作和错误。 内核已提供USB、I2C等平台设备的模型开发人员可基于此编写驱动快速实现功能并利用内核的电源和热插拔管理。 80.2 设备模型的好处 设备模型在内核驱动中至关重要它统一描述硬件及关系带来以下优势 1. 代码复用多个设备可共用同一驱动减少冗余提升复用性和维护性。 // 示例多个设备使用同一驱动 struct device_driver my_driver; register_driver_for_devices(my_driver, device_list); 2. 资源动态管理设备模型支持资源如内存、中断的动态申请和释放。 // 示例动态申请资源 device_resource request_resource(device, RESOURCE_TYPE, ...); // 释放资源 release_resource(device_resource); 3. 简化驱动编写提供通用API使驱动编写更简化和模块化。 // 示例使用通用API注册设备 register_device(my_device); // 处理设备事件 device_event_handler(my_device, EVENT_TYPE, ...); 4. 热插拔支持设备模型支持运行时动态添加或移除设备并生成事件。 // 示例监听热插拔事件 register_hotplug_event_listener(my_device, hotplug_event_callback); 5. 面向对象设计设备被视为对象具有属性和方法支持通过设备模型的机制来继承和扩展。 // 示例设备对象及其方法 struct device { void (*init)(struct device*); void (*release)(struct device*); // 其他属性和方法 }; struct my_device { struct device base; // 特定属性和方法 }; void my_device_init(struct my_device* dev) { // 初始化设备 dev-base.init(dev-base); // 其他初始化操作 } 80.3 kobject 和 kset 基本概念 kobject内核对象 是内核中的通用对象模型表示各种实体。 kobject是一个结构体其中包含了一些描述该对象的属性和方法。 每个 kobject 在 /sys 下对应一个目录。 每一个kobject都会对应/sys/下一个目录 比如查看平台总线要进入/sys/bus 目录下bus 目录下的文件都是和总线相关的目录比如 amba 总线CPU 总线platform 总线。 kobject存在树状关系 KObject存在树状关系 一个 kobject 可以有一个父 kobject 和多个子 kobject通过 parent 指针可以将它们连接起来形成一个层次化的结构类似于目录结构。 struct kobject { const char *name; // 对象的名称 struct list_head entry; // 链接到父对象的子对象列表中的项 struct kobject *parent; // 指向父对象的指针 struct kset *kset; // 指向包含该对象的kset的指针 struct kobj_type *ktype; // 指向定义对象类型的kobj_type结构体的指针 struct kernfs_node *sd; // 指向sysfs目录中对应的kernfs_node的指针 struct kref kref; // 引用计数用于管理对象的生命周期 // 仅在CONFIG_DEBUG_KOBJECT_RELEASE配置启用时存在 #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; // 是否已发送添加事件通知 unsigned int state_remove_uevent_sent:1;// 是否已发送移除事件通知 unsigned int uevent_suppress:1; // 是否抑制uevent发送 // Android内核ABI保留字段 ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3); ANDROID_KABI_RESERVE(4); }; /*用于描述kobj对象的结构体*/ struct kobj_type { void (*release)(struct kobject *kobj); //release函数const struct sysfs_ops *sysfs_ops; //sysfs操作struct attribute **default_attrs; //obj对象的属性成员,包括名称、文件模式 }; /*sysfs文件操作*/ struct sysfs_ops { /*读操作*/ssize_t (*show)(struct kobject *kobj, struct attribute *attr, char *buf); /*写操作*/ssize_t (*store)(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count); }; /*属性结构体*/ struct attribute { const char *name; //属性名称,会生成对应文件夹umode_t mode; //文件模式 }; kset内核对象集合 用于组织和管理一组相关的 kobject。 包含 kobject 作为成员自身也是一个 kobject。 提供全局链表和锁确保线程安全的管理。 可定义 uevent 操作处理相关事件。 struct kset { struct list_head list; // 链接到全局kset列表的项 spinlock_t list_lock; // 保护list的自旋锁 struct kobject kobj; // 作为kset自身的kobject const struct kset_uevent_ops *uevent_ops; // 指向处理uevent操作的函数指针集 // Android内核ABI保留字段 ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3);ANDROID_KABI_RESERVE(4); } __randomize_layout; 80.4 kset 和 kobject 的关系 在 Linux 内核中kset 和 kobject 是相关联的两个概念它们之间存在一种层次化的关系 kset和 kobject的关系 一个 kset 可以包含多个 kobject而一个 kobject只能属于一个 kset。 kset 提供了对 kobject 的集合管理和操作接口用于组织和管理具有相似特性或关系的 kobject。这种关系使得内核能够以一种统一的方式管理和操作不同类型的内核对象。 80.5 创建 kobject 两种创建 kobject 的方法 一种是使用 kobject_create_and_add() 函数创建 kobject对象 /*创建kobject对象*/ struct kobject *kobject_create_and_add(const char *name, //kobject对象名称struct kobject *parent);//父kobject对象 另一种是使用 kzalloc() 和 kobject_init_and_add() 函数创建 kobject对象。 void *kzalloc(size_t size, //内存大小gfp_t flags);//内存分配标志 //GFP_KERNEL(可以在睡眠的情况下分配内存用于内核的正常操作)//GFP_ATOMIC(不能睡眠用于中断上下文或紧急情况下).int kobject_init_and_add(struct kobject *kobj, //kobject指针const struct kobj_type *ktype, //kobject_type指针, //定义了与 kobject 相关的方法//如 release 函数用于释放资源//sysfs_ops 用于处理 sysfs 文件系统操作等。struct kobject *parent, //父kobject指针const char *name); //kobject名称 // 定义了三个 kobject 指针变量mykobject01、mykobject02、mykobject03 struct kobject *mykobject01; struct kobject *mykobject02; struct kobject *mykobject03;// 定义了一个 kobj_type 结构体变量 mytype用于描述 kobject 的类型。 struct kobj_type mytype; // 模块的初始化函数 static int mykobj_init(void) {int ret;// 创建 kobject 的第一种方法// 创建并添加了名为mykobject01的 kobject 对象父 kobject 为 NULLmykobject01 kobject_create_and_add(mykobject01, NULL);// 创建并添加了名为mykobject02的 kobject 对象父 kobject 为 mykobject01。mykobject02 kobject_create_and_add(mykobject02, mykobject01);// 创建 kobject 的第二种方法// 1 使用 kzalloc 函数分配了一个 kobject 对象的内存mykobject03 kzalloc(sizeof(struct kobject), GFP_KERNEL);// 2 初始化并添加到内核中名为mykobject03。ret kobject_init_and_add(mykobject03, mytype, NULL, %s, mykobject03);return 0; }// 模块退出函数 static void mykobj_exit(void) {// 释放了之前创建的 kobject 对象kobject_put(mykobject01);kobject_put(mykobject02);kobject_put(mykobject03); } kobject_get()可用于增加 kobject的引用计数。         kobject_put()用于减少 kobject的引用计数。 /*增加kobject的引用计数*/ struct kobject *kobject_get(struct kobject *kobj);/*减少kobject的引用计数*/ void kobject_put(struct kobject *kobj);/*当kobject的引用计数降为0,会调用kobject_type的release方法释放*/ 驱动编译、装载后发现 kobject01kobject03 创建在系统根目录/sys 目录下kobject02 的父节点是 kobject01所以被创建在 mykobject02 目录下。 现在我们成功验证了创建 kobject 就是在系统根目录/sys 目录下创建一个文件夹他们是一一对应的关系。 80.6 创建 kset kset_create_and_add()用于创建 kset对象。 /*创建kset并添加相关操作*/ struct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj); /*kset事件操作函数集,用于处理与kset相关的热拔插事件*/ struct kset_uevent_ops { /*决定是否将某个事件传递到用户空间。当上报热插拔事件时kset 会通过 filter 函数来过滤事件如果 filter 返回 0则阻止该事件上报到用户空间。*/int (*const filter)(struct kset *kset, struct kobject *kobj);/*返回 kset 或其包含的 kobject 的名称*/const char *(*const name)(struct kset *kset, struct kobject *kobj); /*当需要向用户空间发送热插拔事件时uevent 函数会被调用它可以将用户空间需要的参数添加到环境变量中。*/int (*const uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env); }; struct kobj_uevent_env { char *argv[3]; // 用于存储传递给事件的参数通常表示事件的命令行参数 char *envp[UEVENT_NUM_ENVP]; // 包含环境变量指针的数组用于存储传递给事件的环境变量 int envp_idx; // 用于跟踪环境变量数组中的当前索引 char buf[UEVENT_BUFFER_SIZE]; // 用于存储事件的文本数据事件通常以文本形式表示 int buflen; // 用于跟踪事件数据缓冲区中的当前有效数据长度 }; // 定义 kobject 结构体指针用于表示第一个自定义内核对象 struct kobject *mykobject01; // 定义 kobject 结构体指针用于表示第二个自定义内核对象 struct kobject *mykobject02; // 定义 kset 结构体指针用于表示自定义内核对象的集合 struct kset *mykset; // 定义 kobj_type 结构体用于定义自定义内核对象的类型 struct kobj_type mytype;// 模块的初始化函数 static int mykobj_init(void) {int ret;// 创建并添加 kset名称为mykset父 kobject 为 NULL属性为 NULLmykset kset_create_and_add(mykset, NULL, NULL);// 为 mykobject01 分配内存空间大小为 struct kobject 的大小标志为 GFP_KERNELmykobject01 kzalloc(sizeof(struct kobject), GFP_KERNEL);/* 将 mykset 设置为 mykobject01 的 kset 属性 */mykobject01-kset mykset;// 初始化并添加 mykobject01类型为 mytype父 kobject 为 NULL名称为mykobject01ret kobject_init_and_add(mykobject01, mytype, NULL, %s, mykobject01);// 为 mykobject02 分配内存空间大小为 struct kobject 的大小标志为 GFP_KERNELmykobject02 kzalloc(sizeof(struct kobject), GFP_KERNEL);/* 将 mykset 设置为 mykobject02 的 kset 属性*/mykobject02-kset mykset;// 初始化并添加 mykobject02类型为 mytype父 kobject 为 NULL名称为mykobject02ret kobject_init_and_add(mykobject02, mytype, NULL, %s, mykobject02);return 0; }// 模块退出函数 static void mykobj_exit(void) {// 释放 mykobject01 的引用计数kobject_put(mykobject01);// 释放 mykobject02 的引用计数kobject_put(mykobject02); } 第八十一章 为什么要引入设备模型 81.1 设备模型简介 设备模型在内核驱动中至关重要它统一描述和管理设备简化了驱动开发提高了代码复用性和可维护性并支持热插拔和动态资源管理。 设备模型包含以下核心概念 总线Bus连接设备的通信通道可以是物理的如PCI、USB或虚拟的。 设备Device系统中的硬件设备如网卡、显示器等每个设备有唯一标识符。 驱动Driver控制和管理设备操作的软件与操作系统交互发送命令、接收事件等。 类Class逻辑组织单元对相似功能和特性的设备进行分类管理。 88.2 相关结构体 81.2 相关结构体 在Linux设备模型中存在一条名为“platform”的虚拟总线它专门用于管理那些直接与CPU相连、不符合常见总线标准如PCI、USB的设备控制器。 Platform总线为这些设备控制器提供了一个统一的注册和管理机制。 设备控制器通常在设备树中定义并通过设备树与相应的设备驱动程序进行匹配。 设备驱动程序通过注册到Platform总线能够与对应的设备控制器进行绑定和通信从而访问控制器的寄存器、配置设备、处理中断等。 总线 尽管在芯片原厂提供的 BSP 中已经实现了设备模型但是了解这些概念可以好地理解设备的工作原理驱动程序的编写和设备的管理。 81.2.1 struct bus_type bus_type 结构体是 Linux 内核中用于描述总线的数据结构。 以下是结构体包含的成员和其作用的简要说明 name:                总线类型名称         dev_name:        总线设备名称(格式化字符串总线设备名称前缀)         dev_root:           总线设备的根设备         bus_groups:      总线类型的属性组         dev_groups:      设备属性组         drv_groups:       驱动程序属性组 以下是一些回调函数成员 match:         设备和驱动程序之间的匹配函数         uevent:        设备的事件处理函数         probe:          设备的探测函数         sync_state:  设备状态同步函数         remove:       设备的移除函数         online:          设备上线函数         offline       设备离线函数         suspend:      设备的挂起函数         resume:        设备的恢复函数         num_vf:        设备的虚拟功能数目函数         dma_configure:设备的 DMA 配置函数 以下是一些其他成员 pm                        设备的电源管理操作。         iommu_ops          设备的 IOMMU 操作。         p                           子系统私有数据。         lock_key               用于锁机制的锁类别键。         need_parent_lock是否需要父级锁。 //定义在 include/linux/device.h头文件中。/*总线类型结构体*/ struct bus_type { // 总线的名称用于唯一标识该总线类型 const char *name; // 设备名称的格式字符串用于生成连接到该总线上的设备的默认名称 const char *dev_name; // 指向该总线上所有设备的根节点的指针作为所有设备的父节点 struct device *dev_root; // 指向总线属性组的指针数组用于定义和导出总线的属性 const struct attribute_group **bus_groups; // 指向设备属性组的指针数组用于定义和导出连接到该总线上的设备的属性 const struct attribute_group **dev_groups; // 指向驱动属性组的指针数组用于定义和导出与该总线相关的驱动的属性 const struct attribute_group **drv_groups; // 匹配函数用于判断设备和驱动是否匹配 int (*match)(struct device *dev, struct device_driver *drv); // uevent函数用于生成并发送设备事件到用户空间 int (*uevent)(struct device *dev, struct kobj_uevent_env *env); // 探测函数当设备被添加到系统时调用用于初始化设备 int (*probe)(struct device *dev); // 同步状态函数用于同步设备的状态 void (*sync_state)(struct device *dev); // 移除函数当设备从系统中移除时调用 int (*remove)(struct device *dev); // 关机函数在系统关机时调用 void (*shutdown)(struct device *dev); // 上线函数用于将设备设置为在线状态 int (*online)(struct device *dev); // 下线函数用于将设备设置为离线状态 int (*offline)(struct device *dev); // 挂起函数用于将设备挂起到指定的电源状态 int (*suspend)(struct device *dev, pm_message_t state); // 恢复函数用于从挂起状态恢复设备 int (*resume)(struct device *dev); // 获取设备支持的虚拟功能数量的函数 int (*num_vf)(struct device *dev); // DMA配置函数用于配置设备的DMA能力 int (*dma_configure)(struct device *dev); // 指向电源管理操作结构体的指针定义了设备的电源管理行为 const struct dev_pm_ops *pm; // 指向IOMMU操作结构体的指针定义了IOMMU相关的操作 const struct iommu_ops *iommu_ops; // 指向子系统私有数据的指针用于存储子系统的内部状态和信息 struct subsys_private *p; // 锁类关键字用于调试和性能分析确保锁的唯一性和正确性 struct lock_class_key lock_key; // 布尔值指示是否需要对父设备进行加锁操作 bool need_parent_lock; // 保留字段用于Android内核ABI的兼容性保留 ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3); ANDROID_KABI_RESERVE(4); }; 81.2.2 struct device device 结构体是 Linux 内核中用于描述设备的数据结构。 //定义在 include/linux/device.h 头文件中/*设备结构体*/ struct device { // 设备的父设备指针如果设备是顶级设备则此指针为NULL struct device *parent; // 指向设备私有数据的指针通常用于存储设备的内部状态和信息 struct device_private *p; // 与设备相关联的内核对象提供了对象管理和属性导出等功能 struct kobject kobj; // 设备初始化时指定的名称可能用于设备的唯一标识或调试 const char *init_name; // 指向设备类型的指针定义了设备的行为和特性 const struct device_type *type; // 设备所属的总线类型指针用于将设备与总线相关联 struct bus_type *bus; // 指向设备驱动的指针如果设备已绑定到驱动则此指针非空 struct device_driver *driver; // 设备所属的类指针用于将设备分组到特定的设备类中 struct class *class; // 指向设备属性组的指针数组用于定义和导出设备的属性 // 这些属性可以在用户空间通过sysfs文件系统访问 const struct attribute_group **groups; /* optional groups */ // 省略了其他成员... }; 81.2.3 struct device_driver device_driver结构体是Linux内核中描述设备驱动程序的数据结构。 //定义在include/linux/device.h 头文件中。/*设备驱动结构体*/ struct device_driver { // 驱动的名称通常用于日志输出、调试和匹配设备 const char *name; // 指向设备总线类型的指针表示该驱动支持的总线 struct bus_type *bus; // 指向驱动所属模块的指针用于模块加载和卸载时的引用计数 struct module *owner; // 内置模块的名称当驱动是内置到内核中时此字段用于标识模块 const char *mod_name; /* used for built-in modules */ // 是否禁止通过sysfs文件系统绑定或解绑设备 bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ // 探测类型指定驱动是如何被探测和加载的 enum probe_type probe_type; // 指向设备树Device Tree匹配表的指针用于基于设备树信息的设备匹配 const struct of_device_id *of_match_table; // 指向ACPI匹配表的指针用于基于ACPI信息的设备匹配 const struct acpi_device_id *acpi_match_table; // 当设备被探测到时调用的回调函数用于初始化设备 int (*probe) (struct device *dev); // 同步设备状态的回调函数用于更新设备的状态信息 void (*sync_state)(struct device *dev); // 当设备被移除时调用的回调函数用于清理设备资源 int (*remove) (struct device *dev); // 当系统关机时调用的回调函数用于执行驱动级的关机操作 void (*shutdown) (struct device *dev); // 当设备进入挂起状态时调用的回调函数用于保存设备状态 int (*suspend) (struct device *dev, pm_message_t state); // 当设备从挂起状态恢复时调用的回调函数用于恢复设备状态 int (*resume) (struct device *dev); // 指向属性组的指针数组用于定义和导出驱动的属性 const struct attribute_group **groups; // 指向电源管理操作的指针包含了挂起、恢复等电源管理相关的回调函数 const struct dev_pm_ops *pm; // 当设备需要生成coredump时调用的回调函数 void (*coredump) (struct device *dev); // 指向驱动私有数据的指针通常用于存储驱动的内部状态和信息 struct driver_private *p; // 保留字段用于Android内核ABIApplication Binary Interface的兼容性保留 // 这些字段在当前版本中未使用但为未来扩展保留 ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3); ANDROID_KABI_RESERVE(4); }; 81.2.4 struct class class结构体是 Linux 内核中描述设备类的数据结构。 //定义在 include/linux/device.h 头文件中。/*设备类*/ struct class { // 类的名称通常用于日志输出、调试和sysfs文件系统中的目录名 const char *name; // 指向类所属模块的指针用于模块加载和卸载时的引用计数 struct module *owner; // 指向类属性组的指针数组用于定义和导出类的属性 // 这些属性可以在用户空间通过sysfs文件系统访问 const struct attribute_group **class_groups; // 指向设备属性组的指针数组这些属性是应用于该类下所有设备的 // 与class_groups不同这些属性更具体于设备实例 const struct attribute_group **dev_groups; // 指向类设备对象的指针是类在sysfs中的顶级目录对象 struct kobject *dev_kobj; // 当设备发生事件时如添加、移除等此回调函数用于生成uevent消息 int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env); // 当需要获取设备节点如设备文件的名称和模式时调用的回调函数 char *(*devnode)(struct device *dev, umode_t *mode); // 当类被卸载时调用的回调函数用于执行清理操作 void (*class_release)(struct class *class); // 当设备被释放时调用的回调函数用于执行设备特定的清理操作 void (*dev_release)(struct device *dev); // 在系统关机前针对该类下的设备调用的预处理回调函数 int (*shutdown_pre)(struct device *dev); // 指向命名空间类型操作的指针定义了与命名空间相关的操作 // 如挂载、卸载命名空间等注意这在某些内核版本中可能不存在 const struct kobj_ns_type_operations *ns_type; // 当需要获取设备的命名空间时调用的回调函数 // 这通常与设备文件的创建和访问权限有关 const void *(*namespace)(struct device *dev); // 获取设备所有权的回调函数用于设置设备的用户ID和组ID void (*get_ownership)(struct device *dev, kuid_t *uid, kgid_t *gid); // 指向电源管理操作的指针包含了挂起、恢复等电源管理相关的回调函数 const struct dev_pm_ops *pm; // 指向类私有数据的指针通常用于存储类的内部状态和信息 struct subsys_private *p; // 保留字段用于Android内核ABIApplication Binary Interface的兼容性保留 // 这些字段在当前版本中未使用但为未来扩展保留 ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3); ANDROID_KABI_RESERVE(4); }; 第八十二章 进一步探究设备模型 为什么当创建 kobj 的时候父节点为 NULL会在系统根目录/sys 目录下创建呢。 82.1 什么是 sysfs 文件系统 sysfs是Linux内核的一个虚拟文件系统它在/sys目录下以层次结构展示设备和内核对象的信息。用户空间程序可以通过文件系统接口访问这些信息从而浏览、获取和配置内核中的设备、总线、驱动程序等对象。 82.2 设备模型的基本框架 kobject和 kset构成了Linux设备模型的基本框架因为它们为内核对象提供了统一的表示和管理机制。 kobject是内核对象的基础它包含了对象的基本属性和与设备模型集成的接口。 kset则是kobject的集合提供了对一组相似对象的统一管理。 在Linux设备模型中许多重要的结构体都嵌入了kobject 如cdev字符设备和platform_device平台设备等。 这样做是为了将这些高级对象接入到设备模型中使它们能够利用 kobject提供的机制进行管理和访问。 例如cdev结构体中嵌入了kobject使其能够作为设备模型中的一个对象被管理和访问。 同样platform_device结构体中包含了device结构体而device结构体又嵌入了kobject。 这样platform_device就能够通过 device和 kobject与设备模型进行集成。 所以我们也可以把总线设备驱动看作是 kobject 的派生类。因为他们都是设备模型中的实体通过继承或扩展 kobject 来实现与设备模型的集成。 在 Linux 内核中kobject 是一个通用的基础结构用于构建设备模型。每个 kobject 实例对应于 sys 目录下的一个目录这个目录包含了该 kobject 相关的属性操作和状态信息。 每个kobjedt实例对应于 /sys/目录下的一个目录 因此可以说 kobject 是设备模型的基石通过创建对应的目录结构和属性文件 它提供了一个统一的接口和框架用于管理和操作设备模型中的各个实体。 82.3 代码层面分析 在系统启动的时候会在/sys 目录下创建以下目录 系统启动时在/sys下自动创建的目录 当 kobject_create_and_add() 的 parent 参数为 NULL 时新创建的 kobject 会在 /sys 根目录下创建相应的目录。 这是因为在 kobject_add_internal() 和 sysfs_create_dir_ns() 中如果 kobj-parent 为 NULL则使用 sysfs_root_kn即 /sys 根目录的节点作为父节点。 sysfs_root_kn 是在系统启动时通过 sysfs_init() 初始化的。 sysfs_init()在系统启动时初始化 sysfs创建 sysfs_root 和 sysfs_root_kn分别代表 sysfs 的根和根节点。 sysfs_root_kn 是 sysfs 根目录的节点代表 /sys。  第八十三章 虚拟文件系统 sysfs 目录层次分析 83.1 sys目录对应设备模型的层次结构 我们进入到 Linux 系统的/sys 目录下可以看到如下文件夹 /sys目录 和设备模型有关的文件夹为 busclassdevices。 /sys/bus /sys/class /sys/devices /sys/devices该目录包含了系统中所有设备的子目录。 每个设备子目录代表一个具体的设备。 每个设备子目录中包含了设备的属性、状态和其他相关信息。 /sys/devices下包含系统所有设备的子目录。设备子目录代表具体的设备 /sys/bus该目录包含了总线类型的子目录。 每个子目录代表一个特定类型的总线例如 PCI、USB 等。 每个总线子目录中包含与该总线相关的设备和驱动程序的信息。 /sys/bus下包含系统所有总线的子目录。总线子目录代表总线相关的设备和驱动信息 /sys/class该目录包含了设备类别的子目录。 每个子目录代表一个设备类别例如磁盘、网络接口等。 每个设备类别子目录中包含了属于该类别的设备的信息。 /sys/class下包含设备类别的子目录每个子目录代表一个设备类别含属于该类别的设备的信息 使用class进行归类可以提供一种扩展性和可移植性的机制。 当引入新的设备类型时可将其归类到现有的类别中无需修改现有设备管理和驱动程序。 比如应用现在要设置 gpio 如果使用类可以直接使用以下命令 echo 1 /sys/class/gpio/gpio157/value 如果不使用类使用以下命令 echo 1 /sys/devices/platform/fe770000.gpio/gpiochip4/gpio/gpio157/value 83.2 sys 目录层次图解 Sys 目录层次结构如下图所示 sys目录层次结构 第八十四章 引用计数器 84.1 引用计数器介绍 引用计数器管理内存通过计数引用数量决定何时释放资源。 对象创建时计数为1引用增加时计数上升引用失效时计数下降计数为0时释放资源。 84.2 引用计数器 kref 介绍 kref 是 Linux 内核中提供的一种引用计数器实现。 它是一种轻量级的引用计数技术用于管理内核中的对象的引用计数。 // 定义一个结构体 kref用于表示具有引用计数的对象 struct kref { // refcount 是一个 refcount_t 类型的成员用于存储引用计数 refcount_t refcount; }; // 使用 typedef 定义一个名为 refcount_t 的新类型它是一个结构体 typedef struct { atomic_t refs; // refs 是一个 atomic_t 类型的成员确保引用计数的原子性操作 } refcount_t; typedef struct { int counter; // 在实际应用中不应直接操作这个成员而应使用原子操作函数 } atomic_t; kobject结构体中就有 kref结构体成员 以实现引用计数的管理。 这样可以通过对 struct kref 的操作来对 struct kobject 进行引用计数的管理并在引用计数减少到 0 时释放相关资源。 device_node结构体中有 kobject结构体成员简介完成内核对象的实现和引用计数的管理。 84.3 常用 api 函数 kref_init()用于初始化 kref结构体对象设置引用计数为 1。 kref_get()结构体 kref 的引用计数加1。 kref_put()结构体 kref 的引用计数减1并在计数为零时调用释放函数release()。 refcount_set()设置 refcount_t 的计数值。 kref_init()用于初始化 kref结构体对象设置引用计数为 1。 /*初始化 kerf结构体,引用计数值给1*/ static inline void kref_init(struct kref *kref) { refcount_set(kref-refcount, 1); } kref_get()结构体 kref 的引用计数加1。 /*kerf引用计数值加一*/ static inline void kref_get(struct kref *kref) { refcount_inc(kref-refcount); } kref_put()结构体 kref 的引用计数减1并在计数为零时调用释放函数release()。 /*kerf引用计数减一*/ static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref)) { if (refcount_dec_and_test(kref-refcount)) { release(kref); return 1; // 引用计数已为零资源已释放 } return 0; // 引用计数未为零资源未释放 } refcount_set()设置 refcount_t 的计数值。 /*设置计数值*/ static inline void refcount_set(refcount_t *r, int n) { atomic_set(r-refs, n); } 84.4 引用计数器实验 // 定义了三个 kobject 指针变量mykobject01、mykobject02、mykobject03 struct kobject *mykobject01; struct kobject *mykobject02; struct kobject *mykobject03;// 定义了一个 kobj_type 结构体变量 mytype用于描述 kobject 的类型。 struct kobj_type mytype; // 模块的初始化函数 static int mykobj_init(void) {int ret;// 创建 kobject 的第一种方法// 创建并添加了名为mykobject01的 kobject 对象父 kobject 为 NULLmykobject01 kobject_create_and_add(mykobject01, NULL);printk(mykobject01 kref is %d\n, mykobject01-kref.refcount.refs.counter);// 创建并添加了名为mykobject02的 kobject 对象父 kobject 为 mykobject01。mykobject02 kobject_create_and_add(mykobject02, mykobject01);printk(mykobject01 kref is %d\n, mykobject01-kref.refcount.refs.counter);printk(mykobject02 kref is %d\n, mykobject02-kref.refcount.refs.counter);// 创建 kobject 的第二种方法// 1 使用 kzalloc 函数分配了一个 kobject 对象的内存mykobject03 kzalloc(sizeof(struct kobject), GFP_KERNEL);// 2 初始化并添加到内核中名为mykobject03。ret kobject_init_and_add(mykobject03, mytype, NULL, %s, mykobject03);printk(mykobject03 kref is %d\n, mykobject03-kref.refcount.refs.counter);return 0; }// 模块退出函数 static void mykobj_exit(void) {printk(mykobject01 kref is %d\n, mykobject01-kref.refcount.refs.counter);printk(mykobject02 kref is %d\n, mykobject02-kref.refcount.refs.counter);printk(mykobject03 kref is %d\n, mykobject03-kref.refcount.refs.counter);// 释放了之前创建的 kobject 对象kobject_put(mykobject01);printk(mykobject01 kref is %d\n, mykobject01-kref.refcount.refs.counter);printk(mykobject02 kref is %d\n, mykobject02-kref.refcount.refs.counter);printk(mykobject03 kref is %d\n, mykobject03-kref.refcount.refs.counter);kobject_put(mykobject02);printk(mykobject01 kref is %d\n, mykobject01-kref.refcount.refs.counter);printk(mykobject02 kref is %d\n, mykobject02-kref.refcount.refs.counter);printk(mykobject03 kref is %d\n, mykobject03-kref.refcount.refs.counter);kobject_put(mykobject03);printk(mykobject01 kref is %d\n, mykobject01-kref.refcount.refs.counter);printk(mykobject02 kref is %d\n, mykobject02-kref.refcount.refs.counter);printk(mykobject03 kref is %d\n, mykobject03-kref.refcount.refs.counter); } 引用计数图解 如上图 III 所示如果在 objectA 下面创建俩个 objectobjectA 的计数器值为 3。 如上图所示 IV如果在 objectA 下面创建俩个 object那么 objectA 的计数器值为 3在 objectB 下创建 object,那么 objectB 的计数器值为 2objectC 的计数器值为 1。 第八十五 kobject 释放实例分析 当引用计数器的值变为 0后会自动调用自定义的释放函数release()去执行释放的操作。 kobject的创建 kobject_create_and_add()函数创建 kobj对象 分配内存kzalloc。 初始化kobjectkobject_init设置默认ktype。 将kobject添加到系统中kobject_add创建sysfs文件系统条目。 struct kobject * kobject_create_and_add(const char *name, //kobject名称struct kobject *parent, //父kobjectconst struct kobj_type *ktype) //kobj_type类型 kobject_init_and_add()函数创建 kobj对象 手动分配内存。 初始化kobjectkobject_init需要手动实现ktype结构体。 将kobject添加到系统中kobject_add创建sysfs文件系统条目。 /*手动创建*/ int kobject_init_and_add(struct kobject *kobj, //kobject指针const char *name, //kobject名称struct kobject *parent, //父kobjectconst struct kobj_type *ktype) //kobj_type类型 kobject的释放 kobject_put()减少kobject的引用计数并在计数为0时调用release()释放资源。 kobject_release()是默认释放函数底层调用kobject_cleanup()进行资源清理。 /*kobject释放函数*/ static void kobject_release(struct kref *kref) { struct kobject *kobj container_of(kref, struct kobject, kref); kobject_cleanup(kobj); } 资源清理 kobject_cleanup()函数处理资源清理。 检查kobj_type结构体中的release函数 当kobject的引用计数降为零时内核会检查该kobject的kobj_type结构体中是否定义了release回调函数。如果定义了release函数内核会在后续步骤中调用它。发送remove事件如果未发送 在释放kobject之前内核通常会确保已经向用户空间发送了表示kobject被移除的remove事件如果之前还没有发送的话。这是通过sysfs文件系统完成的sysfs会监视kobject的状态变化并在必要时向用户空间广播事件。从sysfs中删除kobject如果未删除 内核会从sysfs文件系统中删除与该kobject对应的目录和文件。这确保了用户空间无法再通过sysfs访问该kobject。调用kobj_type中的release函数如果存在 如果kobj_type结构体中定义了release回调函数内核会在从sysfs中删除kobject之后调用它。release函数负责执行与kobject相关的任何特定清理工作比如释放内存、关闭文件描述符等。释放动态分配的名称如果存在 如果kobject的名称是动态分配的即不是在编译时静态确定的内核会释放这个名称所占用的内存。这是资源管理的一部分确保不会泄漏任何动态分配的资源。 /*kobject资源清理*/ static void kobject_cleanup(struct kobject *kobj) { struct kobj_type *t get_ktype(kobj); //获取 kobject的 kobj_type成员const char *name kobj-name; //获取 kobj的名称// 检查并调用kobj_type中的release函数等 if (t t-release) { t-release(kobj); } if (name) { //如果名称存在kfree_const(name); //动态释放} } kobject_cleanup() 函数的实现表明最终调用的释放函数是在 kobj_type 结构体中定义的。这解释了为什么在使用 kobject_init_and_add() 函数时kobj_type 结构体不能为空的原因。 因为释放函数是在 kobj_type 结构体中定义的如果不实现释放函数就无法进行正确的资源释放。 动态 kobject的 ktype dynamic_kobj_ktype定义了动态创建的 kobject的类型。 指定了释放函数dynamic_kobj_release()。 /*动态 kobj_type */ static struct kobj_type dynamic_kobj_ktype { .release dynamic_kobj_release, //指向struct sysfs_ops结构体的指针该结构体定义了与sysfs文件系统交互的操作。.sysfs_ops kobj_sysfs_ops, }; /*动态 kobj的释放函数*/ static void dynamic_kobj_release(struct kobject *kobj) { kfree(kobj); } 第八十六章 引入并完善 kobject_type 结构体 // 定义了 kobject 指针变量mykobject03 struct kobject *mykobject03;// 定义 kobject 的释放函数 static void dynamic_kobj_release(struct kobject *kobj) {printk(kobject: (%p): %s\n, kobj, __func__);kfree(kobj); }// 定义了一个 kobj_type 结构体变量 mytype用于描述 kobject 的类型。 struct kobj_type mytype {.release dynamic_kobj_release, };// 模块的初始化函数 static int mykobj_init(void) {int ret;// 创建 kobject 的第二种方法// 1 使用 kzalloc 函数分配了一个 kobject 对象的内存mykobject03 kzalloc(sizeof(struct kobject), GFP_KERNEL);// 2 初始化并添加到内核中名为mykobject03。ret kobject_init_and_add(mykobject03, mytype, NULL, %s, mykobject03);return 0; }// 模块退出函数 static void mykobj_exit(void) {kobject_put(mykobject03);//释放kref引用计数 } 第八十七章 创建属性文件并实现读写功能 sysfs虚拟文件系统下有 bus、class、devices目录 分别包括 不同总线 bus下的设备不同 class下的设备和代表具体设备 device的子目录。 sysfs虚拟文件系统基于 kobject和 kset实现基本的设备管理机制。 在使用 kobject_init_and_add()初始化 kobj指针时可以手动给定 kobj_type类型 kobj_type类型有 release()成员、sysfs_ops结构体成员、属性attribute结构体数组成员 sysfs_ops结构体成员有 show()和 store()成员 attribute结构体有 属性名称name和 文件模式mode成员。  所谓创建属性文件 就是完善 kobject结构体的 kobj_type结构体成员的 sysfs_ops结构体成员的 show()和 store()。 // 自定义的 kobject 结构体包含一个 kobject 对象和两个整型值 struct mykobj {struct kobject kobj;int value1;int value2; };// 自定义的 kobject 释放函数 static void dynamic_kobj_release(struct kobject *kobj) {struct mykobj *mykobject01 container_of(kobj, struct mykobj, kobj);printk(kobject: (%p): %s\n, kobj, __func__);kfree(mykobject01); }// 自定义的 attribute 对象 value1 和 value2 struct attribute value1 {.name value1, .mode 0666, };struct attribute value2 {.name value2, .mode 0666, };// 将 attribute 对象放入数组中 struct attribute *myattr[] {value1, value2, NULL, };// 自定义的 show 函数用于读取属性值 ssize_t myshow(struct kobject *kobj, struct attribute *attr, char *buf) {ssize_t count;struct mykobj *mykobject01 container_of(kobj, struct mykobj, kobj);if (strcmp(attr-name, value1) 0){count sprintf(buf, %d\n, mykobject01-value1);}else if (strcmp(attr-name, value2) 0){count sprintf(buf, %d\n, mykobject01-value2);}else{count 0;}return count; }// 自定义的 store 函数用于写入属性值 ssize_t mystore(struct kobject *kobj, struct attribute *attr, const char *buf, size_t size) {/*获取完整的结构体*/struct mykobj *mykobject01 container_of(kobj, struct mykobj, kobj);/*比较被操作的属性文件的名称*/if (strcmp(attr-name, value1) 0){sscanf(buf, %d\n, mykobject01-value1);//写入自定义的结构体的value1}else if (strcmp(attr-name, value2) 0){sscanf(buf, %d\n, mykobject01-value2);写入自定义的结构体的value2}return size; }// 自定义的 sysfs_ops 结构体包含 show 和 store 函数指针 struct sysfs_ops myops {.show myshow, .store mystore, };/*包含释放函数、默认属性和 sysfs_ops*/ static struct kobj_type mytype {.release dynamic_kobj_release, //填充 realease函数.default_attrs myattr, //填充 attribute结构体.sysfs_ops myops, //填充sysfs_ops操作集 };// 定义了 mykobj 结构体指针变量 mykobject01 struct mykobj *mykobject01;// 模块的初始化函数 static int mykobj_init(void) {int ret;// 分配并初始化 mykobject01mykobject01 kzalloc(sizeof(struct mykobj), GFP_KERNEL);mykobject01-value1 1;mykobject01-value2 1;// 初始化并添加 mykobject01 到内核中名为mykobject01ret kobject_init_and_add(mykobject01-kobj, //kobj指针mytype, //kobj_type类型,NULL, //父object指针mykobject01); //kobj的名称return 0; }// 模块的退出函数 static void mykobj_exit(void) {// 释放 mykobject01kobject_put(mykobject01-kobj); } module_init(mykobj_init); // 指定模块的初始化函数 module_exit(mykobj_exit); // 指定模块的退出函数 驱动加载之后我们进入/sys/目录下可以看到创建生成的 myobject01 因为 每个kobject对象对应于/sys/目录下的一个文件夹。 kobject注册后对应/sys/目录下的文件夹 进到 myobject01 目录下可以看到创建的属性文件 value1 和 value2。  kobj_type结构体的attribute结构体数组也对应属性文件夹 我们可以使用 echo 和 cat 命令对属性值进行写入和读取 向属性文件进行写入和读取其实是调用 kobj的 kobj_type的 show()和store() 第八十八章 优化属性文件读写函数 kobj_type的 attribute原填充方式。 // 自定义的 attribute 对象 value1 和 value2 struct attribute value1 {.name value1, .mode 0666, };struct attribute value2 {.name value2, .mode 0666, };// 将 attribute 对象放入数组中 struct attribute *myattr[] {value1, value2, NULL, };// 自定义的 sysfs_ops 结构体包含 show 和 store 函数指针 struct sysfs_ops myops {.show myshow, .store mystore, };/*包含释放函数、默认属性和 sysfs_ops*/ static struct kobj_type mytype {.release dynamic_kobj_release, //填充 realease函数.default_attrs myattr, //填充 attribute结构体.sysfs_ops myops, //填充sysfs_ops操作集 }; 使用 __ATTR()宏定义声明并初始化 kobj_attribute对象。 // 定义 attribute 对象 value1 和 value2 假设已经完成了 自定义的show和store函数 struct kobj_attribute value1 __ATTR(value1, //对象,声明的同时定义0664, //权限show_myvalue1, //show函数store_myvalue1); //store函数 struct kobj_attribute value2 __ATTR(value2, 0664, show_myvalue2, store_myvalue2);// 将 attribute 对象放入数组中 struct attribute *myattr[] {value1.attr, value2.attr, NULL, };// 自定义的 sysfs_ops 结构体包含 show 和 store 函数指针 struct sysfs_ops myops {.show myshow, .store mystore,}; //!!!注意这里绑定的函数才是读写属性文件时回调的// 自定义的 kobj_type 结构体包含释放函数、默认属性和 sysfs_ops static struct kobj_type mytype {.release dynamic_kobj_release, .default_attrs myattr, .sysfs_ops myops, }; __ATTR宏是用于快速创建并初始化 kobj_attribute结构体的便捷方式。这个宏接受几个参数包括属性的名称、权限使用八进制表示法、以及指向show和store回调函数的指针。 struct kobj_attribute { struct attribute attr; //kobj_type的attribute结构体成员ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, char *buf); ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); }; 第八十九章 创建多个属性文件的简便方法 前面几章我们通过为 kobj_type绑定 attribute数组来创建属性文件。 但如果要创建大量属性文件这种方法显然低效。 89.1 sysfs_create_group 函数 sysfs_create_group() 函数用于在 sysfs 中创建一个组(group)。 组是一组相关的属性文件的集合可将它们放在同一个目录下提供更好的组织性和可读性。 /*在sysfs文件系统中创建一个组*/ int sysfs_create_group(struct kobject *kobj, //指向包含目标组的 kobject 的指针。const struct attribute_group *grp); //指向 attribute_group 结构体的指针//该结构体定义了组中的属性文件。 struct attribute_group { const char *name; // 组名用于sysfs中的目录名 const struct attribute **attrs; // 指向属性指针数组的指针数组以NULL结尾 // 可选回调函数用于判断属性是否可见 mode_t (*is_visible)(struct kobject *kobj, struct attribute *attr, int index); }; 下面展示使用 sysfs_create_group() 创建一个组并添加属性文件 // 定义 attribute 对象 value1 和 value2 struct kobj_attribute value1 __ATTR(value1, 0664, show_myvalue1, store_myvalue1); struct kobj_attribute value2 __ATTR(value2, 0664, show_myvalue2, store_myvalue2);/*将 attribute对象添加进数组*/ struct attribute *attr[] {value1.attr, value2.attr, NULL, };/*创建一个名为 myattr的组,并将 attribute添加进组中*/ const struct attribute_group my_attr_group {.name myattr, .attrs attr, };// 模块的初始化函数 static int mykobj_init(void) {int ret;// 创建并添加 kobject mykobject01 mykobject01 kobject_create_and_add(mykobject01, NULL);//将组添加进 kobjret sysfs_create_group(mykobject01, my_attr_group);return ret; }// 模块的退出函数 static void mykobj_exit(void) {// 释放 kobject mykobject01 kobject_put(mykobject01); } 第九十章 注册一个自己的总线 在 设备模型中包含 总线、 设备、 驱动和 类四个概念。 90.1 bus_register()函数 我们进入开发板的/sys/bus 目录下/sys/bus 是 Linux 系统中的一个目录用于表示总线bus子系统的根目录。如果我们自己注册一个总线会在此目录下显示。 /sys/bus目录 bus_register() 函数用于将一个自定义总线注册到 Linux 内核中。 /*注册一个总线到内核中*/ int bus_register(struct bus_type *bus); bus_unregister() 函数用于取消注册一个已经注册的自定义总线。 /*取消注册一个自定义总线到内核*/ void bus_unregister(struct bus_type *bus); /*匹配函数*/ int mybus_match(struct device *dev, struct device_driver *drv) {// 检查设备名称和驱动程序名称是否匹配return (strcmp(dev_name(dev), drv-name) 0); }/*探测函数*/ int mybus_probe(struct device *dev) {struct device_driver *drv dev-driver;if (drv-probe)drv-probe(dev);return 0; }struct bus_type mybus {.name mybus, // 总线的名称.match mybus_match, // 设备和驱动程序匹配的回调函数.probe mybus_probe, // 设备探测的回调函数 };// 模块的初始化函数 static int bus_init(void) {int ret;ret bus_register(mybus); // 注册总线return 0; }// 模块退出函数 static void bus_exit(void) {bus_unregister(mybus); // 取消注册总线 } module_init(bus_init); // 指定模块的初始化函数 module_exit(bus_exit); // 指定模块的退出函数 驱动加载之后我们进入/sys/bus 目录下可以看到创建生成的总线 mybus 注册的自定义总线文件 进到 mybus 目录下可以看到创建的属性文件 自定义总线的属性文件 第九十一章 在总线目录下创建属性文件 91.1 bus_create_file()函数 bus_create_file() 函数用于在总线的 sysfs 目录下创建一个属性文件。 int bus_create_file(struct bus_type *bus, //bus_type用来描述总线struct kobject *kobj, //kobject内核成员 const struct attribute *attr);//属性,成员有name和mode struct bus_attribute mybus_attr {/*attribute成员*/.attr {.name value, .mode 0664, },.show mybus_show, //show方法//省略了store方法};/*总线下创建属性文件*/ ret bus_create_file(mybus, mydevice.kobj, mybus_attr.attr);// 模块的初始化函数 static int bus_init(void) {int ret;ret bus_register(mybus); // 注册总线ret bus_create_file(mybus, mybus_attr); // 在 sysfs 中创建属性文件return 0; } 上述示例代码创建了一个名为 value 的属性文件并指定了访问权限为 0664。在创建属性文件时还可指定其他属性的回调函数如 .show、.store 等实现对属性值的读取和写入操作。 驱动加载之后我们进入/sys/bus 目录下可以看到创建生成的总线 mybus我们进到 mybus 目录下可以看到创建属性文件 value。 总线下创建属性 使用 cat或者 echo对属性文件进行读写可触发 show、store函数。 cat属性文件触发show函数 第九十二章 总线注册流程分析 92.1 bus_register 函数解析 开发板上电我们进入到开发板的/sys/bus/mybus 目录下。 为什么在 sys/bus 目录下会生成 mybus 目录以及对应的 devicesdriversdrivers_autoprobedrivers_probeuevent 目录和属性呢 bus_register()函数的主要步骤 1、分配并初始化subsys_private结构体 struct subsys_private { struct kset subsys; struct kset *devices_kset; struct kset *drivers_kset; // 其他成员... }; priv kzalloc(...); priv-bus bus; //将priv与当前总线关联 bus-p priv; //让总线快速访问priv 2、初始化阻塞通知链表 BLOCKING_INIT_NOTIFIER_HEAD(priv-bus_notifier); 3、设置子系统名称 kobject_set_name(priv-subsys.kobj, %s, bus-name); 4、设置子系统属性 priv-subsys.kobj.kset bus_kset; //设置kset priv-subsys.kobj.ktype bus_ktype; //设置 bus_ktype 5、注册子系统的 kset kset_register(priv-subsys); 6、创建并添加devices和drivers子目录的 kset /*创建子设备的kset*/ priv-devices_kset kset_create_and_add(devices, NULL, priv-subsys.kobj); /*创建子驱动的kset*/ priv-drivers_kset kset_create_and_add(drivers, NULL, priv-subsys.kobj); 7、初始化接口链表、互斥锁和设备/驱动的 klist klist_init(priv-klist_devices, klist_devices_get, klist_devices_put); klist_init(priv-klist_drivers, NULL, NULL); 8、添加驱动探测文件和总线属性组 add_probe_files(bus); //驱动探测文件 bus_add_groups(bus, bus-bus_groups);//总线属性组 第九十三章 platform 总线注册流程实例分析 在内核初始化过程中platform_bus_init() 函数负责初始化平台总线。 清理早期平台设备 early_platform_cleanup() 函数清空早期平台设备列表上的所有节点。 注册平台总线设备 调用 device_register(platform_bus) 将 platform_bus 结构体注册到设备子系统中。 注册平台总线类型 调用 bus_register(platform_bus_type) 将 platform_bus_type 结构体注册到总线子系统中。 platform_bus_type 结构体定义了平台总线的关键属性包括 struct bus_type platform_bus_type { .name platform, .dev_attrs platform_dev_attrs, // 设备属性可能包含获取sys文件名等 .match platform_match, // 匹配设备和驱动的函数 .uevent platform_uevent, // 消息传递函数 .pm platform_dev_pm_ops, // 电源管理操作 // ... 可能还有其他成员具体取决于内核版本和配置 }; 电源管理操作platform_dev_pm_ops 这个结构体包含了与电源管理相关的操作函数如挂起、恢复等。 这些函数在系统进入休眠或唤醒状态时会被调用以管理平台上设备的电源状态。 注意platform_bus_init()注册的 platform总线和 bus_init()注册的总线有一些区别。 在Linux内核中platform总线用于管理与硬件平台紧密相关的设备。         为了注册platform设备首先需要注册一个platform bus设备作为桥梁。         这个bus设备通过device_register()函数添加到设备层次结构并与platform总线关联。         这样platform总线初始化时就能识别并管理platform设备。 第九十四章 在总线下注册设备实验 定义了一个名为 mybus 的总线 并实现了总线的匹配回调函数 mybus_match 和设备探测回调函数 mybus_probe。 同时还定义了一个名为 value 的属性文件并实现了属性的显示回调函数 mybus_show。 /*总线模块代码*/ /*设备和驱动匹配函数*/ int mybus_match(struct device *dev, struct device_driver *drv) {// 检查设备名称和驱动程序名称是否匹配return (strcmp(dev_name(dev), drv-name) 0); };/*设备探测回调函数*/ int mybus_probe(struct device *dev) { // 获取与设备关联的驱动程序的指针 struct device_driver *drv dev-driver; // 检查驱动程序是否有probe函数 if (drv-probe) { // 调用驱动程序的probe函数并传递设备指针作为参数 drv-probe(dev); } // 返回0表示成功在Linux内核中0通常表示成功非0值表示错误 return 0; }struct bus_type mybus {.name mybus, // 总线的名称.match mybus_match, // 设备和驱动程序匹配的回调函数.probe mybus_probe, // 设备探测的回调函数 };/*EXPORT_SYMBOL_GPL宏用于导出内核符号使其对其他模块这些模块也必须遵循GPL协议可见。*/ EXPORT_SYMBOL_GPL(mybus); ssize_t mybus_show(struct bus_type *bus, char *buf) {// 在 sysfs 中显示总线的值return sprintf(buf, %s\n, mybus_show); };struct bus_attribute mybus_attr {.attr {.name value, // 属性的名称.mode 0664, // 属性的访问权限},.show mybus_show, // 属性的 show 回调函数 };// 模块的初始化函数 static int bus_init(void) {int ret;ret bus_register(mybus); // 注册总线ret bus_create_file(mybus, mybus_attr); // 在 sysfs 中创建属性文件return 0; }// 模块退出函数 static void bus_exit(void) {bus_remove_file(mybus, mybus_attr); // 从 sysfs 中移除属性文件bus_unregister(mybus); // 取消注册总线 } 我们编写驱动文件 device.c在驱动中Linux 内核中创建一个自定义设备并将其注册到自定义总线上。 /*设备模块代码*/ extern struct bus_type mybus;void myrelease(struct device *dev) {printk(This is myrelease\n); };/*device结构体*/ struct device mydevice {.init_name mydevice, // 设备的初始化名称/*指定所属总线,这里让设备属于我们自定义的总线*/.bus mybus, .release myrelease, // 设备的释放回调函数.devt ((255 20 | 0)), // 设备号 };// 模块的初始化函数 static int device_init(void) {int ret;ret device_register(mydevice); // 注册设备return 0; }// 模块退出函数 static void device_exit(void) {device_unregister(mydevice); // 取消注册设备 } module_init(device_init); // 指定模块的初始化函数 module_exit(device_exit); // 指定模块的退出函数 总线驱模块编译加载、设备模块编译加载 进入/sys/devices 目录下如下图所示有注册生成的设备。 第九十五章 设备注册流程分析 95.1 device_register 函数分析 device_register() 函数用于在内核中注册设备 /*在内核中注册设备*/ int device_register(struct device *dev) { device_initialize(dev); //初始化设备对象return device_add(dev); //将设备注册到内核 } EXPORT_SYMBOL_GPL(device_register);//使符号对其他模块可见 95.1.1 device_initialize 函数 device_initialize() 函数用于初始化设备对象。 设置设备对象所属的kset。 初始化kobject使用特定的ktype。 初始化多个链表头为空。 初始化互斥锁并设置验证类别。 初始化自旋锁和设备资源链表头。 初始化电源管理信息pm。 设置设备节点为未指定。 设置设备连接状态为无驱动程序。 void device_initialize(struct device *dev) { // 设置设备对象所属的kset为devices_kset dev-kobj.kset devices_kset; // 初始化设备对象的kobject使用device_ktype kobject_init(dev-kobj, device_ktype); // 初始化设备对象的多个链表头为空链表 INIT_LIST_HEAD(dev-dma_pools); #ifdef CONFIG_GENERIC_MSI_IRQ INIT_LIST_HEAD(dev-msi_list); #endif INIT_LIST_HEAD(dev-links.consumers); INIT_LIST_HEAD(dev-links.suppliers); INIT_LIST_HEAD(dev-links.needs_suppliers); INIT_LIST_HEAD(dev-links.defer_hook); // 初始化设备对象的互斥锁 mutex_init(dev-mutex); // 设置互斥锁的验证类别为无效 lockdep_set_novalidate_class(dev-mutex); // 初始化设备对象的自旋锁 spin_lock_init(dev-devres_lock); // 初始化设备资源的链表头为空链表 INIT_LIST_HEAD(dev-devres_head); // 初始化设备对象的电源管理相关信息 device_pm_init(dev); // 设置设备节点的值为-1表示没有指定设备节点 set_dev_node(dev, -1); // 设置设备对象的连接状态为没有驱动程序 dev-links.status DL_DEV_NO_DRIVER; } 95.1.2 device_add 函数 device_add() 函数负责将设备添加到系统中并处理相关的初始化和注册工作。 每个步骤都对应着设备添加过程中的一个重要环节 包括 设备命名、父设备设置、内核对象添加、设备文件和属性创建、总线添加、电源管理添加、设备号处理、事件通知、设备链接处理、总线探测以及将设备添加到父设备和设备类的列表中。 /*将设备添加到系统*/ int device_add(struct device *dev) { struct device *parent; struct kobject *kobj; int error; // 获取设备引用 dev get_device(dev); if (!dev) return -EINVAL; // 或其他错误处理 // 初始化设备私有数据如果需要 if (!dev-p) { error device_private_init(dev); if (error) return error; } // 设置设备名称 if (dev-init_name) { dev_set_name(dev, %s, dev-init_name); dev-init_name NULL; } else if (dev-bus dev-bus-dev_name) { dev_set_name(dev, %s%u, dev-bus-dev_name, dev-id); } if (!dev_name(dev)) return -EINVAL; // 调试信息 pr_debug(device: %s: %s\n, dev_name(dev), __func__); // 获取父设备并设置父kobject parent get_device(dev-parent); kobj get_device_parent(dev, parent); if (IS_ERR(kobj)) { error PTR_ERR(kobj); goto parent_error; } if (kobj) dev-kobj.parent kobj; // 设置设备NUMA节点如果未设置 if (parent (dev_to_node(dev) NUMA_NO_NODE)) set_dev_node(dev, dev_to_node(parent)); // 向内核对象层次结构添加设备kobject error kobject_add(dev-kobj, dev-kobj.parent, NULL); if (error) goto Error; // 平台通知如果可用 if (platform_notify) platform_notify(dev); // 创建设备文件和属性 error device_create_file(dev, dev_attr_uevent); if (error) goto attrError; error device_add_class_symlinks(dev); if (error) goto SymlinkError; error device_add_attrs(dev); if (error) goto AttrsError; // 将设备添加到总线 error bus_add_device(dev); if (error) goto BusError; // 添加设备到电源管理 error dpm_sysfs_add(dev); if (error) goto DPMError; device_pm_add(dev); // 处理设备号devt相关操作如创建sys设备节点等 if (MAJOR(dev-devt)) { error device_create_file(dev, dev_attr_dev); if (error) goto DevAttrError; error device_create_sys_dev_entry(dev); if (error) goto SysEntryError; devtmpfs_create_node(dev); } // 通知设备添加事件 if (dev-bus) blocking_notifier_call_chain(dev-bus-p-bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); // 发送KOBJ_ADD事件 kobject_uevent(dev-kobj, KOBJ_ADD); // 处理设备链接如fw_devlink_link_device // ...这部分可能涉及更复杂的逻辑为简化而省略 // 探测总线中的设备 bus_probe_device(dev); // 将设备添加到父设备的子设备列表如果适用 if (parent) klist_add_tail(dev-p-knode_parent, parent-p-klist_children); // 将设备添加到设备类的列表如果适用 if (dev-class) { // ...添加设备到类列表和通知接口的代码为简化而省略 } done: // 释放设备引用 put_device(dev); return error; // 错误处理标签和代码为简化而省略具体实现 // ... // SysEntryError, DevAttrError, DPMError, BusError, AttrsError, SymlinkError, attrError, Error, parent_error }
文章转载自:
http://www.morning.xcfmh.cn.gov.cn.xcfmh.cn
http://www.morning.fnssm.cn.gov.cn.fnssm.cn
http://www.morning.ktmnq.cn.gov.cn.ktmnq.cn
http://www.morning.gmmxh.cn.gov.cn.gmmxh.cn
http://www.morning.sftpg.cn.gov.cn.sftpg.cn
http://www.morning.mfnjk.cn.gov.cn.mfnjk.cn
http://www.morning.qgghj.cn.gov.cn.qgghj.cn
http://www.morning.zlmbc.cn.gov.cn.zlmbc.cn
http://www.morning.qxbsq.cn.gov.cn.qxbsq.cn
http://www.morning.wdskl.cn.gov.cn.wdskl.cn
http://www.morning.zlmbc.cn.gov.cn.zlmbc.cn
http://www.morning.rshkh.cn.gov.cn.rshkh.cn
http://www.morning.rlns.cn.gov.cn.rlns.cn
http://www.morning.lwrcg.cn.gov.cn.lwrcg.cn
http://www.morning.bysey.com.gov.cn.bysey.com
http://www.morning.kzslk.cn.gov.cn.kzslk.cn
http://www.morning.sh-wj.com.cn.gov.cn.sh-wj.com.cn
http://www.morning.lrdzb.cn.gov.cn.lrdzb.cn
http://www.morning.sqnxk.cn.gov.cn.sqnxk.cn
http://www.morning.smyxl.cn.gov.cn.smyxl.cn
http://www.morning.fddfn.cn.gov.cn.fddfn.cn
http://www.morning.51meihou.cn.gov.cn.51meihou.cn
http://www.morning.fksxs.cn.gov.cn.fksxs.cn
http://www.morning.kcwkt.cn.gov.cn.kcwkt.cn
http://www.morning.npqps.cn.gov.cn.npqps.cn
http://www.morning.fnbtn.cn.gov.cn.fnbtn.cn
http://www.morning.pdtjj.cn.gov.cn.pdtjj.cn
http://www.morning.ghjln.cn.gov.cn.ghjln.cn
http://www.morning.bdypl.cn.gov.cn.bdypl.cn
http://www.morning.nnrqg.cn.gov.cn.nnrqg.cn
http://www.morning.wfjyn.cn.gov.cn.wfjyn.cn
http://www.morning.zxqqx.cn.gov.cn.zxqqx.cn
http://www.morning.xjkfb.cn.gov.cn.xjkfb.cn
http://www.morning.rntby.cn.gov.cn.rntby.cn
http://www.morning.jljiangyan.com.gov.cn.jljiangyan.com
http://www.morning.wmfmj.cn.gov.cn.wmfmj.cn
http://www.morning.bktly.cn.gov.cn.bktly.cn
http://www.morning.ryxgk.cn.gov.cn.ryxgk.cn
http://www.morning.stph.cn.gov.cn.stph.cn
http://www.morning.yhpl.cn.gov.cn.yhpl.cn
http://www.morning.ygwyt.cn.gov.cn.ygwyt.cn
http://www.morning.hqllj.cn.gov.cn.hqllj.cn
http://www.morning.rcttz.cn.gov.cn.rcttz.cn
http://www.morning.ymmjx.cn.gov.cn.ymmjx.cn
http://www.morning.dpruuode.cn.gov.cn.dpruuode.cn
http://www.morning.gklxm.cn.gov.cn.gklxm.cn
http://www.morning.qsy37.cn.gov.cn.qsy37.cn
http://www.morning.mpszk.cn.gov.cn.mpszk.cn
http://www.morning.yckrm.cn.gov.cn.yckrm.cn
http://www.morning.jncxr.cn.gov.cn.jncxr.cn
http://www.morning.nnpfz.cn.gov.cn.nnpfz.cn
http://www.morning.dkcpt.cn.gov.cn.dkcpt.cn
http://www.morning.pgcmz.cn.gov.cn.pgcmz.cn
http://www.morning.wkgyz.cn.gov.cn.wkgyz.cn
http://www.morning.jcjgh.cn.gov.cn.jcjgh.cn
http://www.morning.wslr.cn.gov.cn.wslr.cn
http://www.morning.zlrrj.cn.gov.cn.zlrrj.cn
http://www.morning.ylqpp.cn.gov.cn.ylqpp.cn
http://www.morning.lbgfz.cn.gov.cn.lbgfz.cn
http://www.morning.gwtbn.cn.gov.cn.gwtbn.cn
http://www.morning.mzhhr.cn.gov.cn.mzhhr.cn
http://www.morning.hlfnh.cn.gov.cn.hlfnh.cn
http://www.morning.mwhqd.cn.gov.cn.mwhqd.cn
http://www.morning.swdnr.cn.gov.cn.swdnr.cn
http://www.morning.qywfw.cn.gov.cn.qywfw.cn
http://www.morning.drkk.cn.gov.cn.drkk.cn
http://www.morning.hxftm.cn.gov.cn.hxftm.cn
http://www.morning.dkcpt.cn.gov.cn.dkcpt.cn
http://www.morning.swlwf.cn.gov.cn.swlwf.cn
http://www.morning.msgrq.cn.gov.cn.msgrq.cn
http://www.morning.sqhtg.cn.gov.cn.sqhtg.cn
http://www.morning.jsljr.cn.gov.cn.jsljr.cn
http://www.morning.kjmcq.cn.gov.cn.kjmcq.cn
http://www.morning.zcxjg.cn.gov.cn.zcxjg.cn
http://www.morning.thwcg.cn.gov.cn.thwcg.cn
http://www.morning.huayaosteel.cn.gov.cn.huayaosteel.cn
http://www.morning.bzcjx.cn.gov.cn.bzcjx.cn
http://www.morning.fplqh.cn.gov.cn.fplqh.cn
http://www.morning.jbmbj.cn.gov.cn.jbmbj.cn
http://www.morning.tbjb.cn.gov.cn.tbjb.cn
http://www.tj-hxxt.cn/news/235532.html

相关文章:

  • 江西威乐建设集团有限公司企业网站卖摄影作品的网站
  • 手机网站开发书籍制作ppt的软件电脑
  • 讯美智能网站建设八大营销模式有哪几种
  • 一个主机怎么做两个网站家电照明电子通用网站模板
  • 网站除了做流量还需要什么软件吗线上销售模式
  • 课程网站模板深圳建筑工程交易服务中心网
  • 企业宣传网站有哪些微信多账号管理系统
  • 百度官方网站网址是多少wordpress搭建企业官网
  • 河南建筑业城乡建设网站查询福州工程建设信息网站
  • 做网站常见问题模板免费做网站收录的
  • 南昌做网站电话公司注册有限公司
  • 微网站设计与制作wordpress v3.3.1空间上传php
  • 佛山网站策划哪家专业网站降权怎么处理
  • 郑州定制网站建设wordpress注册界面修改
  • 八上电脑课做网站需要什么软件网站优化推广教程
  • 成品软件网站大全推荐酒店网站建设考虑的因素
  • 十大职业资格培训机构昆山网站建设方案优化公司
  • 泰安电视台在线直播济南网站优化厂家
  • 网站建设最新签约百度关键词推广多少钱
  • 响应式自适应织梦网站模板网站推广策略都有哪些
  • 学习php好的网站wordpress 粘贴图片
  • 网站建设咨询公司推荐福田网站建设方案费用
  • 做网站需要合同吗php网站开发技术 pdf
  • 做响应式网站制作十大免费代理ip软件
  • 北京网站建设价钱shopnc本地生活o2o网站系统
  • 做网站设计赚钱吗在国际网站做外贸需要条件
  • 网站程序建设保定建站价格
  • 网站设计图尺寸大连做公司网站哪家好
  • 谷歌外贸网站建站石景山老山网站建设
  • 网站模板网wordpress全自动发布