linux内核makefile分析

2019-07-13 01:10发布

  本文简单介绍了 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以及目标文件.oMakefile.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  
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需要知道编译模块时是基于那些目标文件的,因此您需要设置一个$(-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学习之路 
来源:CSDN 
原文:https://blog.csdn.net/lizuobin2/article/details/51464152 
版权声明:本文为博主原创文章,转载请附上博文链接!