转:Linux 内核模块编译 Makefile 详解

一、模块的编译

       讲到驱动编译分为静态编译动态编译;静态编译即为将驱动直接编译进内核,动态编译即为将驱动编译成模块。

而动态编译又分为两种:

a -- 内部编译

       在内核源码目录内编译

b -- 外部编译

       在内核源码的目录外编译

.模块程序的编译   ----------------条件
    (1)需要内核源码
    (2)内核源码针对硬件平台配置过
    (3)内核源码正确地编译过

二、具体编译过程分析 

 对于一个普通的linux设备驱动模块,以下是一个经典的makefile代码,使用下面这个makefile可以完成大部分驱动的编译,使用时只需要修改一下要编译生成的驱动名称即可。只需修改obj-m的值。


ifneq  ($(KERNELRELEASE),)
obj-m:=hello.o
else
KDIR := /lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)
all:
    make -C $(KDIR) M=$(PWD) modules
clean:
    rm -f *.ko *.o *.symvers *.cmd *.cmd.o
endif
obj-m += add.o 
add-objs += add.o first.o

KERNEL_DIR=/home/hhch/6818GEC/kernel #内核源码的路径
CROSS_DIR=/home/hhch/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi- #用的交叉编译工具
all:
	make ARCH=arm  CROSS_COMPILE=$(CROSS_DIR)      -C   $(KERNEL_DIR)  M=`pwd`  modules
clean:
	make ARCH=arm  CROSS_COMPILE=$(CROSS_DIR)      -C   $(KERNEL_DIR)  M=`pwd`  modules clean

1、makefile 中的变量

    先说明以下makefile中一些变量意义:

(1)KERNELRELEASE            在linux内核源代码中的顶层makefile中有定义

(2)shell pwd                          取得当前工作路径    ---或     'pwd'

(3)shell uname -r                  取得当前内核的版本号

(4)KDIR                                  当前内核的源代码目录。

 ( 5 ) modueles                         目标指向obj-m变量中设定的模块

三、内部编译简单说明

        如果把hello模块移动到内核源代码中。例如放到/usr/src/linux/driver/中, KERNELRELEASE就有定义了。

     在/usr/src/linux/Makefile中有KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)$(LOCALVERSION)。

这时候,hello模块也不再是单独用make编译,而是在内核中用make modules进行编译,此时驱动模块便和内核编译在一起。

关于linux源码的目录有两个,分别为

 "/lib/modules/$(shell uname -r)/build"

"/usr/src/linux-header-$(shell uname -r)/"

       但如果编译过内核就会知道,usr目录下那个源代码一般是我们自己下载后解压的而lib目录下的则是在编译时自动copy过去的,两者的文件结构完全一样,因此有时也将内核源码目录设置成/usr/src/linux-header-$(shell uname -r)/。关于内核源码目录可以根据自己的存放位置进行修改。

(5)make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules

这就是编译模块了:

a -- 首先改变目录到-C选项指定的位置(即内核源代码目录),其中保存有内核的顶层makefile;

b -- M=选项让该makefile在构造modules目标之前返回到模块源代码目录;然后,modueles目标指向obj-m变量中设定的模块;在上面的例子中,我们将该变量设置成了hello.o。

2、make 的的执行步骤

a -- 第一次进来的时候,宏“KERNELRELEASE”未定义,因此进入 else;

b -- 记录内核路径,记录当前路径;

       由于make 后面没有目标,所以make会在Makefile中的第一个不是以.开头的目标作为默认的目标执行。默认执行all这个规则

c -- make -C $(KDIR) M=$(PWD) modules

    -C 进入到内核的目录执行Makefile ,在执行的时候KERNELRELEASE就会被赋值,M=$(PWD)表示返回当前目录,再次执行makefile,modules 编译成模块的意思

     所以这里实际运行的是

     make -C /lib/modules/2.6.13-study/build M=/home/fs/code/1/module/hello/ modules

d -- 再次执行该makefile,KERNELRELEASE就有值了,就会执行obj-m:=hello.o

     obj-m:表示把hello.o 和其他的目标文件链接成hello.ko模块文件,编译的时候还要先把hello.c编译成hello.o文件

可以看出make在这里一共调用了3次

   1)-- make
   2)-- linux内核源码树的顶层makedile调用,产生。o文件
   3)-- linux内核源码树makefile调用,把.o文件链接成ko文件

3、编译多文件

若有多个源文件,则采用如下方法:

obj-m := hello.o

hello-objs := file1.o file2.o file3.o

三、内部编译简单说明

        如果把hello模块移动到内核源代码中。例如放到/usr/src/linux/driver/中, KERNELRELEASE就有定义了。

     在/usr/src/linux/Makefile中有KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)$(LOCALVERSION)。

这时候,hello模块也不再是单独用make编译,而是在内核中用make modules进行编译,此时驱动模块便和内核编译在一起。

猜你喜欢

转载自blog.csdn.net/h490516509/article/details/88825795