【linux kernel】对linux内核设备的注册机制和查找机制分析

news2024/11/20 11:26:48

文章目录

    • 1、简介
    • 2、device_initialize分析
    • 3、device_add分析
    • 4、总结


🔺【linux内核系列文章】

👉对一些文章内容进行了勘误,本系列文章长期不定时更新,希望能分享出优质的文章!

  • 1、《linux内核数据结构分析之哈希表》
  • 2、《一文总结linux内核通知链》
  • 3、《linux内核中的debugfs》
  • 4、《linux内核数据结构分析之链表》
  • 5、《linux media子系统分析之media控制器设备》
  • 6、《V4L2-PCI驱动程序样例分析(上)》
  • 7、《v4l2框架分析之v4l2_fh》
  • 8、《 v4l2框架分析之v4l2_subdev》
  • 9、《 v4l2框架分析之v4l2_device》
  • 10、《v4l2框架分析之video_device》
  • 11、《linux内核重要函数 | do_initcalls》
  • 12、《Linux设备驱动模型 | bus》
  • 13、《linux内核裁剪随想》
  • 14、《基于ARM64分析linux内核的链接脚本vmlinux.lds.S》
  • 15、《linux内核start_kernel函数的早期操作》
  • 16、《start_kernel函数详解系列之proc_caches_init》
  • 17、《start_kernel函数详解系列之fork_init》
  • 18、《start_kernel函数详解系列之rcu_init》
  • 19、《start_kernel函数详解系列之proc_root_init》
  • 20、《start_kernel详解系列之【setup_arch】》
  • 21、《linux内核如何启动用户空间进程(上)》
  • 22、《linux内核如何启动用户空间进程(下)》
  • 23、《一文总结linux内核的完成量机制》
  • 24、《一文总结linux内核设备驱动的注册和卸载》
  • 25、《linux内核的启动加载程序的总结》
  • 26、《linux内核入口:head.o》
  • 27、《挂载根文件系统之rootfs》
  • 28、《mount系统调用剖析》
  • 29、《devtmpfs文件系统分析》
  • 30、《linux内核的kthreadd线程》
  • 31、《linux内核的进程调度—调度策略》
  • 32、《linux系统调用实践(Arm架构)》
  • 33、《对linux内核__init机制的实践》
  • 34、《linux 内核中EXPORT_SYMBOL()分析与实践》
  • 35、《linux内核如何挂载根文件系统》
  • 36、《linux内核如何唤醒线程》
  • 37、《linux内核的init线程》
  • 38、《linux内核伪文件系统—sysfs分析》
  • 39、《linux 内核设备模型的初始化(上)》
  • 40、《linux 内核设备模型的初始化(下)》
  • 41、《linux内核伪文件系统—proc分析》
  • 42、《linux中断管理—workqueue工作队列》
  • 43、《linux中断管理—软中断》
  • 44、《linux中断管理 | tasklet》
  • 45、《linux中断管理 | 中断管理框架(01)》
  • 46、《linux内存管理 | 分配物理内存页面》
  • 47、《linux内存管理 | 释放内存页面》

1、简介

本文基于内核源码4.19.4分析。

linux内核设备的注册由device_register()函数完成,这个函数是linux设备驱动模型的核心函数,实现在/drivers/base/core.c中:

device_register()函数中,分为两个步骤:

  • (1)调用device_initialize():该步骤用于初始化一个device。

  • (2)调用device_add():该函数用于将device添加到linux内核的device树中。

2、device_initialize分析

该函数接收一个struct device *dev参数,在该函数中初始化struct device结构中的几个重要成员:

  • 设置dev->kobj.kset为device_kset。device_kset是一个struct kset类型的全局变量,用于向sysfs文件系统中导出目录:/sys/device/* 。

  • 初始化dev中的kobject,并指定与这个对象相关联的ktype为device_type

  • 初始化dma_pools链表。

  • 初始化struct device中的各种锁:

  • 初始化device的电源管理:

  • 如果在NUMA下,还会初始化设置device的numa_node为-1。

  • 接着初始化device下的links中的链表:

struct device 中的links表示链接到该设备的suppliers和consumers,由struct dev_links_info表示:

  • 设置device下的links.status值为DL_DEV_NO_DRIVER,表示此时还没有对应驱动attach到这个设备。

以上步骤则是device_initialize()初始化设备时完成的操作。

3、device_add分析

  • (1)调用get_device(dev)增加device的引用计数。

  • (2)如果dev->p为NULL,则调用device_private_init()设置device的私有数据:

  • (3)设置device的name:

如果开启支持pr_debug()函数,则会打印出对应的设备名称。

  • (4)寻找父设备和父设备对应的kobj,并调用kobject_add()将dev->kobj添加到dev->kobj.parent:

  • (5)使用device_create_file为device创建sysfs属性文件:
error = device_create_file(dev, &dev_attr_uevent);

dev_attr_uevent是一个struct device_attribute类型的数据,该结构用于描述导出设备属性的接口,定义如下:

struct device_attribute {
	struct attribute	attr;
	ssize_t (*show)(struct device *dev, struct device_attribute *attr,
			char *buf);
	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count);
};
  • (6)添加类的符号链接:
error = device_add_class_symlinks(dev);

device_add_class_symlinks()的功能是将设备添加到指定的设备类中,并在/sys/class目录下为设备创建符号链接,以便用户空间程序能够方便地访问和管理设备。

  • (7)调用device_add_attrs()为设备添加属性:
error = device_add_attrs(dev);

device_add_attrs()的功能是为设备添加属性,并在/sys/devices目录下创建相应的属性文件。这样,用户空间程序可以通过访问设备的属性文件来读取和修改设备的属性值。这个函数在设备驱动的初始化过程中常常被调用,以确保设备的属性能够正确地显示和访问。

  • (8)调用bus_add_device()添加设备到bus:


bus_add_device用于将设备添加到总线上。它的功能是将一个设备(struct device结构体)添加到指定总线(struct bus_type结构体)上,并进行相应的初始化和注册操作。

bus_add_device的执行逻辑:

  • (1)从dev->bus中取得bus_type*类型的指针bus,如果获取bus不成功,则函数直接返回;如果bus获取成功,则会继续后续的第(2)步操作。
  • (2)调用device_add_attrs接口,将由bus->dev_attrs指针定义的默认attribute添加到内核中,这个操作会体现在sysfs文件系统中的/sys/devices/xxx/xxx_device/目录中。
  • (3)调用device_add_groupsbus_dev_groups添加到内核中。
  • (4)调用sysfs_create_link将该设备在sysfs中的目录,链接到该bus的devices目录下
  • (5)接着依然调用sysfs_create_link,在该设备的sysfs目录中,创建一个指向该设备所在bus目录的链接,命名为subsystem。
  • (6)前面几个操作实则是向sysfs文件系统注册关于设备的信息,向用户空间抛出接口。最后步骤则是调用klist_add_tail()将该设备指针保存到bus->p->klist_devices中。
  • (9)调用device_pm_add()将一个设备添加到PM核心的active设备链表中。

  • (10)创建设备节点:

  • (11)通过bus_notifier告知系统设备已经添加:

  • (12)调用bus_probe_device()为该设备probe一个驱动。该函数实现如下:

具体执行流程如下:

  • (1)从dev中解析出该dev所在而bus,如果bus不存在,则退出该函数。
  • (2)如果设置了driver_autoprobe,则调用device_initial_probe(dev)。该函数本质调用到device_attach(),尝试将设备连接到驱动程序。
  • (3)遍历bus上的子系统接口链表interfaces,如果add_dev函数指针存在,则调用对应的函数。(从源码来看有些驱动程序,会使用struct subsys_interface来实现,在此处实现对注册的subsys_interface下的add_dev的调用执行)
  • (13)如果父设备存在,则会将该设备添加到父设备的klist_children链表中(klist_children是包含此设备的所有子节点的链表):

  • (14)如果设备的class不为NULL,则会将class绑定到device:
klist_add_tail(&dev->p->knode_class,&dev->class->p->klist_devices);
  • (15)通知所有的interface接口:

在内核中,struct class_interface是用于表示设备类和设备驱动之间的接口的结构体。它定义了设备类与设备驱动之间的关联关系,允许设备驱动在注册时与相应的设备类进行关联,并提供了一组函数指针,用于设备类调用设备驱动中的操作。

struct class_interface结构体定义如下:

struct class_interface {
    struct list_head node;
    struct class *class;
    int (*add)(struct device *dev, struct class_interface *class_intf);
    void (*remove)(struct device *dev, struct class_interface *class_intf);
};
  • node: 用于将struct class_interface链接到设备类的接口链表中。
  • class: 指向与该接口相关联的设备类。
  • add: 指向设备类调用设备驱动的添加操作的函数指针。当设备添加到设备类时,会调用此函数。
  • remove: 指向设备类调用设备驱动的移除操作的函数指针。当设备从设备类中移除时,会调用此函数。

通过使用struct class_interface,设备驱动可以与设备类进行交互,以便在设备添加或移除时执行相应的操作。这种机制允许设备驱动与设备类解耦,使得设备驱动可以在设备类的上下文中执行一些操作,而无需直接操作设备类。

回到device_add()中,使用了list_for_each_entry()遍历interfaces链表,如果设置了class_intf->add_dev,则调用该回调函数指针指向的函数。

4、总结

结合本文内容和linux内核源码,得出以下结论:

  • (1)在设备驱动模型中,所有的设备注册操作最后都会调用device_register()函数实现。

  • (2)在笔者分析的linux版本下的device_register()中,存在两个数据结构:struct class_interface struct subsys_interface。从内核源码来看,这两个结构只在为数不多的几个特定的驱动程序中使用,具体有什么特殊目的咱不所知,猜测可能是linux内核历史发展遗留下来的代码,在device_register中仍然保留了对这部分代码的支持。

  • (3)在device_register()中调用了bus_probe_device(),从而证明在注册设备的时候发生了**『设备与驱动匹配』**的过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1094745.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

冲刺十五届蓝桥杯P0006平面切分

文章目录 题目思路代码总结 题目 平面切分 思路 这道题是一个思维题把,之前没有接触过平面几何的知识,做起来感觉还是比较难的,用到的set集合和自己创建一个类 首先我们知道,一根直线A是可以将平面切分成两块的,如…

如何能优化网站开展速度

随着互联网技术的飞速发展,网站加载速度已经成为一个不可忽视的关键要素。 当您的网站速度缓慢,不仅会影响用户的访问体验,还可能导致用户的流失。以下是一些创新的技巧和策略,帮助您提升网站打开速度。 一、在图像压缩上做文章 …

ArcGIS JS API 演示Demo

ArcGIS JS API Demo演示地址 https://ralucanicola.github.io/JSAPI_demos/ ArcGIS JS API Demo下载地址 https://github.com/RalucaNicola/JSAPI_demos

PyTorch入门教学——使用PyCharm创建一个PyTorch项目

首先需要创建好PyTorch的虚拟环境,步骤:PyTorch入门教学——简介与环境配置-CSDN博客打开PyCharm,新建项目,选择项目的存放位置。选择先前配置的解释器,也就是虚拟环境中的解释器。(记住创建的虚拟环境所在…

1、资源包下载

1 、百度云盘永久下载地址 : 链接: https://pan.baidu.com/s/13pBco75qXU6bLxlTtZ29TQ 提取码: ixkg 2 、官方下载地址: https://dev.mysql.com/downloads/mysql/ 3 、注意:下载时候要选择自己的系统和对应的电脑 CPU 位数&a…

Multisim14 逻辑分析仪的使用教程(打开关闭+详细具体)

一、打开逻辑分析仪 总结:根据自身的分析需要选择分析的参数,保存并允许结果。然后根据参数的设置,看运行结果是否满足项目分析需要。 二、 关闭逻辑分析仪 最后,再次运行即可关闭逻辑分析仪了! 总结:如果…

LLM 系列 | 21 : Code Llama实战(上篇) : 模型简介与评测

引言 小伙伴们好,我是《小窗幽记机器学习》的小编:卖热干面的小女孩。 个人CSDN首页:JasonLiu1919_面向对象的程序设计,深度学习,C-CSDN博客 今天开始以2篇小作文介绍代码大语言模型Code Llama。上篇主要介绍Code Llama的基本情况并基于Hug…

Linux系统编程_文件编程第2天:写整数、结构体,fopen等

1. 文件编程小应用之修改程序的配置文件&#xff08;407.10&#xff09; FILE/demo14.c #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <s…

VSCode 调试 u-boot

文章目录 VSCode 调试 u-boot调试配置启动 u-boot 脚本调试界面重定向之后继续调试参考 VSCode 调试 u-boot 调试配置 参考 qemu基础篇——VSCode 配置 GDB 调试 要想调试 u-boot 只需要再添加一个 u-boot 的配置即可 {"version": "0.2.0","conf…

Python 机器学习入门之K近邻算法

系列文章目录 第一章 Python 机器学习入门之线性回归 第一章 Python 机器学习入门之梯度下降法 第一章 Python 机器学习入门之牛顿法 第二章 Python 机器学习入门之逻辑回归 番外 Python 机器学习入门之K近邻算法 K近邻算法 系列文章目录前言一、K近邻算法简介1、定义2、用途 …

【Unity引擎核心-Object,序列化,资产管理,内存管理】

文章目录 整体介绍Native & Managed Objects什么是序列化序列化用来做什么Editor和运行时序列化的区别脚本序列化针对序列化的使用建议 Unity资产管理导入Asset Process为何要做引擎资源文件导入Main-Assets和 Sub-Assets资产的导入管线Hook&#xff0c;AssetPostprocessor…

对协议的基本认识

目录 前言 TCP网络计算器的模拟实现 制定协议 协议protocol的整体代码 TCP网络计算器的服务端类TcpServer TcpServer类的整体代码 TCP网络计算器的服务端 服务端CalServer.cc的整体代码 TCP网络计算器的客户端 客户端CalClient.cc的整体代码 对模拟实现的TCP网络计算…

匿名内部类的使用:(一看就会!!!)

知识点&#xff1a; 匿名内部类依旧是一个类&#xff0c;但是没有名字&#xff0c;同时还是一个对象&#xff1b;再类的内部&#xff1b; 使用方法指南&#xff1a; 先创建一个类&#xff0c;可以是接口、抽象类、普通父类需要明确声明关系 &#xff0c;父与子、实现接口、抽…

不容易解的题10.15

395.至少有K个重复字符的最长字串 395. 至少有 K 个重复字符的最长子串 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/longest-substring-with-at-least-k-repeating-characters/description/?envTypelist&envIdZCa7r67M自认为是不好做的题。尤其…

自动化的采集链接和自动推送必应的在线工具

搜索LMCJL在线工具 进入后点击站长工具类型&#xff0c;选择必应自动推送 进去后&#xff0c;添加域名&#xff0c;点击数据管理&#xff0c;输入必应的token 然后开启推送&#xff0c;就可以实现&#xff0c;自动化采集链接&#xff0c;自动推送给必应。 必应的站长后台官网…

基于吉萨金字塔建造优化的BP神经网络(分类应用) - 附代码

基于吉萨金字塔建造优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于吉萨金字塔建造优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.吉萨金字塔建造优化BP神经网络3.1 BP神经网络参数设置3.2 吉萨金字…

16.SpringBoot前后端分离项目之简要配置一

SpringBoot前后端分离项目之简要配置一 前面对后端所需操作及前端页面进行了了解及操作&#xff0c;这一节开始前后端分离之简要配置 为什么要前后端分离 为了更低成本、更高效率的开发模式。 前端有一个独立的服务器。 后端有一个独立的服务器。两个服务器之间实时数据交换…

关于YOLOv8不显示GFlops的问题解决

屏幕显示GFlops需要安装此pip库: thop。但是没有安装&#xff0c;则不显示&#xff0c;导致debug比较困难。

Linux:进程调度的O(1)算法

文章目录 并发的理解程序运行时的数据进程切换的过程 内核的调度队列和调度原理 并发的理解 前面总结到了&#xff0c;关于并发的概念&#xff0c;并发针对的是单核的CPU上同时运行很多情况&#xff0c;并不是某个程序在CPU上运行就一直运行&#xff0c;而是根据一定的时间片和…

Linux C/C++ 嗅探数据包并显示流量统计信息

嗅探数据包并显示流量统计信息是网络分析中的一种重要技术&#xff0c;常用于网络故障诊断、网络安全监控等方面。具体来说&#xff0c;嗅探器是一种可以捕获网络上传输的数据包&#xff0c;并将其展示给分析人员的软件工具。在嗅探器中&#xff0c;使用pcap库是一种常见的方法…