内核模块的makefile规则详解
这里主要对内核源码各子集目录中的Kbulid(内核的编译系统)Makfile进行简单介绍 这部分式内核驱动或设备驱动开发这最长接触到的
Makefile 的语法包括几个方面
1、 编译进内核的模块
如果需要将一个模块配置进内核,需要makefile中进行配置:
obj-y +=foo.o
2、编译可加载的模块
所有在配置文件中标记为-m的模块将被编译成可加载模块.ko文件。
如果需要将一个模块配置为可加载模块,需要在makefile中进行配置:
obj-m +=foo.o
3、模块编译以来多个文件
通常的,驱动开发这也会将单独编译在即开发的驱动模块中,当一个驱动模块依赖多个源文件时,需要通过一下方式来指定依赖文件:
obj-m +=foo.o
foo-y :=a.o b.o c.o
foo.o 由a.o,b.o,c.o 生成,然后调用$(LD) -r 将a.o b.o c.o 连接成foo.o文件
4、编译选项
在内核态,编译的选项由EXTRA_CFLAGS, EXTRA_AFLAGS和 EXTRA_LDFLAGS修改成了ccflags-y asflags-y和ldflags-y. ccflags-y asflags-y和ldflags-y这三个变量的值分别对应编译、汇编、链接时的参数。
5、最简单的Makefile
obj-m +=hello.o
DIR = 内核所在的目录/build/
all:
make -C $(DIR) M=`PWD` modules
clean:
make -C $(DIR) M=`PWD` clean
6、 := 、?= 、 += 、=区别
1 ifdef DEFINE_VRE
2 VRE = "hello world!"
3 else
4 endif
5
6 ifeq ($(OPT),define)
7 VRE ?="hello world! First!"
8 endif
9
10 ifeq ($(OPT),add)
11 VRE += "KELLY!"
12 endif
13
14 ifeq ($(OPT),recover)
15 VRE := "hello world! Again"
16 endif
17
18 all:
19 @echo $(VRE)
敲入以下make命令:
make DEFINE_VRE=true OPT=define 输出:Hello World!
make DEFINE_VRE=true OPT=add 输出:Hello World! Kelly!
make DEFINE_VRE=true OPT=recover 输出:Hello World! Again!
make DEFINE_VRE= OPT=define 输出:Hello World! First!
make DEFINE_VRE= OPT=add 输出:Kelly!
make DEFINE_VRE= OPT=recover 输出:Hello World! Again!
总结:
= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值
7、同时编译多个可加载模块
当一个.o目标文件依赖多个源文件时,就可以这样指定
obj-m +=hello.o
hello-y :=a.o b.o hello_world.o
或者
obj-m +=hello.o
hello-objs :=a.o b.o hello_world.o
hello.o目标文件依赖于a.o,b.o,hello_world.o,那么这里的a.o和b.o如果没有指定源文件,根据推导规则就是依赖源文件a.c,b.c,hello_world.c.除了hello-y,同时也可以用hello-objs,实现效果是一样的。
同时编译多个可加载模块 kbuild支持同时编译多个可加载模块,也就是生成多个.ko文件,它的格式是这样的:
obj-m := foo.o bar.o
foo-y := <foo_srcs>
bar-y := <bar_srcs>
8、ifneq ($(KERNELRELEASE))
通常标准的Makefile 会写成这样:
ifneq ($(KERNELRELEASE)) #中间有空格
obj-m := hello.o
else
KDIR ?=/lib/modules/`uname -r`/build
all:
MAKE -C $(KDIR) M=`PWD` modules
clean:
MAKE -C $(KDIR) M=`PWD` clean
endif
为什么要添加一个ifneq,else,all条件判断。
这得从linux内核模块make执行的过程说起:当键入make时,make在当前目录下寻找makefile并执行,KERNELRELEASE在顶层的makefile中被定义,
所以在执行当前makefile时KERNELRELEASE并没有被定义,走else分支,直接执行
MAKE -C $(KDIR) M=$(PWD) modules
而这条指令会进入到$(KDIR)目录,调用顶层的makefile,在顶层makefile中定义了KERNELRELEASE变量.
在顶层makefile中会递归地再次调用到当前目录下的makefile文件,这时KERNELRELEASE变量已经非空,所以执行if分支,
在可加载模块编译列表添加hello模块,由此将模块编译成可加载模块放在当前目录下。
归根结底,各级子目录中的makefile文件的作用就是先切换到顶层makefile,然后通过obj-m在可加载模块编译列表中添加当前模块,
kbuild就会将其编译成可加载模块。如果是直接编译整个内核源码,就省去了else分支中进入顶层makefile的步骤。
需要注意的一个基本概念是:每一次编译,顶层makefile都试图递归地进入每个子目录调用子目录的makefile,
只是当目标子目录中没有任何修改时,默认不再进行重复编译以节省编译时间。