Linux内核Makefile分析

        本文简单介绍了 Linux 内核 Makefile 的大框架,对于KBuild 系统的详细的编译过程没有作过多的分析,写的很粗犷~还请见谅。

更详细的内容请参考:

        嵌入式Linux应用开发完全手册

        makefile.txt 这个可以参考  http://blog.csdn.net/lizuobin2/article/details/51447338

        深度探索Linux操作系统:系统构建和原理解析      

        

        Linux内核中的Makefile虽然复杂,但是它的目的和作用与简单的Makefile都是一样的,无非是将文件编译,链接。它复杂在Linux内核文件太多,包括各种架构、各种设备,如何根据用户需求把它们巧妙的联系在一起,是Linux内核Makefile的难点。在Linux内核里,每个子目录都有一个makefile,它被称作Kbuilt-makefile,它将当前目录的文件编译成built-in.o、以及库文件、模块文件。然后顶层Makefile里指定这些built-in.o的路径,将它们连接在一起,具体的细节,请看下文.

Linux 内核Makefile 分类

        内核makefile.txt中将makefile分为 5部分,Kernel Makefile、ARCH Makefile、KBuild Makefile、.config文件以及scripts/Makefile.*

 

Kernel Makefile 

        Kernel Makefile 位于Linux 内核源代码的顶层目录,也叫 Top Makefile 。它主要用于指定编译Linux Kernel 目标文件(vmlinux )和模块(module )路径。它根据.config文件决定了内核根目录下那些文件、子目录被编译进内核。对于内核或驱动开发人员来说,这个文件几乎不用任何修改。


ARCH Makefile

        ARCH Makefile 位于ARCH/$(ARCH)/Makefile ,是系统对应平台的Makefile 。Kernel Top Makefile 会包含这个文件来指定平台相关信息。ARCH Makefile同样根据.config文件,决定了ARCH/$(ARCH) 目录下 那些文件、子目录被编译进内核 只有平台开发人员会关心这个文件。

 

Kbuild Makefile
        从Linux 内核2.6 开始,Linux 内核的编译采用Kbuild 系统 ,这同过去的编译系统有很大的不同,Kbuild 系统使用Kbuild Makefile 来编译内核或模块。当Kernel Makefile 被解析完成后,Kbuild 会读取相关的Kbuild Makefile 进行内核或模块的编译。Kbuild Makefile 有特定的语法指定哪些编译进内核中、哪些编译为模块、及对应的源文件是什么等。内核及驱动开发人员需要编写这个Kbuild Makefile 文件。


scripts/Makefile.* 通用规则

Makefile.build 
        被顶层Makefile所调用,与各级子目录的Makefile合起来构成一个完整的Makefile文件,定义built-in.o、.lib以及目标文件.o的生成规则。这个Makefile文件生成了子目录的.lib、built-in.o以及目标文件.o
Makefile.clean 
        被顶层Makefile所调用,用来删除目标文件等
Makefile.lib 
        被Makefile.build所调用,主要是对一些变量的处理,比如说在obj-y前边加上obj目录
Kbuild.include 
        被Makefile.build所调用,定义了一些函数,如if_changed、if_changed_rule、echo-cmd 

详细分析请参考:深度探索Linux操作系统:系统构建和原理解析


.config

       来自配置过程,生成 auto.conf 以及 autoconf.h,被顶层Makefile所包含,参考http://blog.csdn.net/lizuobin2/article/details/51429937

Linux内核Makefile分析_第1张图片

 

Kbuild Makefile  

         内核虽然有自己的构建系统Kbuild,但是kbuild 并不是什么新的东西,我们可以把kbuild 看作利用GNU Make组织的一套复杂的构建系统,虽然kbuild 也在Make 基础上做了适当的扩展,但是因为内核的复杂性,所以kbuild 要比一般的项目的Makefile 的组织要复杂的多。为了方便Linux开发者能够编写Makefile,kbuild考虑得不可谓不周到,比如,kbuild将所有与编译过程相关的共用规则和变量都提取到scripts 目录下的Makefile.build中,而具体的子目录下的 Makefile 文件则可以编写的非常简单与直接
        大多的Kbuild 文件的名字都是Makefile 。为了与其他Makefile 文件相区别,你也可以指定Kbuild Makefile 的名字为 Kbuild 。而且如果“Makefile ”和“Kbuild ”文件同时存在,则Kbuild 系统会使用“Kbuild ”文件。在这里,我们的 Kbuild Makefile 就是各个子目录的Makefile ,它的作用就是 指定当前目录下的文件,哪些被编译进当前目录的built-in.o、那些被编译成模块、那些不编译
    
       Kbuild Makefile 的一个最主要功能就是指定编译什么,这个功能是通过下面两个对象指定的obj-? 和xxx-objs 
       例如:
             obj-y += foo.o
             obj-m += abc.o
       大多数情况下是这样:
       obj-$(CONFIG_DM9000) += dm9000.o
       具体的 CONFIG_DM9000 是y 还是m取决于配置过程,配置过程产生.config文件,.config又产生auto.conf以及autoconf.h文件,包含在makefile文件里。
       参考:http://blog.csdn.net/lizuobin2/article/details/51429937
       如果内核模块是通过几个源文件编译而成的,您可以使用和上面同样的方法指定您想要编译的模块.然而此时Kbuild需要知道编译模块时是基于那些目标文件的,因此您需要设置一个$(<module_name>-objs)变量来告诉编译器.
       例如:#drivers/isdn/i4l/Makefile
              obj-$(CONFIG_ISDN) += isdn.o
              isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o


       用 obj-* 连接的Objects 在指明的文件夹中被用作模块或者综合进built-in.o 。也又可能被列出的objects 将会被包含进一个库,lib.a 。所有用lib-y 列出的objects 在那个文件夹中被综合进单独的一个库。列在obj-y 且 附加列在lib-y 中的Objects 将不会被包含在库中,因为他们将会被任意的存取。对于被连接在lib-m 中,连续的objects 将会被包含在lib.a 中。值得注意的是kbuild makefile 可能列出文件用作built-in ,并且作为库的一部分。因此,同一个文件夹可能包含一个built-in.o 和lib.a 文件 
     例如: 
             lib-y    := checksum.o delay.o
     这里讲会创建一个基于checksum.o 和delay.o 的库文件。对于kbuild ,识别一个lib.a 正在被构建,这个文件夹应该被列在 libs-y 中。 lib-y 的使用方法通常被限制在lib/ 和arc/*/lib 中。

 

Kernel Makefile
/* 指定 平台、编译器 */
        ARCH        ?= arm

        CROSS_COMPILE    ?= arm-linux-

/* 设置 6 类文件编译路径 */

        init-y        := init/

        drivers-y    := drivers/ sound/ firmware/

        net-y        := net/

        libs-y        := lib/

        core-y        := usr/

        core-y        += kernel/ mm/ fs/ ipc/ security/ crypto/ block/


/* 包含 ARCH Makefile 在通用编译路径基础上增加 架构相关的路径 */
        include $(srctree)/arch/$(SRCARCH)/Makefile

        head-y        := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o

        core-y                += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/

        core-y                += $(machdirs) $(platdirs)

        core-$(CONFIG_FPE_NWFPE)    += arch/arm/nwfpe/

        core-$(CONFIG_FPE_FASTFPE)    += $(FASTFPE_OBJ)

        core-$(CONFIG_VFP)        += arch/arm/vfp/

        drivers-$(CONFIG_OPROFILE)      += arch/arm/oprofile/

        libs-y                := arch/arm/lib/ $(libs-y)


/* 指定路径下的built-in.o 文件 */

        init-y           := $(patsubst %/, %/built-in.o, $(init-y))

        core-y        := $(patsubst %/, %/built-in.o, $(core-y))

        drivers-y    := $(patsubst %/, %/built-in.o, $(drivers-y))

        net-y           := $(patsubst %/, %/built-in.o, $(net-y))

        libs-y1        := $(patsubst %/, %/lib.a, $(libs-y))

        libs-y2        := $(patsubst %/, %/built-in.o, $(libs-y))

        libs-y          := $(libs-y1) $(libs-y2)

$(patsubst A, AB, C) 会将AB中的A替换为C
例如:

        net-y        := $(patsubst %/, %/built-in.o, $(net-y))

        $(net-y) == net/

        将 %/built-in.o中的%/替换为net/

结果为:net/built-in.o


/* 最终链接成vmlinux-all */

        vmlinux-init := $(head-y) $(init-y)

        vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)

        vmlinux-all  := $(vmlinux-init) $(vmlinux-main)

        vmlinux-lds  := arch/$(SRCARCH)/kernel/vmlinux.lds

        export KBUILD_VMLINUX_OBJS := $(vmlinux-all)

根据 vmlinux-all 我们可以得出文件的排放顺序,

                head-y        路径下的built-in.o

                init-y            路径下的built-in.o

                core-y         路径下的built-in.o

                libs-y           路径下的built-in.o

                drivers-y     路径下的built-in.o

                net-y           路径下的built-in.o

你可能感兴趣的:(linux,makefile,Kbuild)