1.1 概述
2.6的Makefile的写法和应用相对于2.4有了一些变化,可能对于很多人来说,因为找不到相关的文档,都是模仿内核中已有的文件来写自己的Makefile。其实,在内核的Documentation
/ kbuild目录下面,还是有对内核Makefile语法的详细说明的。在这里就2.6内核中Makefile最常见的简单应用情况做一个翻译和归纳介绍。
不知道为什么
2.6内核的Makefile分为5个组成部分:
l 最顶层的Makefile
l 内核的.config配置文件
l 在arch/$(ARCH) 目录下的体系结构相关的Makefile
l 在scripts/ 目录下的 Makefile.* 文件,是一些Makefile的通用规则
l 各级目录下的大概约500个kbuild Makefile文件
顶层的Makefile文件读取 .config文件的内容,并总体上负责build内核和模块。Arch
Makefile则提供补充体系结构相关的信息。 Scripts目录下的Makefile文件包含了所有用来根据kbuild Makefile
构建内核所需的定义和规则。
1.2 Kbuild Makefile
对于Makefiles的不同组成部分,有一些不同的语法规则。针对的对象也不同,对于大部分内核模块或设备驱动的开发者和使用者来说,最常接触到的就是各层目录下基于kbuild架构的kbuild
Makefile文件。
Kbuild Makefile的语法结构非常简单,核心内容主要包括
1.2.1 目标定义
目标定义就是用来定义哪些内容要做为模块编译,哪些要编译链接进内核。
例如
obj-y += foo.o
表示要由foo.c或者foo.s文件编译得到foo.o并链接进内核,而obj-m则表示该文件要作为模块编译。 除了y,m以外的obj-x形式的目标都不会被编译。
而更常见的做法是根据.config文件的CONFIG_ 变量来决定文件的编译方式,如:
obj-$(CONFIG_ISDN) += isdn.o
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
除了obj-形式的目标以外,还有lib-y library库,hostprogs-y 主机程序等目标,但是基本都应用在特定的目录和场合下。
1.2.2 多文件模块的定义
最简单的kbuild Makefile如上一节一句话的形式就够了,如果一个模块由多个文件组成,那么稍微复杂一些,采用模块名加
–objs后缀或者 –y后缀的形式来定义模块的组成文件。如以下例子:
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
模块的名字为ext2,由balloc.o和bitmap.o两个目标文件最终链接生成ext2.o
直至ext2.ko文件,是否包括xattr.o取决于内核配置文件的配置情况。如果CONFIG_EXT2_FS的值是y也没有关系,在此过程中生成的
ext2.o将被链接进built-in.o最终链接进内核。这里需要注意的一点是,该kbuild
Makefile所在的目录中不应该再包含和模块名相同的源文件如ext2.c/ext2.s。
或者写成如-objs的形式:
obj-$(CONFIG_ISDN) += isdn.o
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
1.2.3 目录层次的迭代
如下例:
obj-$(CONFIG_EXT2_FS) += ext2/
如果CONFIG_EXT2_FS
的值为y或m,kbuild将会将ext2目录列入向下迭代的目标中,但是其作用也仅限于此,具体ext2目录下的文件是要作为模块编译还是链入内核,还是有ext2目录下的Makefile文件的内容来决定的。
1.2.4 模块的编译
编译模块的时候,你可以将模块放在代码树中,用Make modules的方式来编译你的模块,你也可以将模块相关文件目录放在代码树以外的位置,用如下命令来编译模块:
make -C path/to/kernel/src M=$PWD modules
-C指定代码树的位置,M=$PWD 或 M=`PWD` 告诉kbuild回到当前目录来执行build操作。
1.2.5 模块的安装
当你需要将模块安装到非默认位置的时候,你可以用INSTALL_MOD_PATH 指定一个前缀,如:
make INSTALL_MOD_PATH=/foo modules_install
模块将被安装到 /foo/lib/modules目录下。
static int hello_init(void)
{
printk("<1>Hello module init.\n");
return 0;
}
static void hello_exit(void)
{
printk("<1>Goodbye module exit.\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("lizeliang");
MODULE_DESCRIPTION("a simple module");
MODULE_ALIAS("hello");
编译过程首先会到内核源码目录下,读取顶层的Makefile文件,然后再返回模块源码所在目录。
<一>比较简单的写法,有人认为这不是正真的Makefile
lzel@lzel-laptop:~/works/kernel/DeviceDrivers/1/modules$echo obj -m
:=simple.o>Makefile //重定向显示内容到Makefile文件
lzel@lzel-laptop:~/works/kernel/DeviceDrivers/1/modules$chmod +x
Makefile //为什么要这样,我也没弄清楚,谁知道补充一下;其实在我的机子上不加这一句也是可以的
lzel@lzel-laptop:~/works/kernel/DeviceDrivers/1/modules$ make -C
/usr/src/linux-headers-2.6.24-15-generic M=$PWD modules //M=$PWD
M代表要编译的模块的绝对路径 #make -C linux-headers-`uname -r`/dir
表示make将调到/dir下去执行make
<二>对陈老师《Linux操作系统原理与应用》上2.4内核下的一点改进:
lzel@lzel-laptop:~/works/kernel/DeviceDrivers/1/modules$ cat Makefile
obj-m +=simple.o
all:
make -C /usr/src/linux-headers-`uname -r` M=`pwd` modules #编译
clean:
make -C /usr/src/linux-headers-`uname -r` M=`pwd` clean #清理
lzel@lzel-laptop:~/works/kernel/DeviceDrivers/1/modules$ make
lzel@lzel-laptop:~/works/kernel/DeviceDrivers/1/modules$ make clean
<三>IBM网站上
# Makefile2.6
ifneq ($(KERNELRELEASE),)
#KERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量,在第一次读取执行此Makefile
时,KERNELRELEASE没有被定义,所以make将读取执行else之后的内容。
#kbuild syntax. dependency relationshsip of files and target modules
are listed here.
#mymodule-objs := file1.o file2.o ... #表示mymoudule.o 由file1.o与file2.o
等连接生成。如果有多个文件,需要把这一句加上
obj-m := mymodule.o #表示编译连接后将生成mymodule.o模块。
else
PWD := $(shell pwd) #执行shell命令,把当前路径赋值给PWD
KVER ?= $(shell uname -r) #执行shell命令,将当前系统内核版本号赋值给KVER
KDIR := /lib/modules/$(KVER)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) #与"$(MAKE) -C $(KDIR) SUBDIRS
=$(PWD)"的作用是等效的,SUBDIRS是较老的使用方法
clean:
rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions
endif
执行路径:如果make的目标是clean,直接执行clean操作,然后结束。当make的目标为all时,-C $(KDIR)
指明跳转到内核源码目录下读取那里的Makefile;M=$(PWD)
表明然后返回到当前目录继续读入、执行当前的Makefile。当从内核源码目录返回时,KERNELRELEASE已被被定义,kbuild也被启动去解析kbuild语法的语句,make将继续读取else之前的内容。else之前的内容为kbuild语法的语句,
指明模块源码中各文件的依赖关系,以及要生成的目标模块名。
可惜我的机子无法实验这个Makefile,因为在/lib/modules/2.6.24-16-generic/目录下没有build目录
--
My Bolg: http://niutao.cublog.cn