本文摘自 Manning 出版的 Linux in Action 第 15 章。
Linux 使用内核模块管理硬件外围设备。以下是其工作原理。
运行中的 Linux 内核是你不想打乱的东西之一。毕竟,内核是驱动计算机所有操作的软件。考虑到实时系统上必须同时管理多少细节,最好让内核尽可能少地分心,专心完成其工作。但是,如果即使对计算环境进行微小的更改也必须重启整个系统,那么插入新的网络摄像头或打印机可能会对你的工作流程造成痛苦的 disruption。每次添加设备都必须重启才能让系统识别它,这简直效率低下。
为了在稳定性和可用性这两个相互对立的优点之间创建有效的平衡,Linux 隔离了内核,但允许你通过可加载内核模块 (LKM) 动态添加特定功能。如下图所示,你可以将模块视为一段软件,它告诉内核在哪里找到设备以及如何处理它。反过来,内核使设备可供用户和进程使用,并监督其运行。

内核模块充当设备和 Linux 内核之间的翻译器。
没有什么可以阻止你编写自己的模块来以你喜欢的方式支持设备,但何必呢?Linux 模块库已经非常强大,通常不需要自己动手。而且绝大多数情况下,Linux 会自动加载新设备的模块,甚至你都不会意识到。
不过,有时出于某种原因,它不会自行发生。(你不会想让招聘经理焦急地等待你微笑的脸庞出现在视频会议求职面试中太久。)为了帮助解决问题,你需要更多地了解内核模块,特别是如何找到将运行你的外围设备的实际模块,以及如何手动激活它。
查找内核模块
按照公认的约定,模块是扩展名为 .ko(内核对象)的文件,位于 /lib/modules/
目录下。不过,在你一直导航到这些文件之前,你可能必须做出选择。因为你在启动时可以选择从发布列表中加载一个,所以支持你的选择所需的特定软件(包括内核模块)必须存在于某个地方。好吧,/lib/modules
/ 就是其中一个地方。在那里,你会找到充满每个可用 Linux 内核版本模块的目录;例如
$ ls /lib/modules
4.4.0-101-generic
4.4.0-103-generic
4.4.0-104-generic
在我的例子中,活动的内核是版本号最高的版本(4.4.0-104-generic),但不能保证你的情况也是如此(内核经常更新)。如果你要对你希望在实时系统上使用的模块进行一些工作,你需要确保你拥有正确的目录树。
好消息:有一个可靠的技巧。与其通过名称识别目录并希望你得到正确的目录,不如使用始终指向活动内核名称的系统变量。你可以使用 uname -r
调用该变量(-r
指定来自通常会显示在系统信息中的内核版本号)
$ uname -r
4.4.0-104-generic
有了这些信息,你可以使用称为命令替换的过程将 uname
合并到你的文件系统引用中。例如,要导航到正确的目录,你需要将其添加到 /lib/modules
。要告诉 Linux “uname” 不是文件系统位置,请将 uname
部分用反引号括起来,如下所示
$ ls /lib/modules/`uname -r`
build modules.alias modules.dep modules.softdep
initrd modules.alias.bin modules.dep.bin modules.symbols
kernel modules.builtin modules.devname modules.symbols.bin
misc modules.builtin.bin modules.order vdso
你会在 kernel/
目录下的子目录中找到大多数模块。花几分钟浏览这些目录,了解事物的排列方式以及可用的内容。文件名通常会让你很好地了解你正在查看的内容。
$ ls /lib/modules/`uname -r`/kernel
arch crypto drivers fs kernel lib mm
net sound ubuntu virt zfs
这是一种定位内核模块的方法;实际上,这是快速而粗略的方法。但这并不是唯一的方法。如果你想获得完整集合,你可以使用 lsmod
列出所有当前加载的模块以及一些基本信息。此截断输出的第一列(此处列出的内容太多)是模块名称,后跟文件大小和编号,然后是每个模块所依赖的其他模块的名称
$ lsmod
[...]
vboxdrv 454656 3 vboxnetadp,vboxnetflt,vboxpci
rt2x00usb 24576 1 rt2800usb
rt2800lib 94208 1 rt2800usb
[...]
有多少太多了?好吧,让我们再次运行 lsmod
,但这次将输出管道传输到 wc -l
以获取行数
$ lsmod | wc -l
113
这些是已加载的模块。总共有多少可用?运行 modprobe -c
并计算行数将给出该数字
$ modprobe -c | wc -l
33350
有 33,350 个可用模块!?看来多年来有人一直在努力为我们提供运行物理设备的软件。
注意:在某些系统上,你可能会遇到自定义模块,这些模块在其唯一的 /etc/modules
文件条目中或作为保存到 /etc/modules-load.d/
的配置文件中引用。这些模块很可能是本地开发项目的产物,可能涉及尖端实验。无论如何,最好对你正在查看的内容有所了解。
这就是你查找模块的方式。你的下一个工作是弄清楚如何手动加载非活动模块,如果出于某种原因,它没有自行发生。
手动加载内核模块
在你加载内核模块之前,逻辑上会指示你必须确认它存在。在你执行此操作之前,你需要知道它的名称。获得该部分有时需要同等程度的魔力和运气,以及在线文档作者的辛勤工作的一些帮助。
我将通过描述我之前遇到过的一个问题来说明这个过程。有一天,出于我仍然不明白的原因,笔记本电脑上的 WiFi 接口停止工作了。就这样。也许是软件更新把它搞坏了。谁知道呢?我运行了 lshw -c network
,并得到了这个非常奇怪的信息
network UNCLAIMED
AR9485 Wireless Network Adapter
Linux 识别了接口(Atheros AR9485),但将其列为 unclaimed。好吧,正如他们所说,“当情况变得艰难时,坚强的人会搜索互联网。” 我搜索了 atheros ar9 linux module,在浏览了五页甚至十年前的结果,建议我要么编写自己的模块,要么干脆放弃之后,我终于发现(至少在 Ubuntu 16.04 中)存在一个可用的模块。它的名字是 ath9k。
是的!这场战斗几乎已经胜利了!向内核添加模块比听起来容易得多。为了再次检查它是否可用,你可以针对模块的目录树运行 find
,指定 -type f
以告诉 Linux 你正在查找文件,然后添加字符串 ath9k
以及 glob 星号以包含所有以你的字符串开头的文件名
$ find /lib/modules/$(uname -r) -type f -name ath9k*
/lib/modules/4.4.0-97-generic/kernel/drivers/net/wireless/ath/ath9k/ath9k_common.ko
/lib/modules/4.4.0-97-generic/kernel/drivers/net/wireless/ath/ath9k/ath9k.ko
/lib/modules/4.4.0-97-generic/kernel/drivers/net/wireless/ath/ath9k/ath9k_htc.ko
/lib/modules/4.4.0-97-generic/kernel/drivers/net/wireless/ath/ath9k/ath9k_hw.ko
再走一步,加载模块
# modprobe ath9k
就是这样。无需重启。无需大惊小怪。
这是另一个示例,向你展示如何处理已损坏的活动模块。曾经有一段时间,使用我的 Logitech 网络摄像头和特定的软件会导致摄像头在下次系统启动之前无法被任何其他程序访问。有时我需要在不同的应用程序中打开摄像头,但没有时间关闭并再次启动。(我运行了很多应用程序,并且在启动后将它们全部就位需要一些时间。)
因为这个模块可能是活动的,所以使用 lsmod
搜索单词 video 应该会给我一些关于相关模块名称的提示。事实上,它比提示更好:唯一用单词 video 描述的模块是 uvcvideo(正如你在下面看到的)
$ lsmod | grep video
uvcvideo 90112 0
videobuf2_vmalloc 16384 1 uvcvideo
videobuf2_v4l2 28672 1 uvcvideo
videobuf2_core 36864 2 uvcvideo,videobuf2_v4l2
videodev 176128 4 uvcvideo,v4l2_common,videobuf2_core,videobuf2_v4l2
media 24576 2 uvcvideo,videodev
可能有一些我可以控制的东西导致了崩溃,我想我可以更深入地挖掘一下,看看是否可以用正确的方式修复问题。但你知道事情就是这样;有时你不在乎理论,只是想让你的设备工作。所以我使用 rmmod
杀死 uvcvideo 模块,并使用 modprobe
重新启动它,一切都很好
# rmmod uvcvideo
# modprobe uvcvideo
再次:无需重启。没有顽固的血迹。
评论已关闭。