Compiling and Loading
The "hello world" example at the beginning of this chapter included a brief demonstration of building a module and loading it into the system. There is, of course, a lot more to that whole process than we have seen so far. This section provides more detail on how a module author turns source code into an executing subsystem within the kernel. 本章开头的“hello world”示例包含了构建模块并将其加载到系统中的简要演示。 当然,整个过程比我们迄今为止看到的要多得多。 本节提供了有关模块作者如何将源代码转换为内核中的执行子系统的更多详细信息。
Compiling Modules
As the first step, we need to look a bit at how modules must be built. The build process for modules differs significantly from that used for user-space applications; the kernel is a large, standalone program with detailed and explicit requirements on how its pieces are put together. The build process also differs from how things were done with previous versions of the kernel; the new build system is simpler to use and produces more correct results, but it looks very different from what came before. The kernel build system is a complex beast, and we just look at a tiny piece of it. The files found in the Documentation/kbuild directory in the kernel source are required reading for anybody wanting to understand all that is really going on beneath the surface. 作为第一步,我们需要看一下必须如何构建模块。 模块的构建过程与用户空间应用程序的构建过程有很大不同; 内核是一个大型的独立程序,对如何将其组合在一起有详细而明确的要求。 构建过程也不同于以前版本的内核。 新的构建系统更易于使用并产生更正确的结果,但它看起来与以前的不同。 内核构建系统是一个复杂的野兽,我们只看它的一小部分。 任何想要了解表面之下真正发生的一切的人都需要阅读内核源代码的 Documentation/kbuild 目录中的文件。
There are some prerequisites that you must get out of the way before you can build kernel modules. The first is to ensure that you have sufficiently current versions of the compiler, module utilities, and other necessary tools. The file Documentation/Changes in the kernel documentation directory always lists the required tool versions; you should consult it before going any further. Trying to build a kernel (and its modules) with the wrong tool versions can lead to no end of subtle, difficult problems. Note that, occasionally, a version of the compiler that is too new can be just as problematic as one that is too old; the kernel source makes a great many assumptions about the compiler, and new releases can sometimes break things for a while. 在构建内核模块之前,您必须先解决一些先决条件。 首先是确保您拥有足够最新版本的编译器、模块实用程序和其他必要工具。 内核文档目录中的 Documentation/Changes 文件总是列出所需的工具版本; 在继续之前,您应该咨询它。 尝试使用错误的工具版本构建内核(及其模块)可能会导致无穷无尽的微妙、困难的问题。 请注意,有时,太新的编译器版本可能与太旧的编译器版本一样有问题。 内核源代码对编译器做了很多假设,新版本有时可能会破坏一段时间。
If you still do not have a kernel tree handy, or have not yet configured and built that kernel, now is the time to go do it. You cannot build loadable modules for a 2.6 kernel without this tree on your filesystem. It is also helpful (though not required) to be actually running the kernel that you are building for. 如果您仍然没有手边的内核树,或者尚未配置和构建该内核,那么现在是时候去做了。 如果您的文件系统上没有此树,您将无法为 2.6 内核构建可加载模块。 实际运行您正在构建的内核也很有帮助(尽管不是必需的)。
Once you have everything set up, creating a makefile for your module is straightforward. In fact, for the "hello world" example shown earlier in this chapter, a single line will suffice: 一旦你完成了所有设置,为你的模块创建一个 makefile 就很简单了。 事实上,对于本章前面显示的“hello world”示例,一行就足够了
obj-m := hello.o
Readers who are familiar with make, but not with the 2.6 kernel build system, are likely to be wondering how this makefile works. The above line is not how a traditional makefile looks, after all. The answer, of course, is that the kernel build system handles the rest. The assignment above (which takes advantage of the extended syntax provided by GNU make) states that there is one module to be built from the object file hello.o. The resulting module is named hello.ko after being built from the object file. 熟悉 make 但不熟悉 2.6 内核构建系统的读者可能想知道这个 makefile 是如何工作的。 毕竟,上面这行不是传统的 makefile 的样子。 答案当然是内核构建系统处理其余部分。 上面的赋值(它利用了 GNU make 提供的扩展语法)表明有一个模块要从目标文件 hello.o 构建。 从目标文件构建后,生成的模块名为 hello.ko。
If, instead, you have a module called module.ko that is generated from two source files (called, say, file1.c and file2.c), the correct incantation would be: 相反,如果您有一个名为 module.ko 的模块,它是从两个源文件(例如 file1.c 和 file2.c)生成的,那么正确的咒语应该是:
obj-m := module.o
module-objs := file1.o file2.o
For a makefile like those shown above to work, it must be invoked within the context of the larger kernel build system. If your kernel source tree is located in, say, your ~/kernel-2.6 directory, the make command required to build your module (typed in the directory containing the module source and makefile) would be: 要使上面显示的 makefile 正常工作,必须在更大的内核构建系统的上下文中调用它。 如果您的内核源代码树位于 ~/kernel-2.6 目录中,则构建模块所需的 make 命令(在包含模块源和 makefile 的目录中键入)将是:
make -C ~/kernel-2.6 M=`pwd` modules
This command starts by changing its directory to the one provided with the -C option (that is, your kernel source directory). There it finds the kernel's top-level makefile. The M= option causes that makefile to move back into your module source directory before trying to build the modules target. This target, in turn, refers to the list of modules found in the obj-m variable, which we've set to module.o in our examples. 该命令首先将其目录更改为 -C 选项提供的目录(即您的内核源目录)。 它在那里找到内核的顶级makefile。 M= 选项会导致 makefile 在尝试构建模块目标之前移回模块源目录。 反过来,这个目标是指在 obj-m 变量中找到的模块列表,我们在示例中将其设置为 module.o。
Typing the previous make command can get tiresome after a while, so the kernel developers have developed a sort of makefile idiom, which makes life easier for those building modules outside of the kernel tree. The trick is to write your makefile as follows: 键入之前的 make 命令可能会在一段时间后变得乏味,因此内核开发人员开发了一种 makefile 习惯用法,这使得那些在内核树之外构建模块的人的生活更轻松。 诀窍是按如下方式编写您的makefile:
# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
# Otherwise we were called directly from the command
# line; invoke the kernel build system.
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
Once again, we are seeing the extended GNU make syntax in action. This makefile is read twice on a typical build. When the makefile is invoked from the command line, it notices that the KERNELRELEASE variable has not been set. It locates the kernel source directory by taking advantage of the fact that the symbolic link build in the installed modules directory points back at the kernel build tree. If you are not actually running the kernel that you are building for, you can supply a KERNELDIR= option on the command line, set the KERNELDIR environment variable, or rewrite the line that sets KERNELDIR in the makefile. Once the kernel source tree has been found, the makefile invokes the default: target, which runs a second make command (parameterized in the makefile as $(MAKE)) to invoke the kernel build system as described previously. On the second reading, the makefile sets obj-m, and the kernel makefiles take care of actually building the module. 再一次,我们看到了扩展的 GNU make 语法在起作用。 这个 makefile 在典型的构建中被读取两次。 从命令行调用 makefile 时,它注意到 KERNELRELEASE 变量尚未设置。 它利用已安装模块目录中的符号链接构建指向内核构建树这一事实来定位内核源目录。 如果您实际上并没有运行您正在构建的内核,您可以在命令行上提供一个 KERNELDIR= 选项,设置 KERNELDIR 环境变量,或者重写在 makefile 中设置 KERNELDIR 的行。 一旦找到内核源代码树,makefile 调用默认值:target,它运行第二个 make 命令(在 makefile 中参数化为 $(MAKE))来调用内核构建系统,如前所述。 在第二次阅读中,makefile 设置了 obj-m,内核 makefile 负责实际构建模块。
This mechanism for building modules may strike you as a bit unwieldy and obscure. Once you get used to it, however, you will likely appreciate the capabilities that have been programmed into the kernel build system. Do note that the above is not a complete makefile; a real makefile includes the usual sort of targets for cleaning up unneeded files, installing modules, etc. See the makefiles in the example source directory for a complete example. 这种构建模块的机制可能会让你觉得有点笨拙和晦涩。 但是,一旦您习惯了它,您可能会喜欢已编程到内核构建系统中的功能。 请注意,以上不是完整的makefile; 真正的 makefile 包括用于清理不需要的文件、安装模块等的常用目标。有关完整示例,请参见示例源目录中的 makefile。