2.2. 编译内核模块

内核模块的编译方式与常规用户空间应用程序略有不同。早期的内核版本要求我们关注这些通常存储在 Makefile 中的设置。尽管 Makefile 是分层组织的,但许多冗余设置累积在子级 Makefile 中,导致它们变得庞大且难以维护。幸运的是,现在有一种新的方法来处理这些事情,称为 kbuild,外部可加载模块的构建过程现在已完全集成到标准内核构建机制中。要了解更多关于如何编译不属于官方内核的模块(例如您在本指南中找到的所有示例),请参阅文件linux/Documentation/kbuild/modules.txt.

那么,让我们看一下一个简单的 Makefile,用于编译名为hello-1.c:

示例 2-2. 用于基本内核模块的 Makefile

obj-m += hello-1.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

从技术角度来看,只有第一行是真正必要的,“all”和“clean”目标是为了纯粹的方便而添加的。

现在,您可以通过发出命令 make 来编译模块。您应该获得类似于以下内容的输出

hostname:~/lkmpg-examples/02-HelloWorld# make
make -C /lib/modules/2.6.11/build M=/root/lkmpg-examples/02-HelloWorld modules
make[1]: Entering directory `/usr/src/linux-2.6.11'
  CC [M]  /root/lkmpg-examples/02-HelloWorld/hello-1.o
 Building modules, stage 2.
  MODPOST
  CC      /root/lkmpg-examples/02-HelloWorld/hello-1.mod.o
  LD [M]  /root/lkmpg-examples/02-HelloWorld/hello-1.ko
make[1]: Leaving directory `/usr/src/linux-2.6.11'
hostname:~/lkmpg-examples/02-HelloWorld#
	

请注意,内核 2.6 引入了一种新的文件命名约定:内核模块现在具有.ko扩展名(代替旧的.o扩展名),这很容易将它们与传统的对象文件区分开来。原因是它们包含一个额外的 .modinfo 节,其中保存了关于模块的附加信息。我们很快就会看到这些信息有什么用。

使用 modinfo hello-*.ko 来查看它是什么类型的信息。

hostname:~/lkmpg-examples/02-HelloWorld# modinfo hello-1.ko
filename:       hello-1.ko
vermagic:       2.6.11 preempt PENTIUMII 4KSTACKS gcc-3.3
depends:

到目前为止,没有什么特别的。但是,一旦我们在后面的示例之一中使用 modinfo,情况就会发生变化,hello-5.ko.

hostname:~/lkmpg-examples/02-HelloWorld# modinfo hello-5.ko
filename:       hello-5.ko
license:        GPL
author:         Peter Jay Salzman
vermagic:       2.6.11 preempt PENTIUMII 4KSTACKS gcc-3.3
depends:
parm:           myintArray:An array of integers (array of int)
parm:           mystring:A character string (charp)
parm:           mylong:A long integer (long)
parm:           myint:An integer (int)
parm:           myshort:A short integer (short)
hostname:~/lkmpg-examples/02-HelloWorld# 

这里有很多有用的信息可以查看。用于错误报告的作者字符串、许可证信息,甚至是对其接受的参数的简短描述。

关于内核模块 Makefile 的更多详细信息,请参阅linux/Documentation/kbuild/makefiles.txt。 在开始修改 Makefile 之前,请务必阅读此文件和相关文件。这可能会为您节省大量工作。

现在是时候使用 insmod ./hello-1.ko 将您新编译的模块插入到内核中了(忽略您看到的关于污点内核的任何内容;我们稍后会介绍)。

加载到内核中的所有模块都列在/proc/modules。 继续并 cat 该文件,以查看您的模块是否真的是内核的一部分。 恭喜,您现在是 Linux 内核代码的作者! 当新鲜感消失后,使用 rmmod hello-1 从内核中删除您的模块。 看一下/var/log/messages只是为了看看它是否已记录到您的系统日志文件中。

这是给读者的另一个练习。 看看init_module()中 return 语句上方的注释? 将返回值更改为负值,重新编译并再次加载模块。 会发生什么?