Modalias
本文档是对 Linux 内核和模块如何查看和理解硬件的介绍,以及这如何转化为 sysfs 'modalias'
什么是 modalias
Modalias 是一个小小的 sysfs 技巧,它将硬件信息导出到一个名为 'modalias' 的文件。该文件只是简单地包含了硬件正常暴露的信息的格式化形式。
$ cat /sys/devices/pci0000:00/0000:00:1f.1/modalias
pci:v00008086d000024DBsv0000103Csd0000006Abc01sc01i8A
什么是 modalias 文件?
如上所述,modalias 文件只是简单地暴露了给定硬件已经告诉内核的信息。该文件只是指定了一种暴露此信息的结构。让我们回到上面的例子
$ cat /sys/devices/pci0000:00/0000:00:1f.1/modalias
pci:v00008086d000024DBsv0000103Csd0000006Abc01sc01i8A
让我们逐个分解它。首先是文件名,/sys/devices/pci0000:00/0000:00:1f.1/modalias
- pci0000:00 是第一个 PCI 总线的 ID。对于大多数机器来说,这将是你拥有的唯一 PCI 总线,但它有可能扩展到 pci0000:01 或 pci0000:02 - 具体细节并不重要,因为你可以很好地猜测你只有一个 PCI 总线(提示: 尝试
ls /sys/devices/pci*
来检查) - 0000:00:1f.1 是给定设备在 PCI 总线上的索引。具体来说,它位于总线 0000:00 上,索引为 1f.1
- 除非你想知道所有这些数字来自哪里,否则所有这些都相当不重要。为了完整性,如果你检查
lspci
的输出,你将看到相同的信息
$ lspci
00:1f.1 IDE interface: Intel Corp.: Unknown device 24db (rev 02)
现在,让我们看一下设备 00:1f.1 的 modalias 文件的内容
pci:v00008086d000024DBsv0000103Csd0000006Abc01sc01i8A
嗯,嘿,我能看到 pci!我认得那个,但是结尾那些乱七八糟的是什么?这些乱七八糟的东西实际上是结构化数据。你会注意到一个重复的字母/数字方案。让我们把它分开,使其更容易阅读
v 00008086 d 000024DB sv 0000103C sd 0000006A bc 01 sc 01 i 8A
这些标识符以及相应的十六进制数字表示给定设备暴露的一些信息。首先,v 是供应商 ID,d 是设备 ID - 这些都是非常标准的数字。实际上,像 hwdetect 这样的工具会使用这些以及其他 sysfs 文件来提供设备信息。你甚至可以找到网站来根据供应商和设备 ID 查找特定的硬件标识,例如,https://devicehunt.com/
我们也可以在这里看到这些数字
$ lspci -n
00:1f.1 Class 0101: 8086:24db (rev 02)
看看 8086:24db 如何与上面列出的 v 和 d 标记匹配?
记录一下,sv 和 sd 是供应商和设备的“子系统”版本。大多数情况下,这些都被忽略。它们主要由硬件开发人员用来区分内部工作原理的细微差异,这些差异不会改变设备的整体。
bc(基类)和 sc(子类)用于创建 lspci
列出的“Class”,顺序为“bcsc”。这是设备类,它相当通用。在这种情况下,“class”在正常的 lspci
输出中查找。我们可以看到“Class 0101”映射到“IDE Interface”(lspci
也查找供应商和设备 ID - 8086 映射到“Intel Corp.”,24DB 映射到“Unknown Device”,呵呵)
i 是“编程接口”,它仅对少数设备类有意义。
此信息如何使用?
好的,现在我们都知道这些信息是什么了。一堆每个设备都暴露的晦涩数字。没什么大不了的。当谈论模块时,这有什么关系呢?
人们倾向于忽略的一件事是 depmod
所做的所有工作。当你运行 depmod
时,它会在 /lib/modules/`uname -r`
中构建一系列“map”文件,这些文件告诉 modprobe 如何处理它需要做的某些事情。在这种情况下,我们可以忽略它们中的大多数。重要的是 modules.alias
。此文件包含模块的别名或辅助名称。只是为了演示,让我们看看别名,例如,snd_intel8x0m
$ grep snd_intel8x0m /lib/modules/$(uname -r)/modules.alias
alias pci:v00008086d00002416sv*sd*bc*sc*i* snd_intel8x0m alias pci:v00008086d00002426sv*sd*bc*sc*i* snd_intel8x0m alias pci:v00008086d00002446sv*sd*bc*sc*i* snd_intel8x0m alias pci:v00008086d00002486sv*sd*bc*sc*i* snd_intel8x0m alias pci:v00008086d000024C6sv*sd*bc*sc*i* snd_intel8x0m alias pci:v00008086d000024D6sv*sd*bc*sc*i* snd_intel8x0m alias pci:v00008086d0000266Dsv*sd*bc*sc*i* snd_intel8x0m alias pci:v00008086d000027DDsv*sd*bc*sc*i* snd_intel8x0m alias pci:v00008086d00007196sv*sd*bc*sc*i* snd_intel8x0m alias pci:v00001022d00007446sv*sd*bc*sc*i* snd_intel8x0m alias pci:v00001039d00007013sv*sd*bc*sc*i* snd_intel8x0m alias pci:v000010DEd000001C1sv*sd*bc*sc*i* snd_intel8x0m alias pci:v000010DEd00000069sv*sd*bc*sc*i* snd_intel8x0m alias pci:v000010DEd00000089sv*sd*bc*sc*i* snd_intel8x0m alias pci:v000010DEd000000D9sv*sd*bc*sc*i* snd_intel8x0m
嘿,等等!我认得那个!那是之前的供应商/设备 ID 信息!
是的,它是。它是一个相当简单的格式“alias <something> <actual module>”。实际上,你可以为你想要的任何东西设置别名。我可以添加“alias boogabooga snd_intel8x0m”,然后安全地“modprobe boogabooga”。
“*”表示它将匹配任何内容,很像文件系统 globbing (ls somedir/*
)。如前所述,大多数别名通过 “*” 匹配忽略 sv、sd、bc、sc 和 i。
modules.alias 文件从何而来?
好的,现在你可能会想“好吧,硬件探测 曾经根据设备表查找东西,这有什么不同?”
不同之处在于此查找表不是静态的。它不是手动维护的。实际上,它是在你每次运行 depmod
时动态构建的。“这些信息从何而来?”,你问?为什么,来自 内核模块 本身。当你考虑一下,每个特定的模块都应该知道它支持哪些硬件,因为它专门为该硬件编码。我的意思是,nvidia 模块开发人员知道他们的模块只适用于 Nvidia(供应商)显卡(类)。实际上,模块实际上导出了此信息。它说“嘿,我可以支持这个:”。
$ modinfo nvidia filename: /lib/modules/2.6.14-ARCH/kernel/drivers/video/nvidia.ko license: NVIDIA alias: char-major-195-* vermagic: 2.6.14-ARCH SMP preempt 686 gcc-4.1 depends: agpgart alias: pci:v000010DEd*sv*sd*bc03sc00i00*
正如你在列出的别名中看到的,它专门查找供应商“10DE”(Nvidia)和 bc/sc 0300(很可能是“显卡”)。实际上,如果你查看 snd_intel8x0m 的 modinfo
$ modinfo snd_intel8x0m filename: /lib/modules/2.6.14-ARCH/kernel/sound/pci/snd-intel8x0m.ko author: Jaroslav Kysela <perex@suse.cz> description: Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7013; NVidia MCP/2/2S/3 modems license: GPL vermagic: 2.6.14-ARCH SMP preempt 686 gcc-4.1 depends: snd-ac97-codec,snd-pcm,snd-page-alloc,snd alias: pci:v00008086d00002416sv*sd*bc*sc*i* alias: pci:v00008086d00002426sv*sd*bc*sc*i* alias: pci:v00008086d00002446sv*sd*bc*sc*i* alias: pci:v00008086d00002486sv*sd*bc*sc*i* alias: pci:v00008086d000024C6sv*sd*bc*sc*i* alias: pci:v00008086d000024D6sv*sd*bc*sc*i* alias: pci:v00008086d0000266Dsv*sd*bc*sc*i* alias: pci:v00008086d000027DDsv*sd*bc*sc*i* alias: pci:v00008086d00007196sv*sd*bc*sc*i* alias: pci:v00001022d00007446sv*sd*bc*sc*i* alias: pci:v00001039d00007013sv*sd*bc*sc*i* alias: pci:v000010DEd000001C1sv*sd*bc*sc*i* alias: pci:v000010DEd00000069sv*sd*bc*sc*i* alias: pci:v000010DEd00000089sv*sd*bc*sc*i* alias: pci:v000010DEd000000D9sv*sd*bc*sc*i*
它匹配了通过 'grep'ing 别名文件找到的别名。每个模块导出的这些别名由 depmod
收集并动态合并到 modules.alias
文件中。没有手动更改查找表,因为它是即时构建的。每个模块都确切地知道它支持什么,因此 depmod
可以使用该信息来帮助加载模块。
udev 如何工作?
udev 与 sysfs(首先暴露 modalias 的文件系统)紧密相连。实际上,要基于 modalias 在添加新设备时(或在启动时首次启动 udev 时)加载模块,这非常简单
DRIVER!="?*", ENV{MODALIAS}=="?*", RUN{builtin}="kmod load $env{MODALIAS}"
是的,就是这样。这是一行代码。这简单的行代码,是默认 udev 规则的一部分,取代了 hotplug。令人惊叹,不是吗?
参见
本文展示了其他 modalias 模板,例如 usb、dmi 和 acpi 子类型
- Modalias 字符串 - 一种将“东西”映射到硬件的实用方法,作者 Petter Reinholdtsen