0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

万千设备,linux内核如何知道?

嵌入式小生 来源:嵌入式小生 2023-07-12 08:52 次阅读

1、简介

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

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

199d7ba0-204a-11ee-962d-dac502259ad0.png

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

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

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

2、device_initialize分析

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

19b23cac-204a-11ee-962d-dac502259ad0.png

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

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

初始化dma_pools链表。

初始化struct device中的各种锁:

19d1e322-204a-11ee-962d-dac502259ad0.png

初始化device的电源管理

19f41bf4-204a-11ee-962d-dac502259ad0.png

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

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

1a0a4da2-204a-11ee-962d-dac502259ad0.png

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

1a1af6f2-204a-11ee-962d-dac502259ad0.png

设置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的私有数据:

1a376102-204a-11ee-962d-dac502259ad0.png

(3)设置device的name:

1a53a498-204a-11ee-962d-dac502259ad0.png

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

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

1a6f4130-204a-11ee-962d-dac502259ad0.png

(5)使用device_create_file为device创建sysfs属性文件:

error=device_create_file(dev,&dev_attr_uevent);

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

structdevice_attribute{
structattributeattr;
ssize_t(*show)(structdevice*dev,structdevice_attribute*attr,
char*buf);
ssize_t(*store)(structdevice*dev,structdevice_attribute*attr,
constchar*buf,size_tcount);
};

(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:

1a8d5b0c-204a-11ee-962d-dac502259ad0.png

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_groups将bus_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)创建设备节点:

1aa8bb0e-204a-11ee-962d-dac502259ad0.png

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

1ac739f8-204a-11ee-962d-dac502259ad0.png

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

1ae151bc-204a-11ee-962d-dac502259ad0.png

具体执行流程如下:

(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是包含此设备的所有子节点的链表):

1afa2ff2-204a-11ee-962d-dac502259ad0.png

(14)如果设备的class不为NULL,则会将class绑定到device:

klist_add_tail(&dev->p->knode_class,&dev->class->p->klist_devices);

(15)通知所有的interface接口:

1b0936aa-204a-11ee-962d-dac502259ad0.png

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

struct class_interface结构体定义如下:

structclass_interface{
structlist_headnode;
structclass*class;
int(*add)(structdevice*dev,structclass_interface*class_intf);
void(*remove)(structdevice*dev,structclass_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。从内核源码来看,这两个结构只在为数不多的几个特定驱动程序中使用,那么可证明这可能是历史发展遗留下来的代码,在device_register中仍然保留了对这部分代码的支持。

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





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 电源管理
    +关注

    关注

    112

    文章

    6013

    浏览量

    141106
  • Linux系统
    +关注

    关注

    4

    文章

    567

    浏览量

    26914
  • dma
    dma
    +关注

    关注

    3

    文章

    535

    浏览量

    99021
  • LINUX内核
    +关注

    关注

    1

    文章

    311

    浏览量

    21389
  • numa
    +关注

    关注

    0

    文章

    7

    浏览量

    3817

原文标题:一脸懵,万千设备,linux内核如何知道?

文章出处:【微信号:嵌入式小生,微信公众号:嵌入式小生】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    面对不断升级的内核,我们该如何学习LINUX设备驱动?

    面对不断升级的linux内核、GNU开发工具、linux环境下的各种图形库,很多linux应用程序开发人员和linux
    发表于 09-29 11:04

    Linux设备驱动开发详解:基于最新的Linux 4.0内核

    Linux设备驱动开发详解:基于最新的Linux 4.0内核
    发表于 08-31 12:29

    什么是linux设备驱动看了就知道

    想要深入理解linux设备驱动,你必须明确以下几个问题:· 应用程序、库、内核、驱动程序的关系· 设备类型· 设备文件、主
    发表于 04-06 06:50

    如何编译设备树和Linux内核镜像文件

    实验任务:编译设备树和Linux内核镜像文件,拷贝到sd卡中,再从sd卡中拷贝到nand闪存对应的分区中,最后nand启动,进入linux系统本篇博客内容:1.实验流程2.注意事项1.
    发表于 12-20 07:11

    Linux内核教程

    本章学习目标掌握LINUX内核版本的含义理解并掌握进程的概念掌握管道的概念及实现了解内核的数据结构了解LINUX内核的算法掌握
    发表于 04-10 16:59 0次下载

    linux内核kernel-api

    linux内核kernel-api,不知道从哪儿找的了,但是你如果想要做内核编程,这是一部api函数详尽的工具书!!!五星推荐
    发表于 10-30 17:16 19次下载

    Linux设备驱动开发详解》第4章、Linux内核模块

    Linux设备驱动开发详解》第4章、Linux内核模块
    发表于 10-27 14:15 0次下载
    《<b class='flag-5'>Linux</b><b class='flag-5'>设备</b>驱动开发详解》第4章、<b class='flag-5'>Linux</b><b class='flag-5'>内核</b>模块

    知道Linux内核模块编程怎么操作?

    针对2.6内核Linux系统,需要你的机器上已经安装了kernel-devel这个包,也就是编译模块所必须的东西:内核的头文件和一些Makefile。
    发表于 05-06 15:42 773次阅读

    知道Linux内核调试关键技术之一的printk?

    内核调试技术之中,最简单的就是printk的使用了,它的用法和C语言应用程序中的printf使用类似,在应用程序中依靠的是stdio.h中的库,而在linux内核中没有这个库,所以在linu
    发表于 05-10 11:18 1598次阅读

    知道Linux内核数据结构中双向链表的作用?

    Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到。我们以双向链表着手开始介绍 Linux 内核
    发表于 05-14 17:27 1757次阅读

    谷歌Android设备内核引入主线Linux内核难吗?

    Android是基于Linux内核的操作系统,但是,运行在Android设备上的内核其实与Google选择的LTS版本Linux
    的头像 发表于 11-22 10:41 2826次阅读
    谷歌Android<b class='flag-5'>设备</b><b class='flag-5'>内核</b>引入主线<b class='flag-5'>Linux</b><b class='flag-5'>内核</b>难吗?

    linux内核是什么_linux内核学习路线

    Linux内核是一个操作系统(OS)内核,本质上定义为类Unix。它用于不同的操作系统,主要是以不同的Linux发行版的形式。Linux
    发表于 09-16 15:49 2385次阅读

    linux内核参数设置_linux内核的功能有哪些

    本文主要阐述了linux内核参数设置及linux内核的功能。
    发表于 09-17 14:40 1220次阅读
    <b class='flag-5'>linux</b><b class='flag-5'>内核</b>参数设置_<b class='flag-5'>linux</b><b class='flag-5'>内核</b>的功能有哪些

    Linux内核】从小小的宏定义窥探Linux内核的精妙设计

    Linux内核】从小小的宏定义窥探Linux内核的精妙设计
    的头像 发表于 08-31 13:30 1641次阅读

    Linux内核UDP收包为什么效率低

    栈收包效率真的很低,这是为什么?有没有办法去尝试着优化?而不是动不动就DPDK。 我们从最开始说起。 Linux内核作为一个通用操作系统内核,脱胎于UNIX那一套现代操作系统理论。 但一开始不
    的头像 发表于 11-13 10:38 239次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>UDP收包为什么效率低