一个简单驱动的makefile文件
#mydev_makefile
ifneq ($(KERNELRELEASE),)
obj-m:=mydev.o
else
KERNELDIR:=/lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *.mod.c *.mod.o *.koendif
-
obj-m :这个变量是指定你要声称哪些模块,模块的格式为 obj-m := <模块名>.o 。上面代码表示把文件mydev.o作为"模块"进行编译,不会编译到内核,但是会生成一个独立的 "mydev.ko" 文件;
-
obj-y:表示把test.o文件编译进内核;
-
KERNELDIR:内核库文件的路径,代码中使用的是内核提供的函数。而这些函数也是有具体实现的,在连接成一个内核模块时要说明这些库文件在哪里,方便链接程序把它们连接成一个完整的模块。
-
KERNELDIR=/lib/modules/$(shell uname -r)/build;其中shell uname -r说的是调用shell里头的uname指令,你可以uname -r看看,他表示的是内核版本号,一般来说我们构造内核树时,它把内核库统一保存在/lib/modules/内核版本号/build目录下。 -
PWD :这是当前工作路径。将
shell pwd作为变量,通过$(shell pwd)形式引用这个变量 -
M= :指定我们源文件的位置
当我们在写完一个模块的Makefile之后,只需要在当前目录下进行make即可。
make时,由于make后面没有目标,所以make会将default作为默认的目标文件进执行。make会执行 \$(MAKE) -C $( KERNELDIR) M= \$(PWD) modules。
-C $(KERNELDIR):指定进入指定的目录即KERNELDIR,是内核源代码目录,调用该目录顶层下的Makefile;M= $(PWD):通过shell命令(pwd命令)获取当前目录,而M不是makefile的选项,是内核根目录下的Makefile中使用的变量。作用是让该Makefile在构造modules目标之前返回到模块源代码目录并在当前目录生成obj-m指定的xxx.o目标模块
make的过程
第一次make的时候,KERNELRELEASE未定义,所以走else的分支,进入kernel目录去编译;进入kernel目录编译的时候会根据M= \$ (PWD) 去$M目录去编译,也就是你make时所在的目录;这时,KERNELRELEASE是被定义过的,所以第二次会走obj-m:=hello.o分支,根据kernel的makefile中指定的规则,会把当前目录下的hello.c编译成hello.o然后生成对应的hello.ko,也就是内核模块。
扩展
$(MAKE) -C $(KERNELDIR) M=\$(PWD) modules #ARCH=riscv CROSS_COMPILE=riscv64-buildroot-linux-gnu-
- arch: 即architecture,就是选择编译哪一种cpu,也就是编译arch/目录下的哪一个子目录。如指定make ARCH=arm就是编译arch/arm下的代码。如果不指定,make将使用本机(用什么机器编译就是什么)的cpu作为缺省ARCH。注意:arch/arm下不但有arm体系架构特有的代码,还有arm特有的kconfig,也就是配置选项,所以在make menuconfig,
make xxxx\_defconfig的时候也必须指定ARCH=arm。 - CROSS_COMPILE:即交叉编译器的前缀(prefix),也就是选择将代码编译成目标cpu的指令的工具,如指定make CROSS_COMPILE=arm-none-linux-gnueabi-就是使用arm-none-linux-gnueabi-gcc, arm-none-linux-gnueabi-ld等工具将代码编译成arm的可执行指令。如果不指定CROSS_COMPILE参数,make时将认为prefix为空,即使用gcc来编译。这里cross_compile的设置,是假定所用的交叉工具链的gcc程序名称为arm-linux-gcc。如果实际使用的gcc名称是some-thing-else-gcc,则这里照葫芦画瓢填some-thing-else-即可。总之,要省去名称中最后的gcc那3个字母。