[
摘要
]
由于
Linux
的独特优势,使越来越多的企业和科研机构把目光转向
Linux
的开发和研究上。目前
Linux
最新的稳定内核版本为
2.6.17
,但是当今绝大部分对于
Linux Makefile
的介绍文章都是基于
2.4
内核的
,可以说关于
2.6
内核
Makefile
相关的文章凤毛麟角,笔者抽时间完成了这篇分析文章,让读者迅速熟悉
Linux
最新
Makefile
体系,从而加深对内核的理解,同时也希望能对
Linux
在公司的推广起到一定的推动作用,算是抛砖引玉吧!
1
Makefile
组织层次
Linux
的
Make
体系由如下几部分组成:
Ø
顶层
Makefile
顶层
Makefile
通过读取配置文件,递归编译内核代码树的相关目录,从而产生两个重要的目标文件:
vmlinux
和模块。
Ø
内核相关
Makefile
位于
arch/$(ARCH)
目录下,为顶层
Makefile
提供与具体硬件体协结构相关的信息。
Ø
公共编译规则定义文件。
包括
Makefile.build
、
Makefile.clean
、
Makefile.lib
、
Makefile.host
等文件组成。这些文件位于
scripts
目录中,定义了编译需要的公共的规则和定义。
Ø
内核配置文件
.config
通过调用
make menuconfig
或者
make xconfig
命令,用户可以选择需要的配置来生成期望的目标文件。
Ø
其他
Makefile
主要为整个
Makefile
体系提供各自模块的目标文件定义,上层
Makefile
根据它所定义的目标来完成各自模块的编译。
2
Makefile
的使用
在编译内核之前,用户必须首先完成必要的配置。
Linux
内核提供了数不胜数的功能,支持众多的硬件体系结构,这就需要用户对将要生成的内核进行裁减。内核提供了多种不同的工具来简化内核的配置,最简单的一种是字符界面下命令行工具:
make config
这个工具会依次遍历内核所有的配置项,要求用户进行逐项的选择配置。这个工具会耗费用户太多时间,除非万不得以(你的编译主机不支持其他配置工具)一般不建议使用。
用户还可以使用利用
ncurse
库编制的图形界面工具,这就是大名鼎鼎的:
make menuconfig
相信以前对
2.4
内核比较熟悉的用户一定不会陌生。当然在
2.6
内核中提供了更漂亮和方便的基于
X11
的图形配置工具:
make xconfig
当用户使用这个工具对
Linux
内核进行配置时,界面下方会出现这个配置项相关的帮助信息和简单描述,当你对内核配置选项不太熟悉时,建议你使用这个工具来进行内核配置。
当用户完成配置后,配置工具会自动生成
.config
文件,它被保存在内核代码树的根目录下。用户可以很容易找到它,当然用户也可以直接对这个文件进行简单的修改。但是当你修改过配置文件之后,你必须通过下面的命令来验证和更新配置:
make oldconfig
跟
2.4
版本的不同之处在于,用户不需要显示的调用
make dep
命令来生成依赖文件,内核会自动维护代码间的依赖关系。
当一切工作完成以后,用户只需要简单键入
make
,剩下所有的工作
makefile
就会自动替你完成了。
3
Makefile
编译流程
当用户使用
Linux
的
Makefile
编译内核版本时,
Makefile
的编译流程如下:
Ø
使用命令行或者图形界面配置工具,对内核进行裁减,生成
.config
配置文件
Ø
保存内核版本信息到
include/linux/version.h
Ø
产生符号链接
include/asm,
指向实际目录
include/asm-$(ARCH)
Ø
为最终目标文件的生成进行必要的准备工作
Ø
递归进入
/init
、
/core
、
/drivers
、
/net
、
/lib
等目录和其中的子目录来编译生成所有的目标文件
Ø
链接上述过程产生的目标文件生成
vmlinux
,
vmlinux
存放在内核代码树的根目录下
Ø
最后根据
arch/$(ARCH)/Makefile
文件定义的后期编译的处理规则建立最终的映象
bootimage,
包括创建引导记录、准备
initrd
映象和相关处理
4
Makefile
关键规则和定义描述
1)
目标定义
目标定义是
Makefile
文件的核心部分,目标定义通知
Makefile
需要生成哪些目标文件、如何根据特殊的编译选项链接目标文件,同时控制哪些子目录要递归进入进行编译。
这个例子
Makefile
文件位于
/fs/ext2
目录
:
#
# Makefile for the linux ext2-filesystem routines.
#
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o /
ioctl.o namei.o super.o symlink.o
ext2-$(CONFIG_EXT2_FS_XATTR)
+= xattr.o xattr_user.o xattr_trusted.o
ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o
ext2-$(CONFIG_EXT2_FS_SECURITY)
+= xattr_security.o
ext2-$(CONFIG_EXT2_FS_XIP)
+= xip.o
这表示与
ext2
相关的目标文件由
ext2-y
定义的文件列表组成,其中
ext2-$(*)
是由内核配置文件
.config
中的配置项决定,最终
Makefile
会在这个目录下统一生成一个目标文件
ext2.o
(由
obj-$(CONFIG_EXT2_FS)
决定)。其中
obj-y
表示为生成
vmlinux
文件所需要的目标文件集合,具体的文件依赖于内核配置。
Makefile
会编译所有的
$(obj-y)
中定义的文件,然后调用链接器将这些文件链接到
built-in.o
文件中。最终
built-in.o
文件通过顶层
Makefile
链接到
vmlinux
中。值得注意的是
$(obj-y)
的文件顺序很重要。列表文件可以重复,文件第一次出现时将会链接到
built-in.o
中,后来出现的同名文件将会被忽略。文件顺序直接决定了他们被调用的顺序,这一点读者需要特别注意。
读者可能会在某些
Makefile
中发现
lib-y
定义,所有包含在
lib-y
定义中的目标文件都将会被编译到该目录下一个统一的库文件中。值得注意的是
lib-y
定义一般被限制在
lib
和
arch/$(ARCH)/lib
目录中。
体系makefile
文件和顶层makefile
文件共同定义了如何建立vmlinux
文件的规则。
$(head-y)
列举首先链接到vmlinux
的对象文件。
$(libs-y)
列举了能够找到lib.a
文件的目录。
其余的变量列举了能够找到内嵌对象文件的目录。
$(init-y)
列举的对象位于$(head-y)
对象之后。
然后是如下位置顺序:
$(core-y), $(libs-y), $(drivers-y)
和 $(net-y)
。
顶层makefile
定义了所有通用目录,arch/$(ARCH)/Makefile
文件只需增加体系相关的目录。
例如: #arch/i386/Makefile
libs-y
+= arch/i386/lib/
core-y
+= arch/i386/kernel/ /
arch/i386/mm/ /
arch/i386/$(mcore-y)/ /
arch/i386/crypto/
drivers-$(CONFIG_MATH_EMULATION)
+= arch/i386/math-emu/
drivers-$(CONFIG_PCI)
+= arch/i386/pci/
…………………………………………
2)
目录递归
Makefile
文件只负责当前目录下的目标文件,子目录中的文件由子目录中的
makefile
负责编译,编译系统使用
obj-y
和
obj-m
来自动递归编译各个子目录中的文件。
对于
fs/Makefile:
obj-$(CONFIG_EXT2_FS) += ext2/
如果在内核配置文件
.config
中,
CONFIG_EXT2_FS
被设置为
y
或者
m
,则内核
makefile
会自动进入
ext2
目录来进行编译。内核
Makefile
只使用这些信息来决定是否需要编译这个目录,子目录中的
makefile
规定哪些文件编译为模块,哪些文件编译进内核。
3)
依赖关系
Linux Makefile
通过在编译过程中生成的
.
文件名
.o.cmd
(比如对于
main.c
文件,它对应的依赖文件名为
.main.o.cmd
)来定义相关的依赖关系。
一般文件的依赖关系由如下部分组成:
Ø
所有的前期依赖文件(包括所有相关的
*.c
和
*.h
)
Ø
所有与
CONFIG_
选项相关的文件
Ø
编译目标文件所使用到的命令行
位于
init
目录下的
main.c
文件的依赖文件
.main.o.cmd
内容如下,读者可以结合起来理解上述文件依赖关系的三个组成部分
:
cmd_init/main.o := gcc -m32 -Wp,-MD,init/.main.o.d
-nostdinc -isystem /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include
-D__KERNEL__ -Iinclude -Iinclude2 -I/home/linux/linux-2.6.17.11/include
-include include/linux/autoconf.h -I/home/linux/linux-2.6.17.11/init
-Iinit -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs
-fno-strict-aliasing -fno-common -Os -fomit-frame-pointer -pipe
-msoft-float -mpreferred-stack-boundary=2 -march=i686 -mcpu=pentium4
-mregparm=3 -ffreestanding
-I/home/linux/linux-2.6.17.11/include/asm-i386/mach-default
-Iinclude/asm-i386/mach-default
-D"KBUILD_STR(s)=/#s" -D"KBUILD_BASENAME=KBUILD_STR(main)"
-D"KBUILD_MODNAME=KBUILD_STR(main)" -c -o init/.tmp_main.o /home/linux/linux-2.6.17.11/init/main.c
deps_init/main.o := /
/home/linux/linux-2.6.17.11/init/main.c /
$(wildcard include/config/x86/local/apic.h) /
$(wildcard include/config/acpi.h) /
#
由于篇幅的关系,此处略去一些定义
……………………………………..
include2/asm/mpspec_def.h /
/home/linux/linux-2.6.17.11/include/asm-i386/mach-default/mach_mpspec.h /
include2/asm/io_apic.h /
include2/asm/apic.h /
init/main.o: $(deps_init/main.o)
$(deps_init/main.o):
4)
特殊规则
特殊规则使用在内核编译需要规则定义而没有相应定义的时候。典型的例子如编译时头文件的产生规则。其他例子有体系makefile
编译引导映像的特殊规则。特殊规则写法同普通的makefile
规则。
编译程序在makefile
所在的目录不能被执行,因此所有的特殊规则需要提供前期文件和目标文件的相对路径。
定义特殊规则时将使用到两个变量:
$(src)
: $(src)
是对于makefile
文件目录的相对路径,当使用代码树中的文件时
使用该变量$(src)
。
$(obj)
: $(obj)
是目标文件目录的相对路径。生成文件使用$(obj)
变量。
例如: #drivers/scsi/Makefile
$(obj)/53c8xx_d.h: $(src)/53c7,8xx.scr $(src)/script_asm.pl
$(CPP) -DCHIP=810 - < $< | ... $(src)/script_asm.pl
这就是使用普通语法的特殊编译规则。
目标文件依赖于两个前提文件。目标文件的前缀是$(obj),
前提文件的前缀是
$(src)(
因为它们不是生成文件)
。
5)
引导映象
体系makefile
文件定义了编译vmlinux
文件的目标对象,将它们压缩和封装成引导代码,并复制到合适的位置。这包括各种安装命令。在Linux
中Makefile
无法为所有的体系结构提供标准化的方法,因此常需要具体硬件体系结构下makefile
提供附加处理规则。
附加处理过程常位于arch/$(ARCH)/
下的boot/
目录。
内核编译体系无法在boot/
目录下提供一种便捷的方法创建目标系统文件。因此arch/$(ARCH)/Makefile
要调用make
命令在boot/
目录下建立目标系统文件。建议使用的方法是在arch/$(ARCH)/Makefile
中设置调用,并且使用完整路径引用arch/$(ARCH)/boot/Makefile
。
例如: #arch/i386/Makefile
boot := arch/i386/boot
bzImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
建议使用"$(Q)$(MAKE) $(build)=<dir>"
方式在子目录中调用make
命令。
当执行不带参数的make
命令时,将首先编译第一个目标对象。在顶层makefile
中第一个目标对象是all:
。
一个体系结构需要定义一个默认的可引导映像。
增加新的前提文件给all
目标可以设置不同于vmlinux
的默认目标对象。
例如: #arch/i386/Makefile
all: bzImage
当执行不带参数的"make"
命令时,bzImage
文件将被编译。
6)
常用编译命令
if_changed
如果必要,执行传递的命令。
用法:
$(builtin-target): $(obj-y) FORCE
$(call if_changed,link_o_target)
当这条规则被使用时它将检查哪些文件需要更新,或命令行被改变。后面这种情况将迫使
重新编译编译选项被改变的执行文件。使用if_changed
的目标对象必须列举在$( builtin-target)
中,否则命令行检查将失败,目标一直会编译。
if_changed_dep
如果必要,执行传递的命令并更新依赖文件。
用法:
%.o: %.S FORCE
$(call if_changed_dep,as_o_S)
当这条规则被使用时它将检查哪些文件需要更新,或命令行被改变。同时它会重新检测依赖关系的改变并将生成新的依赖文件。这是与if_changed
命令的区别。
7)
定制命令
当正常执行带编译命令时命令的简短信息会被显示(要想显示详细的命令,请在命令行中加入V
=1
)。要让定制命令具有这种功能需要设置两个变量:
quiet_cmd_<command> -
将被显示的内容
cmd_<command> -
被执行的命令
例如: #
quiet_cmd_image = BUILD $@
cmd_image = $(obj)/tools/build $(BUILDFLAGS) /
$(obj)/vmlinux.bin > $@
targets += bzImage
$(obj)/bzImage: $(obj)/vmlinux.bin $(obj)/tools/build FORCE
$(call if_changed,image)
@echo 'Kernel: $@ is ready'
执行make
命令编译$(obj)/bzImage
目标时将显示:
BUILD arch/i386/boot/bzImage
8)
预处理链接脚本
当编译vmlinux
映像时将使用arch/$(ARCH)/kernel/vmlinux.lds
链接脚本。
相同目录下的vmlinux.lds.S
文件是这个脚本的预处理的变体。内核编译系统知晓.lds
文件。并使用规则*lds.S -> *lds
。
例如: #arch/i386/kernel/Makefile
always := vmlinux.lds
#Makefile
export CPPFLAGS_vmlinux.lds += -P -C -U$(ARCH)
$(always)
赋值语句告诉编译系统编译目标是vmlinux.lds
。$(CPPFLAGS_vmlinux.lds)
赋值语句告诉编译系统编译vmlinux.lds
目标的编译选项。
编译*.lds
时将使用到下面这些变量:
CPPFLAGS :
定义在顶层Makefile
EXTRA_CPPFLAGS :
可以设置在编译的makefile
文件中
CPPFLAGS_$(@F) :
目标编译选项。注意要使用文件全名。
9)
主机辅助程序的编译
内核编译系统支持在编译阶段编译主机可执行程序。为了使用主机程序需要两个步骤:第一个步骤使用hostprogs-y
变量告诉内核编译系统有主机程序可用。第二步给主机程序添加潜在的依赖关系。有两种方法,在规则中增加依赖关系或使用$(always)
变量。这一部分的内容相对于其他内核文件的编译要简单的多,感兴趣的读者可以参考scripts/Makefile.build
中的相关内容。
10)
Clean
机制
clean
命令清除在编译内核生成的大部分文件,例如主机程序,列举在 $(hostprogs-y)
、$(hostprogs-m)
、$(always)
、$(extra-y)
和$(targets)
中目标文件都将被删除。代码目录数中的"*.[oas]"
、"*.ko"
文件和一些由编译系统产生的附加文件也将被删除。
附加文件可以使用$(clean-files)
进行定义。
例如: #drivers/pci/Makefile
clean-files := devlist.h classlist.h
当执行"make clean"
命令时, "devlist.h classlist.h"
两个文件将被删除。内核编译系统默认这些文件与makefile
具有相同的相对路径,否则需要设置以'/'
开头的绝对路径。
删除整个目录使用以下方式:
例如: #scripts/package/Makefile
clean-dirs := $(objtree)/debian/
这样就将删除包括子目录在内的整个debian
目录。如果不使用以'/'
开头的绝对路径内核编译系统见默认使用相对路径。
通常内核编译系统根据"obj-* := dir/"
进入子目录,但是在体系makefile
中需要显式使用如下方式:
例如: #arch/i386/boot/Makefile
subdir- := compressed/
上面赋值语句指示编译系统执行"make clean"
命令时进入compressed/
目录。
在编译最终的引导映像文件的makefile
中有一个可选的目标对象名称是archclean
。
例如: #arch/i386/Makefile
archclean:
$(Q)$(MAKE) $(clean)=arch/i386/boot
当执行"make clean"
时编译器进入arch/i386/boot
并象通常一样工作。arch/i386/boot
中的makefile
文件可以使用subdir-
标识进入更下层的目录。
注意1: arch/$(ARCH)/Makefile
不能使用"subdir-"
,因为它被包含在顶层makefile
文件中,在这个位置编译机制是不起作用的。
注意2:
所有列举在core-y
、libs-y
、drivers-y
和net-y
中的目录将被"make clean"
命令清除。
4
小结
随着
Linux
的飞速发展,越来越多的开发人员将关注的焦点集中到
Linux
的研究和开发上。如果想对
Linux
内核进行研究和开发,就必须首先熟悉
Linux
内核
Makefile
的组织和编译过程。目前
Linux
最新的稳定内核版本为
2.6.17
,但是当今绝大部分对于
Linux Makefile
的介绍都是基于
2.4
内核的
,可以说关于
2.6
内核
Makefile
相关的文章凤毛麟角,我特意抽时间完成了这篇分析文章,让读者迅速熟悉
Linux
最新
Makefile
体系,从而加深对内核的理解,同时也希望能对
Linux
在公司的推广起到一定的推动作用。
5
参考资料
分享到:
相关推荐
《real6410用户手册3.0内核部分》着重介绍了Linux 3.0内核下的Nandflash驱动配置与优化,这对于理解和掌握real6410开发板上的内存技术设备(Memory Technology Device,MTD)驱动尤为重要。Nandflash作为嵌入式系统...
本文主要讨论的是如何从Linux内核源码生成zImage的过程,特别是针对Linux-3.0版本和ARMv7架构。zImage是一种压缩过的、可引导的内核映像,它是由vmlinux经过一系列转换得到的。 首先,我们看到`KBUILD_IMAGE`变量在...
本文将深入探讨基于OpenLoongson V3.0开发板的Linux 3.0内核版本,该版本的源码在压缩包文件“linux3.0_1c300b-master”中提供。OpenLoongson,也称为龙芯,是中国自主研发的处理器系列,主要用于嵌入式和服务器系统...
8. **编译与安装**:了解如何配置、编译和安装0.11内核也是学习过程的一部分,这涉及到makefile、配置脚本以及内核模块的加载。 通过研究"linux0.11-1-master"这个压缩包中的源代码,不仅可以追溯Linux的发展历程,...
### Linux Kernel 3.0 移植记录及过程详解 #### 一、概述 本文档详细介绍了如何在TX2440A开发板上移植Linux Kernel 3.0的操作步骤。TX2440A是一款基于S3C2440处理器的嵌入式系统开发板,广泛应用于学习和开发嵌入式...
标题中的“rtl8192_8188CU_linux_v3.0.2164.20110715.rar”是一个针对Linux操作系统的无线网卡驱动程序包,由Realtek公司开发。Realtek是一家知名的半导体制造商,其产品广泛应用于网络通信、音频处理等领域。这里的...
深入研究Linux 3.0.86源码,可以学习到如何调试内核、分析性能瓶颈、编写和修改驱动程序,以及如何为社区贡献代码。这对于任何希望成为Linux开发者的个人或团队来说,都是宝贵的资源。通过解压并编译这个源码,你将...
### Linux内核编译详解 #### 一、前言 Linux内核编译是一项重要的技能,对于想要深入了解和定制Linux系统的人来说至关重要。本文将详细解析RHEL6.1上升级内核至3.0.4的过程,从下载内核源码、安装编译环境、配置...
原LINUX设备驱动程序(LDD3)例程在Linux-3.0 .x代码树编译的时候会出现不少错误,如Makefile中出现“Fix it to use EXTAR_CFLAGS”,编译main.c时出现“unknown field ’ioctl‘ specified in initializer”等, 这...
爱科微8800D Linux SDK驱动V3.0版本是专为基于Linux操作系统的开发者设计的一套软件开发工具包,旨在帮助他们充分利用爱科微8800D芯片的功能,进行高效、稳定的系统集成和应用开发。该SDK包含了驱动程序、库文件、...
第一个版本可能是早期的开源驱动,通常在Linux内核的早期版本中集成。这个版本可能需要手动编译和加载,因为它可能并未包含在默认的内核源码中。用户需要下载驱动源码,然后按照特定的步骤配置、编译并安装到系统中...