linux设备模型____宏观印象

2024-05-09 22:38

本文主要是介绍linux设备模型____宏观印象,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

linux设备模型____宏观印象
最近一个机会需要研究一个marvell芯片的设备的驱动,涉及驱动和一些用户态相关部分,正好学习一下驱动和sysfs,本文先是原理,后面的文章是详细描述。本文依托的是linux 2.6.32。
事实上,linux设备模型这个东西,也就是常看到的总线、设备、驱动、类等等,是我们自己抽象出来的理论;但linux的设备驱动,还确实是按照这个理论组建的,不论是linux内核启动过程中的相关初始化过程,还是后续我们自己的驱动的加载删除,都是按照这个理论组建的(也无法不按这种理论组建,因为驱动编程必须得调用内核提供的相应函数),这个组建也是比较繁杂的,后面会详细描述。
另一方面,从代码角度,这个组建过程是很细致的一个过程,它的细致还体现在两方面,一方面,针对总线、设备、驱动、类等等有一堆函数涉及一堆逻辑;另一方面这些总线、设备、驱动、类等等,之所以相关性这么强,还因为linux内核制造出诸如kobject、kset等多种结构来把这个相关性实现,这些东西也很繁杂,后面也会详细描述。
鉴于它很麻烦,所以一点一点描述,首先是从设备模型这个顶层来讲:
肯定都知道sysfs挂在/sys目录下,也肯定知道这个sysfs是和设备驱动关系非常大,到底sysfs是怎么回事后面再说,目前只要知道sysfs和proc很相似、都是基于内存的文件系统、都有查看和设置内核参数的功能、并且sysfs还是linux统一的设备模型的管理者、sysfs挂在/sys目录下即可;先从设备看/sys目录下都是些什么,下面是我的设备的情况:
/ # ls sys/ -l
total 0
drwxr-xr-x   33 root     root             0 Jan  1 00:00 block
drwxr-xr-x   11 root     root             0 Jan  1 00:00 bus
drwxr-xr-x   28 root     root             0 Jan  1 00:00 class
drwxr-xr-x    4 root     root             0 Jan  1 00:02 dev
drwxr-xr-x    6 root     root             0 Jan  1 00:00 devices
drwxr-xr-x    2 root     root             0 Jan  1 00:03 firmware
drwxr-xr-x    2 root     root             0 Jan  1 00:03 fs
drwxr-xr-x    3 root     root             0 Jan  1 00:03 kernel
drwxr-xr-x   41 root     root             0 Jan  1 00:03 module
可见是一堆目录,先把firmware、fs、kernel抛开,其余的block、bus、class、dev、devices、module就是设备当前的设备模型的实现结果;其中block是所有块设备的“集合”,bus是系统当前所有总线的“集合”,class是系统当前都设备的类的“集合”,dev暂时先不管,devices是系统当前所有设备的“集合”,module是系统当前具有非0属性的模块;有时候某些linux机器还会有power目录,这是和电源管理相关的,我这个设备没有,下面是稍细致一点描述这些目录:
/sys/devices:内核对系统中所有设备的分层次表达模型;
/sys/dev/:这个目录下维护一个按字符设备和块设备的主次号码(major:minor)链接到真实的设备(/sys/devices下)的符号链接文件;
/sys/bus/:内核设备按总线类型分层放置的目录结构, devices目录下的所有设备都是连接于某个总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接,bus是构成 Linux 统一设备模型的重要部分;
/sys/class/:是从一个“类别”的角度区分设备,如从PCI、USB等角度制造几个类别,注意,class目录下的设备不一定挂在任何一个总线上,挂在某个总线上的设备不一定出现在class目录下;
/sys/block/:这里是系统中当前所有的块设备所在;
/sys/firmware/:这里是系统加载固件机制的对用户空间的接口,暂不考虑;
/sys/fs/:这里按照设计是用于描述系统中所有文件系统,包括文件系统本身和按文件系统分类存放的已挂载点,但目前只有 fuse,gfs2 等少数文件系统支持 sysfs 接口,一些传统的虚拟文件系统(VFS)层次控制参数仍然在 sysctl (/proc/sys/fs) 接口中,比如我的设备,fs目录是个空目录;
/sys/kernel/:这里是内核所有可调整参数的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的 slab 分配器等几项较新的设计在使用它,其它内核可调整参数仍然位于 sysctl (/proc/sys/kernel) 接口中,比如我的设备,kernel目录是个空目录;
/sys/module/:这里有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件(vmlinuz)中还是编译为外部模块(ko文件),都可能会出现在 /sys/module 中: 编译为外部模块(ko文件)在加载后会出现对应的 /sys/module/<module_name>/, 并且在这个目录下会出现一些属性文件和属性目录来表示此外部模块的一些信息,如版本号、加载状态、所提供的驱动程序等;编译为内联方式的模块则只在当它有非0属性的模块参数时会出现对应的/sys/module/<module_name>,这些模块的可用参数会出现在 /sys/modules/<modname>/parameters/<param_name>中;
/sys/power/:这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等;

除了上面的这些个,目前的内核可能还有一些其他的目录,但目前不用考虑这么多了,目前只考虑对于理解linux设备模型和sysfs最重要的几个,就是上面黑体的4个,bus、device、driver、class;
1、 为什么要搞linux设备模型?
这个确实还是有原因的,正如很多文章的例子所讲,一个U盘插在电脑的USB总线设备上,USB总线设备以PCI设备的身份挂在PCI总线设备上,PCI总线设备直接受CPU管理,当挂起(suspend)电源时,应该以“U盘->USB总线设备->PCI总线设备”的顺序通知每一个设备将电源挂起,当恢复(resume)电源时,应该以反方向的顺序恢复电源,如果顺序不对,设备将无法正常操作;这就是搞linux设备模型的需求或原因。
所以,linux的设备模型要搞成这么个样子:最高层次是总线,可以有不同的总线;然后是每个总线下挂的设备(或是下一级总线,如PCI和USB的关系);而驱动(driver)也挂在总线之下,当它匹配某设备时,也会和该设备产生一些关系;最后是class,它和前面的还不太一样,它是按设备的具体功能分类,底下的各个类和类下边的设备不一定和bus下的设备能够对应,反之亦然;
上面的描述肯定不清楚,这是因为linux设备模型的具体实现是一个非常繁杂的系统,不同部分的代码关联度很大,这对代码效率提高非常有帮助,因为避免了重复地搞很多东西,但确实不利于我这样的初学者理解清楚,下面就是具体描述:
2、 表面理解下什么是bus、device、driver:
下面是linux内核初始化时的一部分内容:
void __init driver_init(void)
{
 /* These are the core pieces */
 /*devtmpfs 的功用是在 Linux 核心 启动早期建立一个初步的 /dev ,和本文关系不大*/
 devtmpfs_init();
 /*重要,在/sys目录下创建一个目录叫devices(实际可不单单创建个目录),之后通过总线挂载的设备都会出现在这个目录下的各个子目录里*/
 devices_init();
 /*重要,在/sys目录下创建一个目录叫bus(同样不单单创建个目录),之后创建的新总线和挂载在某总线下的设备都会出现在这个目录下的各个子目录里*/
 buses_init();
 /*重要,在/sys目录下创建一个目录叫class(同样不单单创建个目录),之后创建的新class和挂载在某class下的设备都会出现在这个目录下的各个子目录里*/
 classes_init();
 /*意思差不多,暂不关注*/
 firmware_init();
 /*虚拟机相关,不关注*/
 hypervisor_init();
/*分水岭,至此,/sys下已经有了三个重要的目录(不单单是目录),bus、devices、class*/
 /* These are also core pieces, but must come after the
  * core core pieces.
  */
 /*重要,这可能是内核的第一个总线,platform,它也是个目录在bus目录下*/
 platform_bus_init();
 /*这可能是内核的第一个设备,system,是/sys/devices目录下第一个添加的东西,它同时是个“集合”,即它本身虽然是设备,但更重要的是它目录底下的设备,暂不关注*/
 system_bus_init();
 /*后面两个暂不关注*/
 cpu_dev_init();
 memory_dev_init();
}
小节:
函数buses_init创建了总线bus这么个东西,并且创建了第一个总线platform,并且platform目录下还有device和driver两个目录;先不看函数buses_init里面到底干什么,先记住buses_init这个函数让linux内核有了总线这个概念了,并且有一个platform总线;
函数devices_init创建了设备devices这么个东西,并且创建了第一个设备system;先不看函数devices_init里面到底干什么,先记住devices_init这个函数让linux内核有了设备这个概念了,并且有system这个设备;
函数classes_init创建了类class这么个东西,先不看函数classes_init里面到底干什么,先记住classes_init这个函数让linux内核有了类这个概念了;
从这以后,编译进内核的各个设备将分别在这些目录下增加各自的内容,当系统启动差不多了,再查看/sys目录下,比如我的设备的状态是这样的:
/ # ls sys/bus/ -l
total 0
drwxr-xr-x    4 root     root             0 Jan  1 16:59 hid
drwxr-xr-x    4 root     root             0 Jan  1 00:00 i2c
drwxr-xr-x    4 root     root             0 Jan  1 16:59 mmc
drwxr-xr-x    5 root     root             0 Jan  1 16:59 pci
drwxr-xr-x    4 root     root             0 Jan  1 13:37 platform
drwxr-xr-x    4 root     root             0 Jan  1 16:59 scsi
drwxr-xr-x    4 root     root             0 Jan  1 16:59 sdio
drwxr-xr-x    4 root     root             0 Jan  1 16:59 usb
drwxr-xr-x    4 root     root             0 Jan  1 16:59 usb-serial
/ #
这说明,我的设备现在有一共9个总线;
其中,platform总线里边的device目录是这样的:
/ # ls sys/bus/platform/devices/
boardEnv/             mv64xxx_i2c.0/       regulatory.0/
cust/                 mv88fx_neta.0/       serial8250.0/
ehci_marvell.70059/    mvsdio/              serial8250/
gpon/                neta/                tpm/
kw_cpuidle.0/         orion_wdt/           tpm_sw/
这说明,我的设备的platform总线下,一共挂载了15个设备;
另外,platform总线里边的driver目录是这样的:
/ # ls sys/bus/platform/drivers/
ehci_marvell/   mv64xxx_i2c/   mvsdio/        soc-audio/
kw_cpuidle/    mv88fx_neta/   serial8250/
应该是有7个驱动,可见,platform挂载的设备不一定都有驱动,这是正常的,因为有的设备就是为了调试(即sysfs功能,查看和设置内核参数)设备参数用的,这也就是说明了很多文章讲的“sysfs是linux设备模型的附属物”是什么意思。
再看看/sys/devices目录下的platform目录下有什么内容:
/ # ls sys/devices/platform/
boardEnv/            mv88fx_neta.0/       serial8250/
cust/                mvsdio/              tpm/
ehci_marvell.70059/    neta/                tpm_sw/
gpon/                orion_wdt/           uevent
kw_cpuidle.0/        regulatory.0/
mv64xxx_i2c.0/       serial8250.0/
应该是16个设备,比/sys/bus/platform/devices目录下多了一个regulatory.0/,先不管它,其余都是对应的。
再看看class的情况:
/ # cat sys/class/
bdi/           mem/         pon/         scsi_host/     vc/
firmware/      misc/          ppp/         sound/       vtconsole/
i2c-adapter/    mmc_host/     rtc/          tpm/
i2c-dev/        mtd/          scsi_device/   tty/
ieee80211/     net/           scsi_disk/     ubi/
input/         pci_bus/       scsi_generic/   usb_device/
应该是26个class,有的class下有一个设备,有的class下有很多设备,并且基本不和/sys/devices/platform/或/sys/bus/platform/devices/目录下的内容有什么对应关系,这就更说明了sysfs的灵活性,它专门用于调试的,linux驱动模型中,除了满足设备正常运行的device和driver,还有很多设备是虚拟的,是有意加的,只是为了调试。
下面举几个实际的例子,先粗略的看设备是怎么加入这个模型中去的:
int __init buses_init(void)
{
 bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
 if (!bus_kset)
  return -ENOMEM;
 return 0;
}
Eg1:函数buses_init告诉linux内核,什么叫做bus,调用函数kset_create_and_add;
int __init platform_bus_init(void)
{
 int error;
 early_platform_cleanup();
 error = device_register(&platform_bus);
 if (error)
  return error;
 error =  bus_register(&platform_bus_type);
 if (error)
  device_unregister(&platform_bus);
 return error;
}
Eg2:函数platform_bus_init,首先创建了一个设备叫platform_bus,这是个总线设备(platform是总线,同时本身也是个设备),调用函数device_register;然后创建了一个总线叫platform,调用函数bus_register;
pd = bus_find_device_by_name(&platform_bus_type, NULL, "gpon");
  if (!pd) {
   platform_device_register_simple("gpon", -1, NULL, 0);
   pd = bus_find_device_by_name(&platform_bus_type, NULL, "gpon");
  }
Eg3:函数platform_device_register_simple在platform总线下创建了设备gpon,实际调用函数是platform_device_add;
pon_udev_class = class_create(THIS_MODULE, “pon”);
Eg4:函数class_create创建了一个类叫pon;
pon_udev_dev   = device_create(pon_udev_class, NULL, dev, NULL, PON_DEV_NAME);
Eg5:紧跟着,函数device_create在类pon下创建了个设备也叫pon;
static struct platform_driver mv64xxx_i2c_driver = {
 .probe = mv64xxx_i2c_probe,
 .remove = __devexit_p(mv64xxx_i2c_remove),
 .driver = {
  .owner = THIS_MODULE,
  .name = MV64XXX_I2C_CTLR_NAME,
 },
};
static int __init
mv64xxx_i2c_init(void)
{
 return platform_driver_register(&mv64xxx_i2c_driver);
}
Eg6:函数platform_driver_register在platform总线下创建了驱动mv64xxx_i2c_driver;
retval = bus_register(&i2c_bus_type);
Eg7:函数bus_register创建了又一个总线i2c_bus;
retval = i2c_add_driver(&dummy_driver);
Eg8:函数i2c_add_driver在总线i2c_bus下创建了一个驱动dummy_driver,实际调用函数driver_register;
res = i2c_add_driver(&i2cdev_driver);
Eg9:函数i2c_add_driver在总线i2c_bus下又创建了一个驱动i2cdev_driver,实际调用函数driver_register;
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
Eg10:函数class_create创建了一个class,叫i2c-dev;
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
Eg11:函数class_compat_register创建了一个class叫i2c-adapter;

经过上面的11个例子,可以看看,是不是和前面打印的bus、devices、driver、class里的相应内容对应上。
这时,应该可以有了linux设备模型的一个初步的概念,即:
1、 bus、devices、driver、class的每一个内容,都可以对应上sysfs即/sys目录下的一个个目录;
2、 每一个bus、devices、driver、class本身也都是一个设备(这个从代码实现上很好理解,因为它们和具体设备、驱动一样,也必须是一个个目录,只不过是相对的顶层目录罢了,可以从数据结构的树的节点去理解)
3、 bus和class都是在顶层,它们下边是一个个实际的bus、class条目,每个bus条目下有device和driver两个目录,每个目录下可能有一些设备目录,每个class条目下可能有一些设备目录;
只有这些远远不够,后面就要详细描述了,描述这种设备模型到底从代码上是怎么实现的,这就更加深刻的让我们理解linux设备模型到底是什么、sysfs是什么以及怎么用。

这篇关于linux设备模型____宏观印象的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/974702

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费