通过hello world介绍2.6内核模块编译的最基本原理 .

来源:互联网 发布:c语言打印字母图形 编辑:程序博客网 时间:2024/05/01 13:58

1、makefile的预备知识

本文旨在介绍编译模块的原理,不详细介绍makefile。

下面是一个简单的没有任何用途的makefile:

1 MAKE_TEST = make test
2
3 all:
4     @echo "make all"
5     @echo "MAKE_TEST = $(MAKE_TEST)"
6
7 test:
8     @echo "make test"
9     @echo "MAKE_TEST = $(MAKE_TEST)"

本Makefile首先定义了一个变量MAKE_TEST,然后有两个目标:all和test

下面是分别执行make和make test的运行结果:

wangjiankun@driver:~/make_study$ make
make all
MAKE_TEST = make test
wangjiankun@driver:~/make_study$ make test
make test
MAKE_TEST = make test
wangjiankun@driver:~/make_study$

make test的运行结果说明:虽然指定了目标test,但是目标test之前定义的变量仍然是起作用的,这是介绍这个例子的唯一目的,用来解释下面的变量KERNELRELEASE在目标modules之前定义。

2、hello world的makefile文件

1 ifneq ($(KERNELRELEASE),)
2     obj-m := hello_world.o
3
4 else
5     KERNELDIR ?= /lib/modules/2.6.19/build
6     PWD := $(shellpwd)
7 default:
8     $(MAKE) -C $(KERNELDIR)M=$(PWD) modules
9 endif

上面是hello world模块的Makefile文件,当在当前目录下执行make命令时,由于变量KERNELRELEASE没有定义,所以make走else分支;

?=操作符make手册是如下解释的:

This statement:

FOO ?= bar

is exactly equivalent to this

ifeq ($(origin FOO), undefined)

FOO = bar

endif

Note that a variable set to an empty value is still defined, so ‘?=’ will not set that variable.

也就是说,第5行只有变量KERNELDIR没有被定义时,才给它赋值,所以此时给KERNELDIR赋值为:/lib/modules/2.6.19/build;

第7行,make遇到了第一个目标:default,执行第8行的命令:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules。第8行中有如下3个介绍点:(1)、-C选项是进目录的意思;(2)、定义了变量M,对于make来说,M就是一个变量,除此之外没有任何特殊的含义,make更不会因为他是module的首字母而认为是在编译模块;(3)、第8行的编译目标是modules。这样make带着两个信息(一个是变量M及其值,另一个是目标是modules)进入由变量KERNELDIR定义的目录来编译,在此是进入目录:/lib/modules/2.6.19/build。如下所示:

wangjiankun@driver:/lib/modules/2.6.19$ pwd
/lib/modules/2.6.19
wangjiankun@driver:/lib/modules/2.6.19$ ls -l
total 1313
lrwxrwxrwx 1 root root     37 Dec 14 16:18 build -> /home/wangjiankun/kernel/linux-2.6.19
drwxr-xr-x 9 root root   1024 Dec 14 16:18 kernel
-rw-r--r-- 1 root root 296327 Dec 14 16:18 modules.alias
-rw-r--r-- 1 root root     69 Dec 14 16:18 modules.ccwmap
-rw-r--r-- 1 root root 299229 Dec 14 16:18 modules.dep
-rw-r--r-- 1 root root    813 Dec 14 16:18 modules.ieee1394map
-rw-r--r-- 1 root root    730 Dec 14 16:18 modules.inputmap
-rw-r--r-- 1 root root  21916 Dec 14 16:18 modules.isapnpmap
-rw-r--r-- 1 root root     74 Dec 14 16:18 modules.ofmap
-rw-r--r-- 1 root root 226143 Dec 14 16:18 modules.pcimap
-rw-r--r-- 1 root root   1135 Dec 14 16:18 modules.seriomap
-rw-r--r-- 1 root root 135623 Dec 14 16:18 modules.symbols
-rw-r--r-- 1 root root 342460 Dec 14 16:18 modules.usbmap
lrwxrwxrwx 1 root root     37 Dec 14 16:18 source -> /home/wangjiankun/kernel/linux-2.6.19
wangjiankun@driver:/lib/modules/2.6.19$

这个目录是在debian下编译内核时生成的,/lib/modules/2.6.19/build指向内核源码包。这样make就要运行内核源码包的主Makefile文件了。

3、linux 2.6内核主Makefile文件对编译模块的支持

由上面的介绍可知:make进入内核源码包带着两个信息:(1)定义了变量M(= 模块的当前目录路径)和(2)目标是modules来执行make命令。先看一下modules目标。

内核主Makefile定义了目标modules:

1202 module-dirs := $(addprefix _module_,$(KBUILD_EXTMOD))
1203 PHONY += $(module-dirs) modules
1204 $(module-dirs): crmodverdir $(objtree)/Module.symvers
1205     $(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@)
1206
1207 modules: $(module-dirs)
1208     @echo '  Building modules, stage 2.';
1209     @echo "wangjiankun noted 1"
1210     $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost

modules目标依赖于由变量module-dirs定义的目标,所以要先编译由变量module-dirs定义的目标,1202行定义了变量module-dirs( := $(addprefix _module_,$(KBUILD_EXTMOD)) ),内核主Makefile有如下定义:

63 # Use make M=dir to specify directory of external module to build
64 # Old syntax make ... SUBDIRS=$PWD is still supported
65 # Setting the environment variable KBUILD_EXTMOD take precedence
66 ifdef SUBDIRS
67   KBUILD_EXTMOD ?= $(SUBDIRS)
68 endif
69 ifdef M
70   ifeq ("$(origin M)", "command line")
71     KBUILD_EXTMOD := $(M)
72   endif
73 endif

由以上定义可知变量KBUILD_EXTMOD就是我们在模块的Makefile中定义的变量M的值,即模块的当前目录。1202行用到了make的一个函数addprefix,addprefix在make手册中如下解释:

$(addprefix prefix,names...)
The argument names is regarded as a series of names, separated by whitespace;
prefix is used as a unit. The value of prefix is prepended to the front of each
individual name and the resulting larger names are concatenated with single
spaces between them. For example,
$(addprefix src/,foo bar)
produces the result ‘src/foo src/bar’.

这样变量module-dirs就等于_module_模块的当前目录路径,在我的测试系统中的值为:_module_/home/wangjiankun/ldd3/ch2

1204行目标:$(module-dirs)依赖于crmodverdir和$(objtree)/Module.symvers

内核主Makefile文件定义了目标:crmodverdir如下所示,其实就是在模块的当前目录下建立一个文件夹.tmp_versions,并将其内部文件全部删除

1190 crmodverdir:
1191     $(Q)mkdir -p $(MODVERDIR)
1192     $(Q)rm -f $(MODVERDIR)/*

内核主Makefile文件定义了目标:$(objtree)/Module.symvers如下所示,其实就是检查内核源码根目录下是否存在文件Module.symvers,如果不存在打印出警告信息,但是模块仍然可以成功编译。

1195 $(objtree)/Module.symvers:
1196     @test -e $(objtree)/Module.symvers || ( /
1197     echo; /
1198     echo "  WARNING: Symbol version dump $(objtree)/Module.symvers"; /
1199     echo "           is missing; modules will have no dependencies and modversions."; /
1200     echo )

1205行的$(patsubst _module_%,%,$@)就是变量module-dirs的值:_module_/home/wangjiankun/ldd3/ch2除去_module_的结果,即:/home/wangjiankun/ldd3/ch2。表达式中的$@意为:The file name of the target.

$(build)的值为:-f scripts/Makefile.build obj(没有找到在什么地方定义,待查,此处是在我的测试系统上,通过echo打印出来的),这样1205行的实际语句为:

@make -f scripts/Makefile.build obj=/home/wangjiankun/ldd3/ch2

scripts/Makefile.build文件比较复杂没有详细研究,但有一点是肯定的,make二次执行模块目录下的Makefile文件就是通过这个脚本直接或间接进行的(目前猜测,可能不是二次执行了模块的Makefile文件,而是将模块的Makefile文件包含到了内核的build系统的脚本或Makefile中使得obj-m := hello_world.o起作用而编译了模块)。通过这个脚本,make二次执行模块的Makefile文件,此时变量KERNELRELEASE已经定义,内核主Makefile有如下定义:

320 # Read KERNELRELEASE from include/config/kernel.release (if it exists)
321 KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)

include/config/kernel.release文件内容如下:

wangjiankun@driver:~/kernel/linux-2.6.19/include/config$ pwd
/home/wangjiankun/kernel/linux-2.6.19/include/config
wangjiankun@driver:~/kernel/linux-2.6.19/include/config$ cat kernel.release
2.6.19
wangjiankun@driver:~/kernel/linux-2.6.19/include/config$

也就是说,此时KERNELRELEASE不但定义了,而且值为:内核版本号,所以make走第2行的分支并通过隐含规则将hello_world.c编译成hello_world.o文件。

make回到1208行打印出:Building modules, stage 2.,并通过1210行生成模块。

 

转自:http://blog.csdn.net/jiankun_wang/article/details/5058794