EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
以下内容仅作参考,能力有限,如有错误还请纠正。
# k" S, z; q0 X8 Y对于一个普通的linux设备驱动模块,以下是一个经典的makefile代码,使用下面这个makefile可以1 C. }; U7 u; X6 {8 n8 ]8 C3 j
完成大部分驱动的编译,使用时只需要修改一下要编译生成的驱动名称即可。只需修改obj-m的值。 ; W3 U" Q" |) N7 L, j; J
ifneq ($(KERNELRELEASE),)# e; R5 a/ o- R
obj-m:=hello.o
9 U6 F! O0 M% g' aelse' @; ~8 q5 N" b6 H% v$ r7 g2 E* L
#generate the path
/ \2 l4 _# [; |3 ACURRENT_PATH:=$(shell pwd)9 B6 ~4 {* U; c1 u; c" l) |
#the absolute path+ F9 a F8 W2 O/ y+ E+ ?
LINUX_KERNEL_PATH:=/lib/modules/$(shell uname -r)/build
7 I( O: Y& s+ e$ |8 _#complie object- v- n( {4 ]9 M- X$ I
default:
# u% R/ X* C7 b1 Omake -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules) s& [/ Q2 ~* c6 Y/ H
clean:( P& K7 k0 `3 \3 i3 _, f8 I
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean9 j; P/ D" o: H. K
endif , B& P, T/ ^) k7 n; F. o
说明:
- Q$ v! u( O) V当我们在模块的源代码目录下运行make时,make是怎么执行的呢?7 Q/ f* ^( \% |+ x- ?, n# l* e$ V( \
假设模块的源代码目录是/home/study/prog/mod/hello/下。+ O1 f. N7 ?# K0 F
先说明以下makefile中一些变量意义:) i4 \ _) }. b
(1)KERNELRELEASE在linux内核源代码中的顶层makefile中有定义
- m' v) x; h$ |5 d/ L3 ~(2)shell pwd会取得当前工作路径
" f) s8 M% @5 Y( ]0 R9 z(3)shell uname -r会取得当前内核的版本号
& ]3 k, v1 Q5 [- ~, b0 h2 p(4)LINUX_KERNEL_PATH变量便是当前内核的源代码目录。
# ^- {. b% E% s6 J3 n+ m5 d关于linux源码的目录有两个,分别为"/lib/modules/$(shell uname -r)/build"和"/usr/src/linux-header-$(shell uname -r)/",- g2 q$ ]4 K, N/ K3 }$ @ K+ m1 d
但如果编译过内核就会知道,usr目录下那个源代码一般是我们自己下载后解压的,而lib目录下的则是在编译时自动copy过去的,# ?% ]% T" X1 Q+ v: d, c: ~0 @
两者的文件结构完全一样,因此有时也将内核源码目录设置成/usr/src/linux-header-$(shell uname -r)/。关于内核源码目录, |5 f2 W' `, n2 _
可以根据自己的存放位置进行修改。9 a; v. l3 N! c
(5)make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules3 U. k- E) C! F Y
这就是编译模块了:首先改变目录到-C选项指定的位置(即内核源代码目录),其中保存有内核的顶层makefile;
4 C M1 l( s8 b/ D6 p+ \ [M=选项让该makefile在构造modules目标之前返回到模块源代码目录;然后,modueles目标指向obj-m变量中设定的模块;
% [5 K1 O: T P ?! ]6 U2 ]+ p( v在上面的例子中,我们将该变量设置成了hello.o。 按照顺序分析以下make的执行步骤:: {; X0 u5 i% X0 Y& d8 H2 N- U/ B# g
在模块的源代码目录下执行make,此时,宏“KERNELRELEASE”没有定义,因此进入else。7 ?" v% Y% H$ g0 a
由于make 后面没有目标,所以make会在Makefile中的第一个不是以.开头的目标作为默认的目标执行。
( @) i) m: E9 i+ B4 \于是default成为make的目标。0 X; s F: c9 T. ^8 I% I$ y* z
make会执行 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules ,假设当前内核版本是2.6.13-study,
: v" `5 l, [. @0 `: s# \5 _/ z所以$(shell uname -r)的结果是 2.6.13-study ,这里实际运行的是
4 u( W4 z, g4 e& P7 B7 g4 \1 Omake -C /lib/modules/2.6.13-study/build M=/home/study/prog/mod/hello/ modules
4 o0 C ^6 g5 Q2 @( ^1 C, s5 w# ?-C 表示到存放内核的目录执行其makefile,在执行过程中会定义KERNELRELEASE,+ W% s ^! e7 G( s
然后M=$(CURDIR)表示返回到当前目录,再次执行makefile,modules表示编译成模块的意思。
% b2 {/ M( P* T- ~. ]3 e( w而此时KERNELRELEASE已定义,则会执行obj-m += hello.o,表示会将hello_world.o目标编译成.ko模块。 r& h) I, f7 f2 {( C
若有多个源文件,则采用如下方法:
5 ^$ ], g, b6 M1 Vobj-m := hello.o
2 _2 N# i8 Q% h+ a3 _) Ihello-objs := file1.o file2.o file3.o2 k/ ^+ S1 w0 k7 e$ o
关于make modules的更详细的过程可以在内核源码目录下的scripts/Makefile.modpost文件的注释 中找到。
( ]3 Q' m4 X0 p& z如果把hello模块移动到内核源代码中。例如放到/usr/src/linux/driver/中, KERNELRELEASE就有定义了。
+ t) ]3 _6 t: Y; v, H: y在/usr/src/linux/Makefile中有KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)$(LOCALVERSION)。* Z* G1 z2 \9 o
这时候,hello模块也不再是单独用make编译,而是在内核中用make modules进行编译,此时驱动模块便和内核编译在一起。
4 g4 ?9 k7 k# A( I--------------------- --------------------------------------------------------------------------------
make -C $(KDIR) M=$(PWD) modules //执行的命令,该命令是make modules命令的扩展,-C选项的作用是指将当前的工作目录转移到指定的目录,即(KDIR)目录,程序到(pwd)当前目录查找模块源码,将其编译,生成.ko文件。 ===================================================================================== modules:; n, ]3 m- z8 M. A
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules 这句是Makefile的规则:这里的$(MAKE)就相当于make,-C 选项的作用是指将当前工作目录转移到你所指定的位置。“M=”选项的作用是,当用户需要以某个内核为基础编译一个外部模块的话,需要在make modules 命令中加入“M=dir”,程序会自动到你所指定的dir目录中查找模块源码,将其编译,生成KO文件。
, p6 c5 _" Q+ f
+ j5 o; v7 s3 m$ C3 B$ v+ f---------------------------------------------------我是分割线------------------------------------------------------------
7 H: Z% K4 g% J/ u+ F( }2 U3 @0 O新的内核模块编程中的make命令里有个M选项,如下: 7 R/ z! _1 u! T
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
- e- w# ^- k! ~2 KM=$(PWD) 意思是返回到当前目录继续读入、执行当前的Makefile。 ) f I% k8 }/ X
请参考:
. A8 [/ P! C9 p9 l/ N# U$ }5 Q从 2.4 到 2.6:Linux 内核可装载模块机制的改变对设备驱动的影响) i2 y5 {# v v. _
2 ]) d5 e5 U' s
这个M是kbuild的东西呢,还是make本来自己就有的东西呢? 9 `7 o4 o5 ~( [6 ~6 }- I
按理说,它是make的一个参数,应该是make的东西,但是make的doc里又找不到,
+ R0 r" B# M$ }1 R如果是kbuild里的东西,它应该怎样来实现呢?经查证这个M是内核根目录下的Makefile中使用的变量。
_: R( O, \& w N( ?" F) S {M是makefile脚本中的一个变量(variable) & Z' f% S0 F) z* d3 K& z5 l" X
" N; O" ]: Y* T4 m# Use make M=dir to specify directory of external module to build8 u V6 q/ I) D. a- `1 ]
# Old syntax make ... SUBDIRS=$PWD is still supported
; Y4 j4 p5 @2 j4 W& K# Setting the environment variable KBUILD_EXTMOD take precedence7 K; a B# W' w$ r( n
ifdef SUBDIRS" Y) N" w" M- X* \
KBUILD_EXTMOD ?= $(SUBDIRS)
) x, ~/ ^/ ?& o& B& X% [+ t/ M6 Sendif) F! w8 z9 }9 [8 ?4 Y% C
ifdef M //如果没有定义或赋值M,此处M未定义(undefined)+ l2 i2 t; R9 B" E
6 T+ s6 m, K' J* h$ Qifeq ("$(origin M)", "command line") //如果定义了,此句用来判断M是否从命令行来# s: H. Y9 C* Z) U$ m) m+ @; T
) H2 r$ l! {* C/ F, w8 ]KBUILD_EXTMOD := $(M)( G- h0 l1 d1 j. r @- s# o3 ^
endif
2 P; z) E2 \" C% @) Aendif
| + W L4 J$ j$ Y3 \- z' T8 Y% z
, K* S( I9 ~1 Z% F7 I P- ^8 e
, r+ [- ]# b+ [! U9 G: @9 B- i4 q. s7 q! o. T: Z1 B* K- D3 P
* ] H2 F8 d, _. g2 n
以下是来自:从 2.4 到 2.6:Linux 内核可装载模块机制的改变对设备驱动的影响
) d9 L# _ E' \0 ^2 c4 p清单3:2.6 内核模块的Makefile模板 | # Makefile2.6 ifneq ($(KERNELRELEASE),) #kbuild syntax. dependency relationshsip of files and target modules are listed here. mymodule-objs := file1.o file2.o obj-m := mymodule.o else PWD := $(shell pwd) KVER ?= $(shell uname -r) KDIR := /lib/modules/$(KVER)/build all: $(MAKE) -C $(KDIR) M=$(PWD) clean: rm -RF .*.cmd *.o *.mod.c *.ko .tmp_versions endif |
6 n1 g6 o) H Q+ }3 M9 ^
) U$ ~' b5 j8 d) DKERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量,在第一次读取执行此Makefile时,KERNELRELEASE没有被定义, 所以make将读取执行else之后的内容。如果make的目标是clean,直接执行clean操作,然后结束。当make的目标为all时,-C $(KDIR) 指明跳转到内核源码目录下读取那里的Makefile;M=$(PWD) 表明然后返回到当前目录继续读入、执行当前的Makefile。当从内核源码目录返回时,KERNELRELEASE已被被定义,kbuild也被启动去解析kbuild语法的语句,make将继续读取else之前的内容。else之前的内容为kbuild语法的语句, 指明模块源码中各文件的依赖关系,以及要生成的目标模块名。mymodule-objs := file1.o file2.o表示mymoudule.o 由file1.o与file2.o 连接生成。obj-m := mymodule.o表示编译连接后将生成mymodule.o模块。 补充一点,"$(MAKE) -C $(KDIR) M=$(PWD)"与"$(MAKE) -C $(KDIR) SUBDIRS =$(PWD)"的作用是等效的,后者是较老的使用方法。推荐使用M而不是SUBDIRS,前者更明确。 通过以上比较可以看到,从Makefile编写来看,在2.6内核下,内核模块编译不必定义复杂的CFLAGS,而且模块中各文件依赖关系的表示简洁清晰。 - B0 u! ]( Z$ v1 c8 u6 g
|