编译内核模块

内核模块需要使用特定的 gcc 选项进行编译才能正常工作。此外,它们还需要使用某些定义的符号进行编译。这是因为内核头文件需要根据我们编译的是内核模块还是可执行文件而表现不同。您可以使用 gcc 的-D选项,或使用#define预处理器命令来定义符号。在本节中,我们将介绍编译内核模块需要执行的操作。

我们使用 gcc 的-isystem选项而不是-I,因为它告诉 gcc 抑制一些 “未使用变量” 警告,这些警告在您包含-W -Wallcauses when you includemodule.h时会发生。 通过在 gcc-3.0 下使用-isystem,内核头文件会被特殊处理,并且警告会被抑制。 如果您改为使用-I(甚至-isystem在 gcc 2.9x 下),“未使用变量” 警告将被打印。如果它们出现,请忽略它们。

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

的模块。例 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中。 继续 cat 该文件以查看您的模块是否真的是内核的一部分。 恭喜您,您现在是 Linux 内核代码的作者了! 当新鲜感消失后,使用 rmmod hello-1 从内核中删除您的模块。 查看一下/var/log/messages,看看它是否已记录到您的系统日志文件中。

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