2.2. 编译内核模块

内核模块在用gcc编译时需要使用特定的参数。另外,一些宏同样需要定义。 这是因为在编译成可执行文件和内核模块时,内核头文件起的作用是不同的。 你可以用gcc的 -D 选项定义宏,或使用 #define 预处理命令。我们会在这章详细讨论这方面的内容来成功的编译我们的内核模块。

我们使用gcc的 -isystem 选项而不是 -I 选项因为要使gcc省略"unused variable"警告当同时打开 -W -Wall 选项和包含时 module.h头文件时。 在gcc-3.0使用选项 -isystem 会让编译器特殊对待内核头文件,所以会省略"unused variable"警告。如果你使用 -I 选项(或在 gcc 2.9x使用 -isystem 选项时), 将会输出"unused variable" 警告。只管忽略就行了。

再来看一个简单的用来编译模块 hello-1.c的Makefile:

Example 2-2. 用来编译一个基本的模块的Makefile

TARGET  := hello-1
WARN    := -W -Wall -Wstrict-prototypes -Wmissing-prototypes
INCLUDE := -isystem /lib/modules/`uname -r`/build/include
CFLAGS  := -O2 -DMODULE -D__KERNEL__ ${WARN} ${INCLUDE}
CC      := gcc-3.0
	
${TARGET}.o: ${TARGET}.c

.PHONY: clean

clean:
    rm -rf {TARGET}.o

作为留给读者的一个练习,编译你的 hello-1.c 并且使用命令 insmod ./hello-1.o 加载入内核。(忽略任何关于污染内核的信息,我们将不久讨论相关内容)。 顺利吗?所有加载入内核的模块都会在文件 /proc/modules列出。去那看看你的模块是否真的是内核的一部分了。 祝贺你!你现在已经是内核模块的作者了。当你的新鲜劲过去后,使用命令 rmmod hello-1卸载模块。再看一下 /var/log/messages 文件的内容是否有相关的日志内容。

这儿是另一个练习。看到了在声明 init_module()上的注释吗? 改变返回值非零,重新编译再加载,发生了什么?