`

Linux 内核 Makefile 体系简单分析

 
阅读更多

原文地址Linux 内核 Makefile 体系简单分析作者tekkamanninja


此文为两年前为好友刘庆敏的书《嵌入式Linux开发详解--基于AT91RM9200和Linux 2.6》中帮忙写的章节的重新整理。如有雷同,纯属必然。经作者同意,将我写的部分重新整理后放入blog中。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

众所周知,Linux内核是使用make命令来配置并编译的,那必然少不了Makefile。在内核目录树中我们可以看到内核编译系统的顶层Makefile文件。但是如此复杂、庞大的内核源码绝不可能使用一个或几个Makefile文件来完成配置编译,而是需要一套同样复杂、庞大,且为Linux内核定制的Makefile系统。她可以说是内核的一个子系统,是内核中比较特殊的一部分,几乎都是应用层的程序和脚本,但又和生成的内核二进制文件息息相关。编译不仅涉及本地编译,还涉及各个平台之间的交叉编译以及二进制文件格式处理等等。她是对Makefile在功能上的扩充,使其在配置编译Linux内核的时候更加灵活、高效和简洁。

尽管她是一个复杂的系统,但对绝大部分内核开发者来说只需要知道如何使用,而无需了解其中的细节。她对绝大部分内核开发者基本上是透明的,隐藏了大部分实现细节,有效地降低了开发者的负担,能使其能专注于内核开发,而不至于花费时间和精力在编译过程上。

以下我们就来简要的了解一下内核Makefile体系。

一、内核Makefile体系概述

其实内核Makefile体系的包含了KconfigKbuild两个系统。她曾经的维护人是Sam Ravnborg <sam@ravnborg.org>,现在的暂时没有查到。参考资料:

kbuild 更换维护者 作者:王聪(西邮神人,崇拜下)

Kconfig对应的是内核配置阶段,如你使用命令:make menuconfig,就是在使用Kconfig系统。Kconfig由以下三部分组成:

scripts/kconfig/*

Kconfig文件解析程序

kconfig

各个内核源代码目录中的kconfig文件

arch/$(ARCH)/configs/*_defconfig

各个平台的缺省配置文件

Kconfig系统生成.config后,Kbuild会依据.config编译指定的目标。后面我会简单地对make %config的流程进行情景分析,这里不必赘述。

Kbuild是内核Makefile体系重点,对应内核编译阶段,由5个部分组成:

顶层Makefile

根据不同的平台,对各类target分类并调用相应的规则Makefile生成目标

.config

内核配置文件

arch/$(ARCH)/Makefile

具体平台相关的Makefile

scripts/Makefile.*

通用规则文件,面向所有的Kbuild Makefiles,所起的作用可以从后缀名中得知。

各子目录下的Makefile文件

由其上层目录的Makefile调用,执行其上层传递下来的命令

而其中scripts目录下的编译规则文件和其目录下的C程序在整个编译过程起着重要的作用。列举如下:

文件名

作用

Kbuild.include

共用的定义文件,被许多独立的Makefile.*规则文件和顶层Makefile包含

Makefile.build

提供编译built-in.o, lib.a规则

Makefile.lib

负责归类分析obj-yobj-m和其中的目录subdir-ym所使用的规则

Makefile.host

本机编译工具(hostprog-y)的编译规则

Makefile.clean

内核源码目录清理规则

Makefile.headerinst

内核头文件安装时使用的规则

Makefile.modinst

内核模块安装规则

Makefile.modpost

模块编译的第二阶段,<module>.o<module>.mod生成<module>.ko时使用的规则

顶层Makefile主要是负责完成vmlinux(内核文件)*.ko(内核模块文件)的编译。顶层Makefile读取.config文件,并根据.config文件确定访问哪些子目录,并通过递归向下访问子目录的形式完成。顶层Makefile同时根据.config文件原封不动的包含一个具体架构的Makefile,其名字类似于arch/$(ARCH)/Makefile。该架构Makefile向顶层Makefile提供其架构的特别信息。

每一个子目录都有一个Makefile文件,用来执行从其上层目录传递下来的命令。子目录的Makefile也从.config文件中提取信息,生成内核编译所需的文件列表。


二、内核Makefile导读与情景分析

1、概述

上面简要介绍了内核Makefile的总体结构,但当我们打开顶层Makefile文件时还是因为她的复杂而觉得无从下手。但是内核Makefile就是Makefile,和最简单的Makefile遵循着同样的规则。所以只要我们静下心来分析,还是可以理解的。当然,在阅读内核的Makefile前,你必须对Makefileshell脚本有一定的基础。

  1. 推荐参考资料:
  2. GNU make中文手册》翻译整理:徐海兵PDF文档
  3. 高级Bash脚本编程指南》翻译:杨春敏 黄毅

根据Makefile的执行规则,在分析Makefile时,首先必须确定一个目标,然后才能确定所有的依赖关系,最后根据更新情况决定是否执行相应的命令。所以要看懂内核Makefile的大致框架,我们首先要了解她里面所定义的目标。而内核Makefile所定义的目标基本上可以通过make help打印出来(因为help本身就是顶层Makefile的一个目标,里面是打印帮助信息的“echo”命令)。

这些目标可以分为以下几个大类:

目标

常用目标举例

作用

配置

%config

config

启动Kconfig,以不同界面来配置内核。

menuconfig

xconfig

编译

all

编译vmlinux内核映像和内核模块

vmlinux

编译vmlinux内核映像

modules

编译内核模块

安装

headers_install

安装内核头文件/模块

modules_install

源码浏览

tags

生成代码浏览工具所需要的文件

TAGS

cscope

静态分析

checkstack

检查并分析内核代码

namespacecheck

headers_check

内核打包

%pkg

以不同的安装格式编译内核

文档转换

%doc

kernel文档转成不同格式

构架相关

(以arm为例)

zImage

生成压缩的内核映像

uImage

生成压缩的u-boot可引导的内核映像

install

安装内核映像

其中的构架相关目标在顶层Makefile上并出现,而是被包含在平台相关的Makefilearch/$(ARCH)/Makefile)中。

2、情景分析

以下我们就来分析一个简单的目标(menuconfig),作为情景分析范例来演示一下内核Makefile的分析方法。

首先当我们在内核源码的根目录下执行make menuconfig命令时,根据规则,make程序读取顶层Makefile文件及其包含的Makefile文件,内建所有的变量、明确规则和隐含规则,并建立所有目标和依赖之间的依赖关系结构链表。make程序最终会调用规则:

config %config: scripts_basic outputmakefile FORCE

$(Q)mkdir -p include/linux include/config

$(Q)$(MAKE) $(build)=scripts/kconfig $@

调用的原因是我们指定的目标“menuconfig”匹配了“%config”。

她的依赖目标是scripts_basicoutputmakefile,以及FORCE。也就是说在完成了这3个依赖目标后,下面的两个命令才会执行以完成我们指定的目标“menuconfig”。

所以我们来看看这三个依赖目标实现的简要过程:

1scripts_basic

make程序会调用规则:

scripts_basic:

$(Q)$(MAKE) $(build)=scripts/basic



他没有依赖目标,所以直接执行了以下的指令,只要将指令展开,我们就知道make做了什么操作。其中比较不好展开的是$(build),她的定义在scripts/Kbuild.include中:

build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj



所以展开后是:

make -f scripts/Makefile.build obj= scripts/basic


也就是make解析执行scripts/Makefile.build文件,且参数obj= scripts/basic。而在解析执行scripts/Makefile.build文件的时候,scripts/Makefile.build又会通过解析传入参数来包含对应文件夹下的Makefile文件(scripts/basic/Makefile),从中获得需要编译的目标。

在确定这个目标以后,通过目标的类别来继续包含一些scripts/Makefile.*文件。例如scripts/basic/Makefile中内容如下:

hostprogs-y := fixdep docproc hash

always := $(hostprogs-y)

# fixdep is needed to compile other host programs

$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep






所以scripts/Makefile.build会包含scripts/Makefile.host。相应的语句如下:

# Do not include host rules unless needed

ifneq ($(hostprogs-y)$(hostprogs-m),)

include scripts/Makefile.host

endif





此外scripts/Makefile.build会包含include scripts/Makefile.lib等必须的规则定义文件在这些文件的共同作用下完成对scripts/basic/Makefile中指定的程序编译。

由于Makefile.build的解析执行牵涉了多个Makefile.*文件,过程较为复杂,碍于篇幅无法一条一条指令的分析,兴趣的读者可以自行分析。

  1. 推荐两篇经典的分析文档:
  2. kbuild实现分析》 作者:x.yin@hotmail.com
  3. 《Kbuild系统原理分析》 作者未知,网上有PDF文档

2outputmakefile

make程序会调用规则:

PHONY += outputmakefile

# outputmakefile generates a Makefile in the output directory, if using a

# separate output directory. This allows convenient use of make in the

# output directory.

outputmakefile:

ifneq ($(KBUILD_SRC),)

$(Q)ln -fsn $(srctree) source

$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \

$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)

endif











从这里我们可以看出:outputmakefile是当KBUILD_SRC不为空(指定O=dir,编译输出目录和源代码目录分开)时,在输出目录建立Makefile时才执行命令的,

如果我们在源码根目录下执行make menuconfig命令时,这个目标是空的,什么都不做。

如果我们指定了O=dir时,就会执行源码目录下的scripts/mkmakefile,用于在指定的目录下产生一个Makefile,并可以在指定的目录下开始编译。

3FORCE

这是一个在内核Makefile中随处可见的伪目标,她的定义在顶层Makefile的最后:

PHONY += FORCE

FORCE:




是个完全的空目标,但是为什么要定义一个这样的空目标,并让许多目标将其作为依赖目标呢?原因如下:

正因为FORCE是一个没有命令或者依赖目标,不可能生成相应文件的伪目标。当make执行此规则时,总会认为FORCE不存在,必须完成这个目标,所以她是一个强制目标。也就是说:规则一旦被执行,make就认为它的目标已经被执行并更新过了。当她作为一个规则的依赖时,由于依赖总被认为被更新过的,因此作为依赖所在的规则中定义的命令总会被执行。所以可以这么说:只要执行依赖包含FORCE的目标,其目标下的命令必被执行。

make完成了以上3个目标之后,就开始执行下面的命令的,首先是

$(Q)mkdir -p include/linux include/config


这个很好理解,就是建立两个必须的文件夹。然后

$(Q)$(MAKE) $(build)=scripts/kconfig $@

这和我们上面分析的$(Q)$(MAKE) $(build)=结构相同,将其展开得到:

make -f scripts/Makefile.build obj=scripts/kconfigmenuconfig

所以这个指令的效果是使make解析执行scripts/Makefile.build文件,且参数obj=scripts/kconfig menuconfig。这样Makefile.build会包含对应文件夹下的Makefile文件(scripts/kconfig /Makefile),并完成scripts/kconfig /Makefile下的目标:

menuconfig: $(obj)/mconf

$< $(Kconfig)


这个目标的依赖条件是$(obj)/mconf,通过分析可知她其实是对应以下规则:

mconf-objs := mconf.o zconf.tab.o $(lxdialog)

……

ifeq ($(MAKECMDGOALS),menuconfig)

hostprogs-y += mconf

endif





也就是编译生成本机使用的mconf程序。完成依赖目标后,通过scripts/kconfig/Makefile中对Kconfig的定义可知,最后执行:

mconfarch/$(SRCARCH)/Kconfig


而对于confxconf等都有类似的过程,所以总结起来:当make %config时,内核根目录的顶层Makefile会临时编译出scripts/kconfig中的工具程序conf/mconf/qconf等负责对arch/$(SRCARCH)/Kconfig文件进行解析。这个Kconfig又通过source标记调用各个目录下的Kconfig文件构建出一个Kconfig,使得工具程序构建出整个内核的配置界面。在配置结束后,工具程序就会生成我们常见的.config文件。

三、在内核中添加自己的模块

虽然内核Makefile体系很是复杂,但是这种复杂带来的确是开发时的便利。其实内核Makefile体系之所以复杂,其中的一个原因就是为了方便扩展。对于一个开发者来要在内核中添加自己的一个驱动代码是非常简单的事情。

一般来说,对于一个新驱动代码的添加,驱动工程师只需要在内核源码的drivers目录的相应子目录下添加新设备驱动源码,并增加或修改该目录下的KconfigMakefile文件即可。

比如你已经写好了一个针对TI 的AM33XX芯片的LED的驱动程序,名为am33xx_led.c

(1)将驱动源码am33xx_led.c等文件复制到linux-X.Y.Z/drivers/char目录。

(2)在该目录下的Kconfig文件中添加LED驱动的配置选项:

config AM33XX_LED

bool "Support for am33xx led drivers"

depends on SOC_OMAPAM33XX

default n

---help---

Say Y here if you want to support forAM33XXLED drivers.








(3)在该目录下的Makefile文件中添加对LED驱动的编译:

obj-$(CONFIG_AM33XX_LED) += am33xx_led.o


这样你就可以在make menuconfig的时候看到这个配置选项,并进行配置了。

当然,上面的例子只是一个意思,对于Kconfig文件和Makefile的详细语法,请参考内核文档:Documentation/kbuild/makefile.txt

四 、在内核Makefile上对读者的建议

这个复杂的Makefile体系体现了很多优秀程序共有的设计思想,对于我们今后的程序设计上有很多值得借鉴的地方。比如:模块化设计、简化编程接口,使得自行添加模块更加的简洁。阅读分析这样复杂的Makefile对于学习和编写Makefileshell脚本有很好的参考价值。如果你正在学习Makefile的编写和阅读,那你可以耐心的分析一下内核的Makefile体系,只要你认真分析了一两个目标的实现,你会发现当你在阅读一些小软件的Makefile时已经是轻车熟路了。特别是现在很多芯片的开发包都是以SDK包的形式发布的,而这些软件包都是通过Makefile体系来实现自动编译和配置的,所以熟悉Makefile是每个Linux开发者都需要做到的。

分享到:
评论

相关推荐

    Linux内核修炼之道

    很好Linux内核休息书籍 第一层次修炼的内容包括了前三章, 目的是希望您能够对 Linux 以及内核有个全面的认 识和了解,掌握分析 Linux 内核源代码的分析方法。 第 1 章主要介绍了 Linux 的 18 年成长史, 或许您会...

    58-Make与Makefile在Linux内核体系编译中的应用

    58-Make与Makefile在Linux内核体系编译中的应用

    linux 内核移植和根文件系统的制作

     1.1.1 Linux版本 Linux内核的版本号可以从源代码的顶层目录下的Makefile中看到,比如2.6.29.1内核的Makefile中: VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 29 EXTRAVERSION = .1 其 中的“VERSION”和...

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

    硬件部件的使用及编程(囊括了常见硬件,比如UART、I*IC、LCD等),UBoot、Linux内核的分析、配置和移植,根文件系统的构造(包括移植busybox、glibc、制作映象文件等),内核调试技术(比如添加kgdb补丁、栈回溯等),...

    Linux kernel pdf 每一行都完全注释-初学者必备

    第2 章 LINUX 内核体系结构 13 21 LINUX 内核模式 13 22 LINUX 内核系统体系结构 14 23 LINUX 内核进程控制 15 24 LINUX 内核对内存的使用方法 16 25 LINUX 内核源代码的目录结构 18 26 内核系统与用户程序的关系 23...

    龙芯内核开发

    根据龙芯平台 Linux 内核实际情况,将内核的源码文件分为三部分: 驱动部分、体系架构相关部分 以及公共部分。 a) 驱动部分: 包含内核 driver 目录下所有的文件。 b) 体系架构相关部分:内核源码中与体系架构相关的...

    清华大学Linux操作系统原理与应用

    1.5.3 Linux内核源代码分析工具 14 习题1 15 第2章 内存寻址 17 2.1 内存寻址简介 17 2.1.1 Intel x86 CPU寻址方式的演变 18 2.1.2 IA32寄存器简介 19 2.1.3 物理地址、虚拟地址及线性地址 21 2.2 分段机制 22 2.2.1...

    疯狂内核之——内核初始化

    2.2 内核编译分析 28 2.2.1 编译配置 29 2.2.2 寻找第一个目标 32 2.2.3 prepare和scripts目标 38 2.2.4 递归编译各对象 41 2.2.5 链接vmlinux 44 2.2.6 制作bzImage 50 3 实模式下的内核代码 57 3.1 内核映像内存...

    嵌入式Linux视频教程全套2011新版-国嵌嵌入式培训下载地址

    -国嵌内核驱动进阶班-1-3(Linux内核配置与编译).avi -国嵌内核驱动进阶班-1-4(Linux内核模块开发).avi -国嵌内核驱动进阶班-1-5(必修实验).avi -第2天(U-Boot移植) -国嵌内核驱动进阶班-2-1(嵌入式linux...

    嵌入式Linux应用程序开发标准教程(第2版全)

    5.3 实验内容——创建Linux内核和文件系统 5.4 本章小结 5.5 思考与练习 第6章 文件I/O编程 6.1 Linux系统调用及用户编程接口(API) 6.1.1 系统调用 6.1.2 用户编程接口(API) 6.1.3 系统命令 6.2 Linux中文件及文件...

    Android底层开发技术实战详解--内核、移植和驱动.(电子工业.王振丽).part3

    1.5 分析Linux内核源代码很有必要 14 1.5.1 源代码目录结构 14 1.5.2 浏览源代码的工具 16 1.5.3 为什么用汇编语言编写内核代码 17 1.5.4 Linux内核的显著特性 18 1.5.5 学习Linux内核的...

    ARM9嵌入式Linux开发 ppt——周卫老师

     分析Linux内核的代码结构以及启动过程,并介绍如何移植到开发板上。  介绍嵌入式Linux文件系统的目录结构,然后构造嵌入式Linux文件系统。  嵌入式Linux驱动程序开发和移植。 嵌入式系统中的GUI介绍。  ...

    嵌入式Linux C编程入门(第2版) PPT

    第8章 嵌入式linux c语言基础——arm linux内核常见数据结构 225 8.1 链表 226 8.1.1 链表概述 226 8.1.2 单向链表 226 8.1.3 双向链表 233 8.1.4 循环链表 234 8.1.5 arm linux中链表使用实例 ...

    史上最强的嵌入式底层驱动开发课程 Linux系统开发+Linux高级程序+主板开发+ARM等

    ├&lt;1 Linux操作系统基础&gt; │ ├01 - 说在前面的话1.mp4 │ ├02 - 说在前面的话2.mp4 │ ├03 - 说在前面的话3.mp4 │ ├04 - 说在前面的话4.mp4 │ ├05 - 计算机组成原理概述1 .mp4 │ ├06 - 计算机组成原理概述2...

    嵌入式Linux应用程序开发详解

    5.3 实验内容——移植Linux内核 164 本章小结 165 思考与练习 165 第6章 文件I/O编程 166 6.1 Linux系统调用及用户编程接口(API) 166 6.1.1 系统调用 166 6.1.2 用户编程接口(API) 167 6.1.3 ...

    嵌入式Linux程序设计案例与实验教程(配套光盘)第一部分

    第3章 嵌入式Linux内核、引导系统和文件系统36 3.1 Linux内核定制、裁剪和添加36 3.1.1 概述36 3.1.2 内核目录简介37 3.1.3 配置文件和配置工具37 3.1.4 内核的编译命令39 实验3.1 Linux内核裁剪与编译40 3.2...

    嵌入式Linux程序设计案例与实验教程-实例代码

    第3章 嵌入式Linux内核、引导系统和文件系统36 3.1 Linux内核定制、裁剪和添加36 3.1.1 概述36 3.1.2 内核目录简介37 3.1.3 配置文件和配置工具37 3.1.4 内核的编译命令39 实验3.1 Linux内核裁剪与编译...

    LINUX操作系统(电子教案,参考答案)

    主要包括Linux的基本概念和操作,Linux的树型结构,Linux的文本编辑,Linux的安装和启动,用户管理,Shell编程技术,进程管理,C编译器,系统扩充,维护与监视,Linux的图形界面,网络的基本概念与设置,Linux在网络...

    嵌入式Linux程序设计案例与实验教程(配套光盘)第二部分

    第3章 嵌入式Linux内核、引导系统和文件系统36 3.1 Linux内核定制、裁剪和添加36 3.1.1 概述36 3.1.2 内核目录简介37 3.1.3 配置文件和配置工具37 3.1.4 内核的编译命令39 实验3.1 Linux内核裁剪与编译40 3.2...

Global site tag (gtag.js) - Google Analytics