面向对象地分析Linux内核设备驱动(2)——Linux内核设备模型与总线

2023-05-16

Linux内核设备模型与总线

-         内核版本 Linux Kernel 2.6.34, 与 Robert.Love的《Linux Kernel Development》(第三版)所讲述的内核版本一样

-         源代码下载路径: https://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.34.tar.bz2


 

1.      Linux内核设备模型分析

1)     kobject对象的设计思想

-         对于没有接触过JAVA、Python等高级面向对象编程语言工程师,第一次看到struct  kobject对象,都会对它的作用感到困惑,不知道为什么这么多Linux内核对象结构体中,都要有一个看起来没什么用的struct  kobject。

-         对于接触过JAVA、Python等面向对象编程语言的工程师,对object对象肯定不陌生。在JAVA、Python中,object对象是所有对象的基类,所有的对象最终都会继承到object对象。关于JAVA中object对象及其相关方法的描述,可以参阅JDK在线文档。

-         在Linux内核设备模型中,也是借鉴了JAVA、Python中的做法,让struct kobject对象作为所有内核设备的基类,kobject是一个最高层次的抽象基类,这样Linux内核才能够通过一个继承树体系,管理到系统里的每一个设备。

-         在Linux内核中,虽然kobject对象可以作为所有设备对象的基类,但是类比于JAVA、Python,我们一般不直接使用kobject这种最高抽象层次的基类作为实际需要开发的设备程序的的直接基类,原因见图1。kobject类比于物质在自然界继承体系里的概念,物质是一个抽象的概念,所有的生物和非生物都继承自物质,即他们和物质都是IS-A的关系。狗 IS-A 物质,水IS-A 物质。但是我们真正研究狗的时候,一般是从其犬科动物或者动物等比较具体的基类开始研究,研究它动物属性,研究它发出叫声的特性,很少研究狗的物质属性(除非是唯物主义哲学家),但是狗确实是物质。

-         同理,在研究一般具体的Linux设备驱动,如视频设备V4L2驱动的代码,一般都从其上层基类struct  video_device研究起,或者再抽血一些,研究struct  video_device 的基类struct  cdev, 很少直接使用最上层的kobject基类。但是V4L2设备驱动,确实 IS-A kobject。


Figure 1 自然界继承体系,所有对象都继承自物质

2)     kobject对象的特性

-         kobjet对象的声明在与相关的API在include/linux/kobject.h文件中。

-         kobject 对象有对象引用计数(kref),父子object索引(parent)等基本属性。

-         kobject提供了sysfs文件系统相关的节点描述,属性与函数,使得Linux系统可以通过特殊的sysfs文件系统,以树形继承的关系来访问Linux内核中的每个具体的kobject对象。实际上,kobject最初的设计目的就是为了在sysfs中模仿windows的设备管理器,提供一个类似树形的,可以管理额访问系统所有设备的接口。

-         kobject对象还提供了Linux系统设备中hotplug热插拔相关的事件与函数方法,使得内核设备可以支持热插拔机制。

-         每个继承了object的Linux设备对象,在调用者获得kobject基类对象实例之后,可以通过container_of()函数,一层一层转换,最终获得具体子类对象。有了kobject,就可以实现通过基类来访问子类对象的机制

3)     Linux内核中继承kobject的主要基础类设备模型

-         Kobject类似于JAVA中的object类,一般不作为内核设备对象的直接基类。但是类似于JDK中有不少对象直接继承自object对象,JDK中直接继承自object的对象,一般都是最为基础类对象,提供给开发者使用。同样,Linux内核中也有不少继承自kobject的基础类对象,Linux内核驱动开发者可以使用这些直接继承自kobject的基础类设备,来构建实际的Linux设备驱动。

-         kset是一个集合容器,用于管理一类object子类对象的集合,继承自某个基类kobject的所有基类的kobject对象都可以用一个kset容器来管理。

-         Linux内核在继承自kobject的重要基础类对象如图2所示。



Figure 2  Linux内核中直接继承kobject的重要基础类对象

-         device类对象一般用于Linux各种总线设备(platform虚拟平台总线、USB总线等)的基类,下一节详细介绍。

-         module对象是用于模块管理接口(就是上一篇文章中单继承与接口一节的接口),接口本身也是一个对象(JAVA中的interface也是对象,继承object),实现了module接口的对象,可以通过模块的方式,动态装载、卸载代码块。

-         class对象是用于设备分类管理的相关接口,通过class接口可以将内核中各种设备类型的信息导出到用户态。

-         cdev对象就是典型的字符设备基类对象,所有的字符设备最终都会继承到cdev对象。cdev对象同时制定了字符设备标准的访问接口函数方法。

-         总之,拥有了以上这些基础类,内核开发者就能开发自己特殊的设备驱动,并且通过这些类和接口,将驱动程序集成到Linux内核中。


3). Linux内核中是怎么管理维护这些继承类对象

-         之前的内容讲了一堆面向对象的概念,描述了一堆与Linux设备驱动有关的对象之间的关系,可Linux内核毕竟是C语言写的,内核中如何维护这些基础类对象呢?

-         在Linux内核drivers/base/ 目录下,有很多重要的代码,目录命名为base/,可见基础类对象这个 名词还是有来源依据的,Linux设备驱动里的对象基本都是继承自drivers/base/里面的对象。

-         drivers/base/base.h中声明了base的一些私有对象属性,以及API,其中很多API如devices_init(),buses_init(),classes_init(),platform_bus_init()等初始化函数都会在Linux内核init函数的driver_init()中被调用,因此可见,Linux内核在启动时通过base.h中的这些初始化*_init()函数使得Linux内核中整个驱动系统相关的基础类组件对象都能进入工作状态。

-         查看devices_init()函数实现代码如下,我们发现实际上,内核在初始化devices的时候,使用kobject对象创建了dev_kobj作为所有device子类对象的基类。并且创建了相应的devices_kset来管理这些子类。

int __init devices_init(void)
{
	devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
	if (!devices_kset)
		return -ENOMEM;
	dev_kobj = kobject_create_and_add("dev", NULL);
	if (!dev_kobj)
		goto dev_kobj_err;
	sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
	if (!sysfs_dev_block_kobj)
		goto block_kobj_err;
	sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
	if (!sysfs_dev_char_kobj)
		goto char_kobj_err;

	return 0;

 char_kobj_err:
	kobject_put(sysfs_dev_block_kobj);
 block_kobj_err:
	kobject_put(dev_kobj);
 dev_kobj_err:
	kset_unregister(devices_kset);
	return -ENOMEM;
}



-         最后,我们实现device的子类对象,并通过device_register()函数将其注册的时候(Linux内核还有很多类似的register函数,例如register_chrdev_region,注册函数意图大同小异,都是让基类能够获取注册后的子类对象),用户就可以通过基类访问注册后的子类对象了。

-         在device_register()函数中,我们看到注册的device子类会和基类的kset容器以及关联起来,最终系统可以通过基类device对象实例dev_kobj所关联的kset容器来访问deivce的子类对象(一般会通过container_of()函数获取子类对象)。

-         实际上,Linux内核中,cdev,device等基础类对象,都会在初始化时init()相关的全局变量,Linux内核需要维护这些全局变量以及相关的容器(kset、数组、list都可以看成容器)。

-         由此可见,面向对象思想里面,继承,多态,虚函数的实现并不神秘,还是通过精巧的设计,用全部变量加容易的方法来管理这些关系。由于有这些由Linux内核维护的全局变量和相关容器,所以在开发设备驱动模块子类时,需要通过注册函数(register)才能让基类能够关联到子类的对象。当有了面向对象的观点,我们可以在更高层次理解Linux内核这些对象的关系,从而设计并改进我们的系统。




2.      Linux内核总线、设备与驱动

1). 计算机系统总线模型

- Linux内核总线的思想来源于如图3所示的计算机系统总线模型。

- 计算机系统总线控制着外部设备与计算机CPU的通信,任意CPU N都可以通过总线访问到任意外部设备。

- 一般情况下,外部设备数量都会大于CPU的数量,有了总线,无需为每个外部设备都配备一个CPU。只有外部设备需要CPU来访问读取处理数据,发送控制信号时,一个空闲的CPU才会通过总线控制器与某个外部设备建立通信连接。

- 一旦CPU处理完某个外部设备的数据之后,CPU可以通过总线控制器,断开和某个外部设备的连接,去处理其它外部设备的访问需求。

- 总之,计算机系统中的总线模型为数量较多的外部设备提供了一种共享数量较少的CPU的控制访问机制。



Figure 3 计算机系统中的总线

2). Linux内核中的与总线

- Linux内核之后有各种各样的软件总线,系统中所支持的所有总线型驱动设备在/sys/bus/ 目录下可以看到。主要的总线型设备驱动有USB、platform、I2C、SPI、SCSI、mmc等。

- Linux内核中所有总线接口的的基类以及相关的API都是在include/linux/device.h中声明。其中一个总线接口包括三个核心的基类对象:struct bus_type、struct device 与struct device_driver。这些基类对象与Linux内核中实际的USB、platform、I2C总线接口的继承关系如图4所示。



Figure 4 Linux内核中,各种总线设备与总线接口基类的对应关系

 

- Linux 内核总线驱动实际上是模仿计算机系统总线的机制,在一个具体类型的总线上(例如I2C、USB、platform)多数的device设备共享少数的device_driver提供了一种管理机制

- 通过总线,将一个设备驱动中,逻辑功能部分(device_driver)与硬件具体资源bsp相关的部分(device)分隔开来,使得同一种类型的多数设备实例(device),能够共享同一个驱动程序逻辑代码(device_driver)。

- struct  bus_type 对象与struct device 对象、structdevice_driver对象构成了一个设备实例化管理接口,对象之间的行为模式如图5所示,类似于抽象共产,将每个device与device_driver装配起来,构造出真正的设备实例。

- struct bus_type对象在 match()函数方法中,通过对比新发现的device 与 device_driver 的 Id(Id可以是name也可以是dts的描述节点或者实际总线自己定义的匹配Id号都可以匹配),为新加入的device设备找到合适的Id匹配的device_driver,然后调用device_driver的probe()函数方法,进行构造实例化,最终产生实例化的设备驱动并且作为节点挂载在/dev目录下。

- struct bus_type对象的 remove()函数则处理设备卸载析构的行为

- bus_type对象与device对象、device_driver对象除了用于实例化的函数方法,还有suspend()、resume()、shutdown()等函数方法,用于实现设备的休眠、唤醒等电源管理相关的功能。

- struct bus_type对象的uevent()函数方法提供了热插拔事件的相关通信机制,通过该函数接口,可以给用户态发送热插拔相关的异步事件。



Figure 5 bus_type 与 device、device_driver对象的行为模式

3).platform总线与设备简介

- 在Linux 内核中,USB、I2C、SPI等总线都是实际存在的总线,都有对应的相关的外部硬件电路以及相关的标准化通信协议最终以电信号为载体与实际的I2C、USB等实际硬件设备通信。

- USB、I2C等总线设备,可以通过真正的硬件热插拔,从而触发具体的bus_type总线进行driver与device匹配,最终在/dev/*目录下构造实例化设备驱动。

- 而struct platform_bus_type对象在Linux内核中代表一个虚拟的平台设备总线。即在系统硬件中,不存在与platform_bus_type对应的硬件电路,在SOC中也不存在对应的总线控制器(USB、I2C等模组在SOC芯片中都有相关的硬件控制器)。

- 虽然struct platform_bus_type不存在真正的总线,但是我们在处理各种杂七杂八的驱动时(比如LED、智能卡、杂项设备、framebuffer等),也有把device_driver的驱动实现逻辑代码和device硬件bsp相关的代码分离出来的需求, 这样使得同样类型但是占用不同端口或者资源(比如 LED1、 LED2都是LED设备,但是一般会占用不同的GPIO口)的device能够共享同一份device_driver的逻辑代码,不需要为每一个LED设备都写一份驱动(维护量无比巨大)。

- 因而Linux内核采用 struct platform_bus_type、structplatform_device 与struct platform_driver三个对象继承了总线设备相关的基类对象,模仿系统总线的行为模式,通过struct platform_bus_type来管理 structplatform_driver与struct platform_device的设备匹配与设备构造实例化

- 虽然platform虚拟平台总线不像usb、I2C等总线接口有真正的硬件设备插拔事件。但是struct platform_driver与struct platform_device对象都实现了module接口,可以编译成module进行insmod/rmmod等动态装载于卸载。那么struct platform_driver与struct platform_device对象在在作为module动态地装载与卸载时,相当于模拟了总线的热插拔事件,那么可以通过insmod/rmmod模拟总线设备的热插拔,来触发struct platform_bus_type对象进行driver与device匹配,并在/dev/*目录下构造实例化真正的设备驱动。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

面向对象地分析Linux内核设备驱动(2)——Linux内核设备模型与总线 的相关文章

  • Skip List--跳表(全网最详细的跳表文章没有之一)

    笔者目前是CPP方向 xff0c 今年 xff08 2023届 xff09 秋招时在简历中写的就是跳表的项目 xff0c 当时是啃源码啃下的 xff0c 把跳表整体的思路是理顺了 但是在面试过程中 xff0c 有不少面试官都对这个项目很感兴
  • 报错:Caused by: org.xml.sax.SAXParseException

    Caused by org xml sax SAXParseException 文档根元素 34 project 34 必须匹配 DOCTYPE 根 34 null 34 错误提示 xff1a gframework beans factor
  • 用malloc动态申请一个二维数组

    利用二级指针申请一个二维数组 define CRT SECURE NO WARNINGS include lt iostream gt include lt vector gt include lt algorithm gt using n
  • 有符号/无符号整数相加溢出的判断方法

    1 有符号数相加溢出判断 1 1 两个有符号的数是正数 当两个有符号整数x y同为正数 xff0c 且x 43 y的结果为非正时 xff0c 发生了正溢出 define CRT SECURE NO WARNINGS include lt i
  • 【算法】求最小子集的和被5整除

    昨天面试了一家公式 xff0c 面试上来问我 xff0c 使用过哪些STL容器 xff0c 我说了一下 xff0c 然后又问从最简单的开始说 面试官 xff1a 说说使用vector是需要注意什么 xff1f 我 xff1a 注意什么 迭代
  • TCP报文结构

    TCP报文结构 TCP报文由俩部分组成 xff1a TCP报头和TCP数据 TCP报文是TCP传输的数据单元 端口号 xff1a 用来标识一台主机的不同进程 1 xff09 源端端口号 xff1a 源端口和IP层解析出来的IP地址标识报文的
  • 【算法】洗牌算法

    碎碎念 xff1a 面试的时候 xff0c 让写这道题 xff0c 快写完了 xff0c 面试官告诉我 xff0c 这是洗牌算法 xff0c 1 介绍 洗牌算法是将原来的数组进行打散 xff0c 使原数组的某个数在打散后的数组中的每个位置上
  • 【操作系统】第一章总结

    1 1 操作系统介绍 1 1 1 操作系统的概念 功能和目标 1 1 1 操作系统的概念 功能和目标 xff08 系统资源的管理者 提供接口 作为扩充机器 虚拟机 xff09 StudyWinter的博客 CSDN博客 1 1 2 操作系统
  • 【操作系统】2.1 进程与线程总结

    2 1 1 操作系统之进程的定义 特征 组成 组织 2 1 1 操作系统之进程的定义 特征 组成 组织 StudyWinter的博客 CSDN博客 进程由程序段 数据段 进程控制块 xff08 PCB xff09 三部分组成 进程是进程实体
  • 【操作系统】2.2 操作系统的调度

    2 2 1 操作系统之处理机调度的概念及层次 2 2 1操作系统之处理机调度的概念及层次 StudyWinter的博客 CSDN博客 操作系统调度的层次 高级调度 xff08 作业调度 xff09 xff1a 外存 内存 中级调度 xff0
  • 【操作系统】2.4 死锁

    这一节也非常重要 2 4 1 死锁的概念 2 4 1 死锁的概念 StudyWinter的博客 CSDN博客 在并发环境下 xff0c 各种进程因竞争资源而造成的一种互相等待对方手里的资源 xff0c 导致各进程都阻塞 xff0c 都无法向
  • 3.1.1 什么是内存?进程的基本原理,深入指令理解其过程

    0 思维导图 1 什么是内存 xff1f 有何作用 xff1f xff08 1 xff09 存储单元 内存 Memory 是计算机的重要部件 xff0c 也称内存储器和主存储器 xff0c 它用于暂时存放CPU中的运算数据 xff0c 以及
  • 怎样用AT指令查询本机手机号码

    分享一下我老师大神的人工智能教程 xff01 零基础 xff0c 通俗易懂 xff01 http blog csdn net jiangjunshow 也欢迎大家转载本篇文章 分享知识 xff0c 造福人民 xff0c 实现我们中华民族伟大
  • 3.1.2 操作系统内存管理管些什么?

    0 思维导图 1 内存空间的分配与回收 2 内存空间的扩展 xff08 实现虚拟性 xff09 3 地址转换 4 内存保护 1 操作系统负责内存空间的分配和回收 xff1b 2 操作系统需要提供某种技术从逻辑上对内存空间进行扩充 xff1b
  • C++11特性

    最近打算照着源文档学习C 43 43 特性 xff0c 先从C 43 43 11的特性开始 学习 xff1a C 43 43 11 cppreference com C 43 43 11 C 43 43 11是继C 43 43 98之后的最
  • C++11特性之auto

    占位符类型说明 对变量而言 xff0c 指定要声明的变量的类型将从其初始值中自动推导出来 对于函数而言 xff0c 指定的返回类型将从其返回语句中推导出来 xff08 从C 43 43 14开始 xff09 对于非模版类型的参数而言 xff
  • C++11特性之decltype

    检查一个实体的声明类型或者检查表达式类型和值类别 语法 strong decltype strong entity strong strong 1 since C 43 43 11 strong decltype strong expres
  • 【算法】151. 反转字符串中的单词

    链接 xff1a https leetcode cn problems reverse words in a string 给你一个字符串 s xff0c 请你反转字符串中 单词 的顺序 单词 是由非空格字符组成的字符串 s 中使用至少一个
  • TensorFlow中numpy与tensor数据相互转化

    numpy与tensor数据相互转化 xff1a Numpy2Tensor 虽然TensorFlow网络在输入Numpy数据时会自动转换为Tensor来处理 xff0c 但是我们自己也可以去显式的转换 xff1a data tensor 6
  • ESP32环境搭建 HomeKit 平台搭建 苹果智能家居HomeKit

    本文所有操作环境在linux下 xff0c 参考README文档 xff0c 官方sdk获取连接 https github com espressif esp apple homekit adk HomeKit是Apple开发的框架 xff

随机推荐

  • 乐鑫代码中Ring Buffers (环形缓冲区)说明

    本文翻译自乐鑫文档 xff0c 详见链接地址 xff0c 结合用例自行测试 xff0c 欢迎交流 源码链接ringbuf c 项目中用乐鑫的wifi模组 xff0c esp8266 xff0c esp32中发现ringbuf的身影 xff0
  • Qt界面程序中嵌入其他可执行exe程序

    背景 xff1a 本文实验环境 windows10 xff0c qt5 14 由于工作需要写了一个小的qt程序 xff0c 然后在实际使用过程中还要和其他小工具来回切换 xff0c 就想能不能把其他工具代码集成 xff0c 最后发现对我来说
  • Windows10系统旧电脑打包迁移新电脑

    由于需要换了一台新电脑 xff0c 但是配置系统是个繁琐的大工程 xff0c 想想windows的环境变量啥的就头大 xff0c 就想偷懒把系统直接转移到新电脑 xff0c 网上搜索了一下是可行的 xff0c 好多ghost备份还原 xff
  • 线程池1.0和any类

    threadpool h ifndef THREAD POOL H define THREAD POOL H include lt iostream gt include lt memory gt include lt functional
  • esp8266,esp32中的SPI FLASH 访问模式(QIO QOUT DIO DOUT)

    本文 ESP8266 和 ESP32 支持四种不同的 SPI flash 访问模式 xff1a DIO DOUT QIO 和 QOUT 这些可以通过 esptool py write flash 的 flash mode 选项设置 这些控制
  • IMX6ULL裸机启动,IMX6ULL运行freeRtos

    IMX6ULL裸机启动 xff0c IMX6ULL运行freeRtos 裸机启动部分 imx6ull中根据运行代码位置的不同RAM flash SD卡 xff0c 上电会先运行片上ROM中的代码 xff0c 搬运的数据大小是不同的 xff0
  • freeRtos中操作pxCurrentTCB部分汇编指令的理解

    关于freeRtos中操作pxCurrentTCB部分汇编指令的理解 asm void vPortSVCHandler void INDENT OFF PRESERVE8 ldr r3 61 pxCurrentTCB Restore the
  • 嵌入式开发中bin文件合并

    1 tr span class token string 39 000 39 span span class token string 39 377 39 span lt span class token operator span dev
  • 鲁班路由器AX1800折腾记录

    京东云鲁班畅享版64G Ram 512M flash 16M 型号 RF CP 02 ssh功能 当时看到帖子说R2010能直接进入ssh xff0c 果断关闭自动升级 xff0c 这一步就省略了 xff0c 参考如下 鲁班R2010 开通
  • Cannot convert a symbolic Tensor to a numpy array

    使用TF进行计算 xff0c 出现 xff1a Traceback most recent call last File 34 infer py 34 line 14 in lt module gt logits 61 classifier
  • ARM嵌入式系统为什么要做内存对齐

    做嵌入式系统软件开发 xff0c 经常在代码中看到各种各样的对齐 xff0c 很多时候我们都是知其然不知其所以然 xff0c 知道要做好各种对齐 xff0c 但是不明白为什么要对齐 xff0c 不对齐会有哪些后果 xff0c 这篇文章大概总
  • 第一篇博客,Hello World

    Hello World 这是我的CSDN第一篇博客 xff0c 掐指一算 xff0c 从学单片机开始 xff0c 学习开发嵌入式软件已经5年了 终于找到一个小窝能够和大家一起分享交流技术 今天是一个美好的开始 xff0c 你好 xff0c
  • Buildroot构建指南--Overview

    使用Buildroot 让嵌入式Linux系统构建更加便捷 本文以Buildroot 2016 05的版本为基础来讲解 xff0c 不同版本之间有细节差异 xff0c 需要根据读者使用的版本自行调整 Buildroot是什么 Buildro
  • Buildroot构建指南--快速上手与实用技巧

    Buildroot官方全英文使用手册的链接是https buildroot org downloads manual manual html xff0c 需要知道每一个细节的朋友 xff0c 可以仔细查阅 xff0c 这篇文章只是我自己从中
  • 构建Linux系统之实现Gnome桌面

    3 3 基于Ubuntu 实现Gnome 桌面 GNOME 是 GNU 网络对象模型环境 The GNU Network Object Model Environment 的简称 xff0c 目标是基于自由软件 xff0c 为Unix 或者
  • Buildroot构建指南——工具链

    Linux系统的交叉编译工具链用来将源代码变成bin文件或者库文件的一个软件 一般大家默认工具链等于gcc或者arm linux gcc xff0c 但是实际上 xff0c gcc只是工具链的编译器部分 xff0c 不是全部 xff0c 制
  • Buildroot构建指南——根文件系统(Rootfs)

    Buildroot构建指南 根文件系统 Rootfs Buildroot的Rootfs构建流程有一个大框架 xff0c 有些部分是Buildroot系统做好的 xff0c 有些细节需要自己来实现 xff0c Rootfs也是Buildroo
  • 面向对象地分析Linux内核设备驱动(1):——Linux内核驱动中面向对象的基本规则和实现方法

    Linux内核驱动中面向对象的基本规则和实现方法 内核版本 Linux Kernel 2 6 34 xff0c 与 Robert Love的 Linux Kernel Development 第三版 所讲述的内核版本一样 源代码下载路径 h
  • 可在线OTA升级的嵌入式系统设计方案

    什么是在线OTA升级 OTA 是Over the Air 的简写 xff0c 空中下载技术的意思 OTA 在线升级在日常消费电子产品中很常见 xff0c 比如手机 xff0c 机顶盒等 xff0c 通过网络 xff0c 下载升级数据包 xf
  • 面向对象地分析Linux内核设备驱动(2)——Linux内核设备模型与总线

    Linux内核设备模型与总线 内核版本 Linux Kernel 2 6 34 xff0c 与 Robert Love的 Linux Kernel Development 第三版 所讲述的内核版本一样 源代码下载路径 https www k