您可以通过运行 lsmod 查看已加载到内核中的模块,它通过读取文件来获取信息/proc/modules.
这些模块是如何进入内核的呢?当内核需要一个内核中没有的功能时,内核模块守护进程 kmod[1] 执行 modprobe 来加载模块。modprobe 传递的字符串有两种形式之一
模块名称,例如softdog或ppp.
更通用的标识符,例如char-major-10-30.
如果 modprobe 收到一个通用标识符,它首先在文件/etc/modprobe.conf中查找该字符串。[2] 如果它找到类似下面的 alias 行
alias char-major-10-30 softdog |
它就知道这个通用标识符指的是模块softdog.ko.
接下来,modprobe 查找文件/lib/modules/version/modules.dep,以查看在加载请求的模块之前是否必须加载其他模块。此文件由 depmod -a 创建,并包含模块依赖关系。例如,msdos.ko需要fat.ko模块已加载到内核中。如果另一个模块定义了请求模块使用的符号(变量或函数),则请求的模块依赖于另一个模块。
最后,modprobe 使用 insmod 首先将任何先决条件模块加载到内核中,然后加载请求的模块。modprobe 指示 insmod 到/lib/modules/version/[3],这是模块的标准目录。insmod 旨在对模块的位置相当“笨拙”,而 modprobe 则知道模块的默认位置,知道如何找出依赖关系并按正确的顺序加载模块。因此,例如,如果您想加载 msdos 模块,您必须运行
insmod /lib/modules/2.6.11/kernel/fs/fat/fat.ko insmod /lib/modules/2.6.11/kernel/fs/msdos/msdos.ko |
或
modprobe msdos |
我们在这里看到的是: insmod 需要您传递完整路径名并按正确的顺序插入模块,而 modprobe 只需获取名称(不带任何扩展名),并通过解析来找出它需要知道的一切/lib/modules/version/modules.dep.
Linux 发行版提供 modprobe、insmod 和 depmod 作为一个名为 module-init-tools 的软件包。在以前的版本中,该软件包被称为 modutils。一些发行版还设置了一些包装器,允许并行安装这两个软件包,并做正确的事情,以便能够处理 2.4 和 2.6 内核。用户无需关心细节,只要他们运行的是这些工具的最新版本。
现在您知道模块是如何进入内核的了。如果您想编写自己的依赖于其他模块的模块(我们称之为“堆叠模块”),那么还有更多内容。但这将不得不等到以后的章节。在解决这个相对高级的问题之前,我们还有很多内容要介绍。
在我们深入研究代码之前,我们需要讨论一些问题。每个人的系统都不同,每个人都有自己的习惯。让您的第一个 “hello world” 程序正确编译和加载有时可能很棘手。请放心,在您克服第一次的最初障碍之后,一切都会一帆风顺。
为某个内核编译的模块如果引导到不同的内核,将无法加载,除非您启用CONFIG_MODVERSIONS在内核中。在本指南的后面部分,我们将介绍模块版本控制。在我们介绍 modversions 之前,如果您运行的内核启用了 modversioning,本指南中的示例可能无法工作。但是,大多数标准的 Linux 发行版内核都默认启用了它。如果您因版本控制错误而无法加载模块,请编译一个禁用 modversioning 的内核。
强烈建议您键入、编译和加载本指南讨论的所有示例。还强烈建议您从控制台执行此操作。您不应该在 X 环境中进行这些操作。
模块不能像printf()那样打印到屏幕上,但它们可以记录信息和警告,这些信息和警告最终会打印在您的屏幕上,但仅在控制台上。如果您从 xterm 中 insmod 一个模块,信息和警告将被记录,但仅记录到您的日志文件中。除非您查看日志文件,否则您不会看到它。为了立即访问此信息,请从控制台完成所有工作。
通常,Linux 发行版会分发以各种非标准方式修补过的内核源代码,这可能会导致问题。
更常见的问题是,一些 Linux 发行版分发不完整的内核头文件。您需要使用 Linux 内核中的各种头文件来编译您的代码。“墨菲定律”指出,缺少的头文件正是您模块工作所需的头文件。
为了避免这两个问题,我强烈建议您下载、编译并引导到一个全新的、标准的 Linux 内核,该内核可以从任何 Linux 内核镜像站点下载。有关更多详细信息,请参阅 Linux 内核 HOWTO。
具有讽刺意味的是,这也可能导致问题。默认情况下,系统上的 gcc 可能会在其默认位置查找内核头文件,而不是您安装新内核副本的位置(通常在/usr/src/中)。这可以通过使用 gcc 的-I开关来解决。
[1] | 在早期版本的 Linux 中,这被称为 kerneld。 |
[2] | 如果存在这样的文件。请注意,实际行为可能取决于发行版。如果您对细节感兴趣,请阅读 module-init-tools 附带的 man 手册,并亲自查看实际情况。您可以使用类似 strace modprobe dummy 的命令来找出 dummy.ko 是如何加载的。仅供参考:我在这里谈论的 dummy.ko 是主线内核的一部分,可以在网络部分找到。它需要编译为模块(当然还要安装)才能工作。 |
[3] | 如果您正在修改内核,为了避免覆盖现有模块,您可能需要使用内核 Makefile 中的EXTRAVERSION变量来创建一个单独的目录。 |