OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4546|回复: 0

【正点原子Linux连载】第三十五章Linux内核顶层Makefile详解--摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

[复制链接]

1118

主题

1129

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4671
金钱
4671
注册时间
2019-5-8
在线时间
1224 小时
发表于 2019-12-27 17:10:37 | 显示全部楼层 |阅读模式
本帖最后由 正点原子01 于 2019-12-27 15:30 编辑

1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址::https://item.taobao.com/item.htm?id=603672744434
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
4)对正点原子Linux感兴趣的同学可以加群讨论:935446741
5)关注正点原子公众号,获取最新资料更新
QQ图片20191129104007.png
第三十五章Linux内核顶层Makefile详解

前几章我们重点讲解了如何移植uboot到I.MX6U-ALPHA开发板上,从本章开始我们就开始学习如何移植Linux内核。同uboot一样,在具体移植之前,我们先来学习一下Linux内核的顶层Makefile文件,因为顶层Makefile控制着Linux内核的编译流程。

35.1 Linux内核获取
       关于Linux的起源以及发展历史,这里就不啰嗦了,网上相关的介绍太多了!即使写到这里也只是水一下教程页数而已,没有任何实际的意义。有限的时间还是放到有意义的事情上吧,LinuxLinux基金会管理与发布,Linux官网为https://www.kernel.org,所以你想获取最新的Linux版本就可以在这个网站上下载,网站界面如图35.1.1所示:
image002.jpg

35.1.1 linux官网
       从图35.1.1可以看出最新的稳定版Linux已经到了5.1.4,大家没必要追新,因为4.x版本的Linux5.x版本没有本质上的区别,5.x更多的是加入了一些新的平台、新的外设驱动而已。
       NXP会从https://www.kernel.org下载某个版本的Linux内核,然后将其移植到自己的CPU上,测试成功以后就会将其开放给NXPCPU开发者。开发者下载NXP提供的Linux内核,然后将其移植到自己的产品上。本章的移植我们就使用NXP提供的Linux源码,NXP提供Linux源码已经放到了开发板光盘中,路径为:1、例程源码-4NXP官方原版UbootLinux-linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2
35.2 Linux内核编译初次编译
       先看一下如何编译Linux源码,这里编译一下I.MX6U-ALPHA开发板移植好的Linux源码,已经放到了开发板光盘中,路径为:1、例程源码-3、正点原子修改后的UbootLinux-linux-imx-4.1.15-2.1.0-g8a006db.tar.bz2。在Ubuntu中新建名为“alientek_linux”的文件夹,然后将linux-imx-4.1.15-2.1.0-g8a006db.tar.bz2这个压缩包拷贝到前面新建的alientek_linux文件夹中并解压,命令如下:
  1. tar -vxjf linux-imx-4.1.15-2.1.0-g8a006db.tar.bz2
复制代码
       解压完成以后的Linux源码根目录如图35.2.1所示:
image004.jpg

35.2.1 正点原子提供的Linux源码根目录
       EMMC核心板为例,讲解一下如何编译出对应的Linux镜像文件。新建名为“mx6ull_alientek_emmc.sh”的shell脚本,然后在这个shell脚本里面输入如下所示内容:
示例代码35.2.1mx6ull_alientek_emmc.sh文件内容
  1. #!/bin/sh
  2. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
  3. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig
  4. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
  5. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16
复制代码
    使用chmod给予x6ull_alientek_emmc.sh可执行权限,然后运行此shell脚本,命令如下:
  1. ./mx6ull_alientek_emmc.sh
复制代码
       编译的时候会弹出Linux图形配置界面,如图35.2.3所示:
image006.jpg

35.2.3 Linux图形配置界面
       Linux的图行界面配置和uboot是一样的,这里我们不需要做任何的配置,直接按两下ESC键退出,退出图形界面以后会自动开始编译Linux。等待编译完成,完成以后如图35.2.4所示:
image008.jpg

35.2.4 Linux编译完成
       编译完成以后就会在arch/arm/boot这个目录下生成一个叫做zImage的文件,zImage就是我们要用的Linux镜像文件。另外也会在arch/arm/boo/dts下生成很多.dtb文件,这些.dtb就是设备树文件。
       编译Linux内核的时候可能会提示“recipefortarget ‘arch/arm/boot/compressed/piggy.lzo’failed”,如图35.2.5所示:
image010.gif

35.2.5 lzop未找到
       35.2.5中的错误提示lzop未找到,原因是没有安装lzop库,输入如下命令安装lzop库即可解决:
  1. sudoapt-getinstalllzop
复制代码
       lzop库安装完成以后在重新编译一下Linux内核即可。
       看一下编译脚本mx6ull_alientek_emmc.sh的内容,文件内容如下:
示例代码35.2.1mx6ull_alientek_emmc.sh文件内容
  1. #!/bin/sh
  2. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
  3. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig
  4. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
  5. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16
复制代码
       2行,执行“makedistclean”,清理工程,所以mx6ull_alientek_emmc.sh每次都会清理一下工程。如果通过图形界面配置了Linux,但是还没保存新的配置文件,那么就要慎重使用mx6ull_alientek_emmc.sh编译脚本了,因为它会把你的配置信息都删除掉!
       3行,执行“makexxx_defconfig”,配置工程。
       4行,执行“makemenuconfig”,打开图形配置界面,对Linux进行配置,如果不想每次编译都打开图形配置界面的话可以将这一行删除掉。
       5行,执行“make”,编译Linux源码。
       可以看出,Linux的编译过程基本和uboot一样,都要先执行“makexxx_defconfig”来配置一下,然后在执行“make”进行编译。如果需要使用图形界面配置的话就执行“makemenuconfig”。
35.3 Linux工程目录分析
将正点原子提供的Linux源码进行解压,解压完成以后的目录如图35.3.1所示:
image012.gif
35.3.1未编译的Linux源码目录
     35.3.1就是正点原子提供的未编译的Linux源码目录文件,我们在分析Linux之前一定要先在Ubuntu中编译一下Linux,因为编译过程会生成一些文件,而生成的这些恰恰是分析Linux不可或缺的文件。编译完成以后使用tar压缩命令对其进行压缩并使用Filezilla软件将压缩后的uboot源码拷贝到Windows下。
       编译后的Linux目录如图35.3.2所示:
image014.gif

35.3.2 编译后的Linux目录
       35.3.2中重要的文件夹或文件的含义见表35.3.1所示:
4DCEC5EF-9936-4c5a-B7B4-58B3DA5A525B.png
35.3.1 Linux目录
表35.3.1中的很多文件夹和文件我们都不需要去关心,我们要关注的文件夹或文件如下:
       1、arch目录
       这个目录是和架构有关的目录,比如armarm64avr32x86等等架构。每种架构都对应一个目录,在这些目录中又有很多子目录,比如bootcommonconfigs等等,以arch/arm为例,其子目录如图35.3.2所示:
image016.jpg

35.3.2 arch/arm子目录
       35.3.2arch/arm的一部分子目录,这些子目录用于控制系统引导、系统调用、动态调频、主频设置等。arch/arm/configs目录是不同平台的默认配置文件:xxx_defconfig,如图35.3.3所示:
image018.jpg

35.3.3 配置文件
       arch/arm/configs中就包含有I.MX6U-ALPHA开发板的默认配置文件:imx_v7_defconfig,执行“make imx_v7_defconfig”即可完成配置。arch/arm/boot/dts目录里面是对应开发平台的设备树文件,正点原子I.MX6U-ALPHA开发板对应的设备树文件如图35.3.4所示:
image020.gif

35.3.4 正点原子I.MX6U开发板对应的设备树
       arch/arm/boot目录下会保存编译出来的ImagezImage镜像文件,而zImage就是我们要用的linux镜像文件。
       arch/arm/mach-xxx目录分别为相应平台的驱动和初始化文件,比如mach-imx目录里面就是I.MX系列CPU的驱动和初始化文件。
       2、block目录
       blockLinux下块设备目录,像SD卡、EMMCNAND、硬盘等存储设备就属于块设备,block目录中存放着管理块设备的相关文件。
       3、crypto目录
       crypto目录里面存放着加密文件,比如常见的crccrc32md4md5hash等加密算法。
       4、Documentation目录
       此目录里面存放着Linux相关的文档,如果要想了解Linux某个功能模块或驱动架构的功能,就可以在Documentation目录中查找有没有对应的文档。
       5、drivers目录
       驱动目录文件,此目录根据驱动类型的不同,分门别类进行整理,比如drivers/i2c就是I2C相关驱动目录,drivers/gpio就是GPIO相关的驱动目录,这是我们学习的重点。
       6、firmware目录
       此目录用于存放固件。
       7、fs目录
       此目录存放文件系统,比如fs/ext2fs/ext4fs/f2fs等,分别是ext2ext4f2fs等文件系统。
       8、include目录
       头文件目录。
       9、init目录
       此目录存放Linux内核启动的时候初始化代码。
       10、ipc目录
       IPC为进程间通信,ipc目录是进程间通信的具体实现代码。
       11、kernel目录
       Linux内核代码。
       12、lib目录
       lib是库的意思,lib目录都是一些公用的库函。
       13、mm目录
       此目录存放内存管理相关代码。
       14、net目录
       此目录存放网络相关代码。
       15、samples目录
       此目录存放一些示例代码文件。
       16、scripts目录
       脚本目录,Linux编译的时候会用到很多脚本文件,这些脚本文件就保存在此目录中。
       17、security目录
       此目录存放安全相关的文件。
       18、sound目录
       此目录存放音频相关驱动文件,音频驱动文件并没有存放到drivers目录中,而是单独的目录。
       19、tools目录
       此目录存放一些编译的时候使用到的工具。
       20、usr目录
       此目录存放与initramfs有关的代码。
       21、virt目录
       此目录存放虚拟机相关文件。
       22、.config文件
       uboot一样,.config保存着Linux最终的配置信息,编译Linux的时候会读取此文件中的配置信息。最终根据配置信息来选择编译Linux哪些模块,哪些功能。
       23、Kbuild文件
       有些Makefile会读取此文件。
       24、Kconfig文件
图形化配置界面的配置文件。
       25、Makefile文件
Linux顶层Makefile文件,建议好好阅读一下此文件。
       26、README文件
此文件详细讲解了如何编译Linux源码,以及Linux源码的目录信息,建议仔细阅读一下此文件。
关于Linux源码目录就分析到这里,接下来分析一下Linux的顶层Makefile。
35.4 VSCode工程创建
       在分析Linux的顶层Makefile之前,先创建VSCode工程,创建过程和uboot一样。创建好以后将文件.vscode/settings.json改为如下所示内容:
示例代码35.4.1.1 settings.json文件内容
  1. {
  2. "search.exclude":{
  3. "**/node_modules": true,
  4. "**/bower_components": true,
  5. "**/*.o":true,
  6. "**/*.su":true,
  7. "**/*.cmd":true,
  8. "Documentation":true,

  9. /* 屏蔽不用的架构相关的文件 */
  10. "arch/alpha":true,
  11. "arch/arc":true,
  12. "arch/arm64":true,
  13. "arch/avr32":true,
  14. "arch/[b-z]*":true,
  15. "arch/arm/plat*":true,
  16. "arch/arm/mach-[a-h]*":true,
  17. "arch/arm/mach-[n-z]*":true,
  18. "arch/arm/mach-i[n-z]*":true,
  19. "arch/arm/mach-m[e-v]*":true,
  20. "arch/arm/mach-k*":true,
  21. "arch/arm/mach-l*":true,

  22. /* 屏蔽排除不用的配置文件 */
  23. "arch/arm/configs/[a-h]*":true,
  24. "arch/arm/configs/[j-z]*":true,
  25. "arch/arm/configs/imo*":true,
  26. "arch/arm/configs/in*":true,
  27. "arch/arm/configs/io*":true,
  28. "arch/arm/configs/ix*":true,

  29. /* 屏蔽掉不用的DTB文件 */
  30. "arch/arm/boot/dts/[a-h]*":true,
  31. "arch/arm/boot/dts/[k-z]*":true,
  32. "arch/arm/boot/dts/in*":true,
  33. "arch/arm/boot/dts/imx1*":true,
  34. "arch/arm/boot/dts/imx7*":true,
  35. "arch/arm/boot/dts/imx2*":true,
  36. "arch/arm/boot/dts/imx3*":true,
  37. "arch/arm/boot/dts/imx5*":true,
  38. "arch/arm/boot/dts/imx6d*":true,
  39. "arch/arm/boot/dts/imx6q*":true,
  40. "arch/arm/boot/dts/imx6s*":true,
  41. "arch/arm/boot/dts/imx6ul-*":true,
  42. "arch/arm/boot/dts/imx6ull-9x9*":true,
  43. "arch/arm/boot/dts/imx6ull-14x14-ddr*":true,
  44. },
  45. "files.exclude":{
  46. "**/.git": true,
  47. "**/.svn": true,
  48. "**/.hg": true,
  49. "**/CVS": true,
  50. "**/.DS_Store": true,
  51. "**/*.o":true,
  52. "**/*.su":true,
  53. "**/*.cmd":true,
  54. "Documentation":true,

  55. /* 屏蔽不用的架构相关的文件 */
  56. "arch/alpha":true,
  57. "arch/arc":true,
  58. "arch/arm64":true,
  59. "arch/avr32":true,
  60. "arch/[b-z]*":true,
  61. "arch/arm/plat*":true,
  62. "arch/arm/mach-[a-h]*":true,
  63. "arch/arm/mach-[n-z]*":true,
  64. "arch/arm/mach-i[n-z]*":true,
  65. "arch/arm/mach-m[e-v]*":true,
  66. "arch/arm/mach-k*":true,
  67. "arch/arm/mach-l*":true,

  68. /* 屏蔽排除不用的配置文件 */
  69. "arch/arm/configs/[a-h]*":true,
  70. "arch/arm/configs/[j-z]*":true,
  71. "arch/arm/configs/imo*":true,
  72. "arch/arm/configs/in*":true,
  73. "arch/arm/configs/io*":true,
  74. "arch/arm/configs/ix*":true,

  75. /* 屏蔽掉不用的DTB文件 */
  76. "arch/arm/boot/dts/[a-h]*":true,
  77. "arch/arm/boot/dts/[k-z]*":true,
  78. "arch/arm/boot/dts/in*":true,
  79. "arch/arm/boot/dts/imx1*":true,
  80. "arch/arm/boot/dts/imx7*":true,
  81. "arch/arm/boot/dts/imx2*":true,
  82. "arch/arm/boot/dts/imx3*":true,
  83. "arch/arm/boot/dts/imx5*":true,
  84. "arch/arm/boot/dts/imx6d*":true,
  85. "arch/arm/boot/dts/imx6q*":true,
  86. "arch/arm/boot/dts/imx6s*":true,
  87. "arch/arm/boot/dts/imx6ul-*":true,
  88. "arch/arm/boot/dts/imx6ull-9x9*":true,
  89. "arch/arm/boot/dts/imx6ull-14x14-ddr*":true,
  90. }
  91. }
复制代码
       创建好VSCode工程以后就可以开始分析Linux的顶层Makefile了。
35.5 顶层Makefile详解
       Linux的顶层Makefileuboot的顶层Makefile非常相似,因为uboot参考了Linux,前602行几乎一样,所以前面部分我们大致看一下就行了。
1、版本号
       顶层Makefile一开始就是Linux内核的版本号,如下所示:
示例代码35.5.1 顶层Makefile代码段
  1. VERSION =4
  2. PATCHLEVEL =1
  3. SUBLEVEL =15
  4. EXTRAVERSION =
复制代码
    可以看出,Linux内核版本号为4.1.15
2、MAKEFLAGS变量
MAKEFLAGS变量设置如下所示:
示例代码35.5.2 顶层Makefile代码段
  1. MAKEFLAGS +=-rR --include-dir=$(CURDIR)
复制代码
       3、命令输出
       Linux编译的时候也可以通过“V=1”来输出完整的命令,这个和uboot一样,相关代码如下所示:
示例代码35.5.3 顶层Makefile代码段
  1. ifeq ("$(originV)","commandline")
  2.    KBUILD_VERBOSE = $(V)
  3. endif
  4. ifndefKBUILD_VERBOSE
  5.    KBUILD_VERBOSE =0
  6. endif

  7. ifeq ($(KBUILD_VERBOSE),1)
  8.    quiet =
  9.    Q =
  10. else
  11.    quiet=quiet_
  12.    Q = @
  13. endif
复制代码
       4、静默输出
       Linux编译的时候使用make-s”就可实现静默编译,编译的时候就不会打印任何的信息,同uboot一样,相关代码如下:
示例代码35.5.4 顶层Makefile代码段
  1. ifneq ($(filter 4.%,$(MAKE_VERSION)),)   # make-4
  2. ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
  3.    quiet=silent_
  4. endif
  5. else                 #make-3.8x
  6. ifneq ($(filter s%-s%,$(MAKEFLAGS)),)
  7.    quiet=silent_
  8. endif
  9. endif

  10. export quiet QKBUILD_VERBOSE
复制代码
       5、设置编译结果输出目录
       Linux编译的时候使用“O=xxx”即可将编译产生的过程文件输出到指定的目录中,相关代码如下:
示例代码35.5.5 顶层Makefile代码段
  1. ifeq ($(KBUILD_SRC),)

  2. # OK, Make called indirectory where kernel src resides
  3. # Do we want tolocate output files in a separate directory?
  4. ifeq ("$(originO)","commandline")
  5.    KBUILD_OUTPUT := $(O)
  6. endif
复制代码
       6、代码检查
       Linux也支持代码检查,使用命令“make C=1”使能代码检查,检查那些需要重新编译的文件。“make C=2”用于检查所有的源码文件,顶层Makefile中的代码如下:
示例代码35.5.6 顶层Makefile代码段
  1. ifeq ("$(originC)","commandline")
  2.    KBUILD_CHECKSRC = $(C)
  3. endif
  4. ifndefKBUILD_CHECKSRC
  5.    KBUILD_CHECKSRC =0
  6. endif
复制代码
       7、模块编译
       Linux允许单独编译某个模块,使用命令“make M=dir”即可,旧语法“make SUBDIRS=dir”也是支持的。顶层Makefile中的代码如下:
示例代码35.5.7 顶层Makefile代码段
  1. # Use make M=dir to specifydirectory of external module to build
  2. # Old syntax make ... SUBDIRS=$PWD is stillsupported
  3. # Setting theenvironment variable KBUILD_EXTMOD take precedence
  4. ifdef SUBDIRS
  5.    KBUILD_EXTMOD ?= $(SUBDIRS)
  6. endif

  7. ifeq ("$(originM)","commandline")
  8.    KBUILD_EXTMOD := $(M)
  9. endif

  10. # If building anexternal module we do not care about the all: rule
  11. # but instead _alldepend on modules
  12. PHONY += all
  13. ifeq ($(KBUILD_EXTMOD),)
  14. _all: all
  15. else
  16. _all: modules
  17. endif

  18. ifeq ($(KBUILD_SRC),)
  19.          # building in the source tree
  20.          srctree :=.
  21. else
  22.          ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
  23.                  # building in a subdirectoryof the source tree
  24.                  srctree :=..
  25. else
  26.                  srctree := $(KBUILD_SRC)
  27.          endif
  28. endif
  29. objtree     :=.
  30. src     := $(srctree)
  31. obj     := $(objtree)

  32. VPATH       := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))

  33. export srctreeobjtree VPATH
复制代码
外部模块编译过程和uboot也一样,最终导出srctree、objtree和VPATH这三个变量的值,其中srctree=.,也就是当前目录,objtree同样为“.”。
       8、设置目标架构和交叉编译器
uboot一样,Linux编译的时候需要设置目标板架构ARCH和交叉编译器CROSS_COMPILE,在顶层Makefile中代码如下:
示例代码35.5.8 顶层Makefile代码段
  1. ARCH               ?= $(SUBARCH)
  2. CROSS_COMPILE     ?= $(CONFIG_CROSS_COMPILE:"%"=%)
复制代码
       为了方便,一般直接修改顶层Makefile中的ARCHCROSS_COMPILE,直接将其设置为对应的架构和编译器,比如本教程将ARCH设置为为armCROSS_COMPILE设置为arm-linux-gnueabihf-,如下所示:
示例代码35.5.9 顶层Makefile代码段
  1. ARCH               ?=arm
  2. CROSS_COMPILE     ?=arm-linux-gnueabihf-
复制代码
       设置好以后我们就可以使用如下命令编译Linux了:
  1. make xxx_defconfig              //使用默认配置文件配置Linux
  2. make menuconfig                 //启动图形化配置界面
  3. make -j16                            //编译Linux
复制代码
       9、调用scripts/Kbuild.include文件
       uboot一样,Linux顶层Makefile也会调用文件scripts/Kbuild.include,顶层Makefile相应代码如下:
示例代码35.5.10 顶层Makefile代码段
  1. # We need somegeneric definitions (do not try to remakethe file).
  2. scripts/Kbuild.include:;
  3. include scripts/Kbuild.include
复制代码
       10、交叉编译工具变量设置
       顶层Makefile中其他和交叉编译器有关的变量设置如下:
示例代码35.5.11 顶层Makefile代码段
  1. AS      = $(CROSS_COMPILE)as
  2. LD      = $(CROSS_COMPILE)ld
  3. CC      = $(CROSS_COMPILE)gcc
  4. CPP     = $(CC)-E
  5. AR      = $(CROSS_COMPILE)ar
  6. NM      = $(CROSS_COMPILE)nm
  7. STRIP       = $(CROSS_COMPILE)strip
  8. OBJCOPY     = $(CROSS_COMPILE)objcopy
  9. OBJDUMP     = $(CROSS_COMPILE)objdump
复制代码
       LALDCC等这些都是交叉编译器所使用的工具。
       11、头文件路径变量
       顶层Makefile定义了两个变量保存头文件路径:USERINCLUDELINUXINCLUDE,相关代码如下:
示例代码35.5.12 顶层Makefile代码段
  1. USERINCLUDE    := \
  2. -I$(srctree)/arch/$(hdr-arch)/include/uapi \
  3. -Iarch/$(hdr-arch)/include/generated/uapi \
  4. -I$(srctree)/include/uapi \
  5. -Iinclude/generated/uapi \
  6. -include $(srctree)/include/linux/kconfig.h

  7. # Use LINUXINCLUDEwhen you must reference the include/ directory.
  8. # Needed to becompatible with the O= option
  9. LINUXINCLUDE    := \
  10. -I$(srctree)/arch/$(hdr-arch)/include \
  11. -Iarch/$(hdr-arch)/include/generated/uapi \
  12. -Iarch/$(hdr-arch)/include/generated \
  13.          $(if $(KBUILD_SRC),-I$(srctree)/include) \
  14. -Iinclude \
  15.          $(USERINCLUDE)
复制代码
       381~386行是USERINCLUDEUAPI相关的头文件路径,第390~396行是LINUXINCLUDELinux内核源码的头文件路径。重点来看一下LINUXINCLUDE,其中srctree=.hdr-arch=armKBUILD_SRC为空,因此,将USERINCLUDELINUXINCLUDE展开以后为:
  1. USERINCLUDE   := \
  2.               -I./arch/arm/include/uapi\
  3.               -Iarch/arm/include/generated/uapi\
  4.               -I./include/uapi\
  5.               -Iinclude/generated/uapi\
  6.        -include ./include/linux/kconfig.h
  7. LINUXINCLUDE   := \
  8.               -I./arch/arm/include\
  9.               -Iarch/arm/include/generated/uapi\
  10.               -Iarch/arm/include/generated\
  11.               -Iinclude\
  12.               -I./arch/arm/include/uapi\
  13.               -Iarch/arm/include/generated/uapi\
  14.               -I./include/uapi\
  15.               -Iinclude/generated/uapi\
  16.        -include ./include/linux/kconfig.h
复制代码
       12、导出变量
       顶层Makefile会导出很多变量给子Makefile使用,导出的这些变量如下:
示例代码35.5.13 顶层Makefile代码段
  1. export VERSIONPATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION
  2. export ARCHSRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC
  3. export CPP AR NMSTRIP OBJCOPY OBJDUMP
  4. export MAKE AWKGENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE
  5. export HOSTCXXHOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS

  6. exportKBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
  7. exportKBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KASAN
  8. exportKBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
  9. exportKBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
  10. exportKBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
  11. exportKBUILD_ARFLAGS
复制代码
35.5.1 makexxx_defconfig过程
       第一次编译Linux之前都要使用“makexxx_defconfig”先配置Linux内核,在顶层Makefile中有“%config”这个目标,如下所示:
示例代码35.5.1.1 顶层Makefile代码段
  1. config-targets :=0
  2. mixed-targets  :=0
  3. dot-config     :=1

  4. ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
  5.      ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
  6.          dot-config :=0
  7.      endif
  8. endif

  9. ifeq ($(KBUILD_EXTMOD),)
  10.          ifneq ($(filter config %config,$(MAKECMDGOALS)),)
  11.                  config-targets :=1
  12.                  ifneq ($(words $(MAKECMDGOALS)),1)
  13.                          mixed-targets :=1
  14.                  endif
  15.          endif
  16. endif

  17. ifeq ($(mixed-targets),1)
  18. # =================================================================
  19. # We're calledwith mixed targets (*config and build targets).
  20. # Handle them oneby one.

  21. PHONY += $(MAKECMDGOALS) __build_one_by_one

  22. $(filter-out__build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one
  23.      @:

  24. __build_one_by_one:
  25.      $(Q)set -e; \
  26. for i in $(MAKECMDGOALS);do \
  27.          $(MAKE)-f $(srctree)/Makefile $i; \
  28.      done

  29. else
  30. ifeq ($(config-targets),1)
  31. # ================================================================
  32. # *config targets only- make sureprerequisites are updated, and
  33. # descend inscripts/kconfig tomake the *configtarget

  34. # Read archspecific Makefile to set KBUILD_DEFCONFIG as needed.
  35. # KBUILD_DEFCONFIGmay point out an alternative default
  36. # configurationused for'make defconfig'
  37. include arch/$(SRCARCH)/Makefile
  38. exportKBUILD_DEFCONFIG KBUILD_KCONFIG

  39. config: scripts_basicoutputmakefile FORCE
  40.      $(Q)$(MAKE) $(build)=scripts/kconfig $@

  41. %config: scripts_basic outputmakefile FORCE
  42.      $(Q)$(MAKE) $(build)=scripts/kconfig $@

  43. else
  44. ......
  45. endif # KBUILD_EXTMOD
复制代码
       490~507行和uboot一样,都是设置定义变量config-targetsmixed-targetsdot-config的值,最终这三个变量的值为:
  1. config-targets= 1
  2. mixed-targets= 0
  3. dot-config= 1
复制代码
       因为config-targets=1,因此第534~541行成立。第534行引用arch/arm/Makefile这个文件,这个文件很重要,因为zImageuImage等这些文件就是由arch/arm/Makefile来生成的。
       535行导出变量KBUILD_DEFCONFIG KBUILD_KCONFIG
       537行,没有目标与之匹配,因此不执行。
       540行,“makexxx_defconfig”与目标“%config”匹配,因此执行。“%config”依赖scripts_basicoutputmakefileFORCE,“%config”真正有意义的依赖就只有scripts_basicscripts_basic的规则如下:
示例代码35.5.1.2 顶层Makefile代码段
  1. scripts_basic:
  2.      $(Q)$(MAKE) $(build)=scripts/basic
  3.      $(Q)rm -f .tmp_quiet_recordmcount
复制代码
       build定义在文件scripts/Kbuild.include中,值为build := -f$(srctree)/scripts/Makefile.build obj,因此将示例代码35.5.1.2展开就是:
  1. scripts_basic:
  2.        @make-f ./scripts/Makefile.build obj=scripts/basic      //也可以没有@,视配置而定
  3.        @rm -f. tmp_quiet_recordmcount                             //也可以没有@
复制代码
       接着回到示例代码35.5.1.1的目标“%config”处,内容如下:
  1. %config: scripts_basic outputmakefile FORCE
  2.        $(Q)$(MAKE)$(build)=scripts/kconfig $@
复制代码
       将命令展开就是:
@make -f ./scripts/Makefile.build obj=scripts/kconfigxxx_defconfig
35.5.2 Makefile.build脚本分析
从上一小节可知,“makexxx_defconfig“配置Linux的时候如下两行命令会执行脚本scripts/Makefile.build:
  1. @make -f ./scripts/Makefile.buildobj=scripts/basic
  2. @make -f ./scripts/Makefile.build obj=scripts/kconfigxxx_defconfig
复制代码
我们依次来分析一下:
1、scripts_basic目标对应的命令
       scripts_basic目标对应的命令为:@make -f./scripts/Makefile.build obj=scripts/basic。打开文件scripts/Makefile.build,有如下代码:
示例代码35.5.2.1 Makefile.build代码段
  1. # The filenameKbuild has precedence over Makefile
  2. kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
  3. kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
  4. include $(kbuild-file)
复制代码
       kbuild-dir展开后为:
  1. kbuild-dir=./scripts/basic
复制代码
       kbuild-file展开后为:
  1. kbuild-file= ./scripts/basic/Makefile
复制代码
最后将59行展开,即:
  1. include./scripts/basic/Makefile
复制代码
       继续分析scripts/Makefile.build,如下代码:
示例代码35.5.2.2 Makefile.build代码段
  1. __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
  2.       $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
  3.       $(subdir-ym) $(always)
  4.      @:
复制代码
       __build是默认目标,因为命令“@make -f./scripts/Makefile.build obj=scripts/basic”没有指定目标,所以会使用到默认目标__build。在顶层Makefile中,KBUILD_BUILTIN1KBUILD_MODULES为空,因此展开后目标__build为:
  1. __build:$(builtin-target) $(lib-target)$(extra-y)) $(subdir-ym) $(always)
  2.        @:
复制代码
       可以看出目标__build5个依赖:builtin-targetlib-targetextra-ysubdir-ymalways。这5个依赖的具体内容如下:
  1. builtin-target =
  2. lib-target =
  3. extra-y =
  4. subdir-ym =
  5. always = scripts/basic/fixdep scripts/basic/bin2c
复制代码
只有always有效,因此__build最终为:
  1. __build: scripts/basic/fixdep scripts/basic/bin2c
  2.        @:
复制代码
       __build依赖于scripts/basic/fixdepscripts/basic/bin2c,所以要先将scripts/basic/fixdepscripts/basic/bin2c.c这两个文件编译成fixdepbin2c
       综上所述,scripts_basic目标的作用就是编译出scripts/basic/fixdepscripts/basic/bin2c这两个软件。
2、 %config目标对应的命令
       %config目标对应的命令为:@make -f./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig,此命令会使用到的各个变量值如下:
  1. src= scripts/kconfig
  2. kbuild-dir =./scripts/kconfig
  3. kbuild-file =./scripts/kconfig/Makefile
  4. include ./scripts/kconfig/Makefile
复制代码
可以看出,Makefile.build会读取scripts/kconfig/Makefile中的内容,此文件有如下所示内容:
示例代码35.5.2.3 scripts/kconfig/Makefile代码段
  1. %_defconfig: $(obj)/conf
  2.      $(Q)[        DISCUZ_CODE_5338        ]lt; $(silent)--defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
复制代码
       目标%_defconfig xxx_defconfig匹配,所以会执行这条规则,将其展开就是:
  1. %_defconfig: scripts/kconfig/conf
  2.        @scripts/kconfig/conf --defconfig=arch/arm/configs/%_defconfig Kconfig
复制代码
       %_defconfig依赖scripts/kconfig/conf,所以会编译scripts/kconfig/conf.c生成conf这个软件。此软件就会将%_defconfig中的配置输出到.config文件中,最终生成Linuxkernel根目录下的.config文件。
35.5.3 make过程
       使用命令“makexxx_defconfig”配置好Linux内核以后就可以使用“make”或者“makeall”命令进行编译。顶层Makefile有如下代码:
示例代码35.5.3.1 顶层Makefile代码段
  1. PHONY := _all
  2. _all:
  3. ...
  4. PHONY += all
  5. ifeq ($(KBUILD_EXTMOD),)
  6. _all: all
  7. else
  8. _all: modules
  9. endif
  10. ......
  11. all: vmlinux
复制代码
    126行,_all是默认目标,如果使用命令“make”编译Linux的话此目标就会被匹配。
       193行,如果KBUILD_EXTMOD为空的话194行的代码成立。
       194行,默认目标_all依赖all
       608行,目标all依赖vmlinux,所以接下来的重点就是vmlinux
       顶层Makefile中有如下代码:
示例代码35.5.3.2 顶层Makefile代码段
  1. # Externallyvisible symbols (used by link-vmlinux.sh)
  2. exportKBUILD_VMLINUX_INIT := $(head-y) $(init-y)
  3. exportKBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y)
  4. exportKBUILD_LDS          := arch/$(SRCARCH)/kernel/vmlinux.lds
  5. exportLDFLAGS_vmlinux
  6. # used by scripts/pacmage/Makefile
  7. exportKBUILD_ALLDIRS := $(sort $(filter-out arch/%,$(vmlinux-alldirs)) arch Documentationinclude samples scripts tools virt)

  8. vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)

  9. # Final link ofvmlinux
  10.        cmd_link-vmlinux = $(CONFIG_SHELL) [        DISCUZ_CODE_5341        ]lt; $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)
  11. quiet_cmd_link-vmlinux = LINK    $@

  12. # Include targetswhich we want to
  13. # execute if the rest of thekernel build went well.
  14. vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
  15. ifdefCONFIG_HEADERS_CHECK
  16.      $(Q)$(MAKE)-f $(srctree)/Makefile headers_check
  17. endif
  18. ifdefCONFIG_SAMPLES
  19.      $(Q)$(MAKE) $(build)=samples
  20. endif
  21. ifdefCONFIG_BUILD_DOCSRC
  22.      $(Q)$(MAKE) $(build)=Documentation
  23. endif
  24. ifdefCONFIG_GDB_SCRIPTS
  25.      $(Q)ln -fsn `cd $(srctree)&&/bin/pwd`/scripts/gdb/vmlinux-gdb.py
  26. endif
  27. +$(call if_changed,link-vmlinux)
复制代码
    920行可以看出目标vmlinux依赖scripts/link-vmlinux.sh$(vmlinux-deps) FORCE。第912行定义了vmlinux-deps,值为:
  1. vmlinux-deps=$(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)
复制代码
       905行,KBUILD_VMLINUX_INIT= $(head-y) $(init-y)
       906行,KBUILD_VMLINUX_MAIN = $(core-y) $(libs-y) $(drivers-y) $(net-y)
       907行,KBUILD_LDS= arch/$(SRCARCH)/kernel/vmlinux.lds,其中SRCARCH=arm,因此KBUILD_LDS= arch/arm/kernel/vmlinux.lds
       综上所述,vmlinux的依赖为:scripts/link-vmlinux.sh$(head-y) $(init-y)$(core-y) $(libs-y) $(drivers-y) $(net-y)arch/arm/kernel/vmlinux.ldsFORCE
       933行的命令用于链接生成vmlinux
       重点来看一下$(head-y) $(init-y)$(core-y) $(libs-y) $(drivers-y) $(net-y)这六个变量的值。
       1、head-y
       head-y定义在文件arch/arm/Makefile中,内容如下:
示例代码35.5.3.3arch/arm/Makefile代码段
  1. head-y      := arch/arm/kernel/head$(MMUEXT).o
复制代码
    当不使能MMU的话MMUEXT=-nommu,如果使能MMU的话为空,因此head-y最终的值为:
  1. head-y = arch/arm/kernel/head.o
复制代码
       2、init-y、drivers-y和net-y
在顶层Makefile中有如下代码:
示例代码35.5.3.4 顶层Makefile代码段
  1. init-y         := init/
  2. drivers-y     := drivers/ sound/ firmware/
  3. net-y          := net/
  4. ...
  5. init-y         := $(patsubst %/, %/built-in.o, $(init-y))
  6. drivers-y     := $(patsubst %/, %/built-in.o, $(drivers-y))
  7. net-y          := $(patsubst %/, %/built-in.o, $(net-y))
复制代码
    从示例代码35.5.3.4可知,init-ylibs-ydrivers-ynet-y最终的值为:
  1. init-y      =init/built-in.o
  2. drivers-y =drivers/built-in.o  sound/built-in.o  firmware/built-in.o
  3. net-y       =net/built-in.o
复制代码
       3、libs-y
       libs-y基本和init-y一样,在顶层Makefile中存在如下代码:
示例代码35.5.3.5 顶层Makefile代码段
  1. libs-y      := lib/
  2. ...
  3. libs-y1     := $(patsubst %/, %/lib.a, $(libs-y))
  4. libs-y2     := $(patsubst %/, %/built-in.o, $(libs-y))
  5. libs-y      := $(libs-y1) $(libs-y2)
复制代码
    根据示例代码35.5.3.5可知,libs-y应该等于“lib.a built-in.o”,这个只正确了一部分!因为在arch/arm/Makefile中会向libs-y中追加一些值,代码如下:
示例代码35.5.3.6 arch/arm/Makefile代码段
  1. libs-y     := arch/arm/lib/ $(libs-y)
复制代码
    arch/arm/Makefilelibs-y的值改为了:arch/arm/lib$(libs-y),展开以后为:
  1. libs-y = arch/arm/lib lib/
复制代码
       因此根据示例代码35.5.3.5的第900~902行可知,libs-y最终应该为:
  1. libs-y = arch/arm/lib/lib.a  lib/lib.a arch/arm/lib/built-in.o lib/built-in.o
复制代码
       4、core-y
       core-yinit-y也一样,在顶层Makefile中有如下代码:
示例代码35.5.3.7 顶层Makefile代码段
  1. core-y     := usr/
  2. ......
  3. core-y     += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
复制代码
    但是在arch/arm/Makefile中会对core-y进行追加,代码如下:
示例代码35.5.3.8 arch/arm/Makefile代码段
  1. core-$(CONFIG_FPE_NWFPE)    += arch/arm/nwfpe/
  2. core-$(CONFIG_FPE_FASTFPE)  += $(FASTFPE_OBJ)
  3. core-$(CONFIG_VFP)      += arch/arm/vfp/
  4. core-$(CONFIG_XEN)      += arch/arm/xen/
  5. core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/
  6. core-$(CONFIG_VDSO)     += arch/arm/vdso/

  7. # If we have amachine-specificdirectory, theninclude it in the build.
  8. core-y          += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
  9. core-y              += arch/arm/probes/
  10. core-y              += arch/arm/net/
  11. core-y              += arch/arm/crypto/
  12. core-y              += arch/arm/firmware/
  13. core-y              += $(machdirs) $(platdirs)
复制代码
    269~274行根据不同的配置向core-y追加不同的值,比如使能VFP的话就会在.config中有CONFIG_VFP=y这一行,那么core-y就会追加“arch/arm/vfp/”。
       277~282行就是对core-y直接追加的值。
       在顶层Makefile中有如下一行:
示例代码35.5.3.9 顶层Makefile代码段
  1. core-y      := $(patsubst %/, %/built-in.o, $(core-y))
复制代码
    经过上述代码的转换,最终core-y的值为:
  1. core-y =        usr/built-in.o                               arch/arm/vfp/built-in.o\
  2. arch/arm/vdso/built-in.o              arch/arm/kernel/built-in.o\
  3. arch/arm/mm/built-in.o                      arch/arm/common/built-in.o\
  4. arch/arm/probes/built-in.o            arch/arm/net/built-in.o\
  5. arch/arm/crypto/built-in.o            arch/arm/firmware/built-in.o\
  6. arch/arm/mach-imx/built-in.o              kernel/built-in.o\
  7. mm/built-in.o                             fs/built-in.o\
  8. ipc/built-in.o                              security/built-in.o\
  9. crypto/built-in.o                         block/built-in.o
复制代码
       关于head-y 、init-y、core-y 、libs-y 、drivers-y和net-y这6个变量就讲解到这里。这些变量都是一些built-in.o或.a等文件,这个和uboot一样,都是将相应目录中的源码文件进行编译,然后在各自目录下生成built-in.o文件,有些生成了.a库文件。最终将这些built-in.o和.a文件进行链接即可形成ELF格式的可执行文件,也就是vmlinux!但是链接是需要连接脚本的,vmlinux的依赖arch/arm/kernel/vmlinux.lds就是整个Linux的链接脚本。
       示例代码35.5.3.2第933行的命令“+$(call if_changed,link-vmlinux)”表示将$(call if_changed,link-vmlinux)的结果作为最终生成vmlinux的命令,前面的“+”表示该命令结果不可忽略。$(callif_changed,link-vmlinux)是调用函数if_changed,link-vmlinux是函数if_changed的参数,函数if_changed定义在文件scripts/Kbuild.include中,如下所示:
示例代码35.5.3.10 scripts/Kbuild.include代码段
  1. <blockquote> if_changed = $(if $(strip $(any-prereq) $(arg-check)),  \
复制代码
    any-prereq用于检查依赖文件是否有变化,如果依赖文件有变化那么any-prereq就不为空,否则就为空。arg-check用于检查参数是否有变化,如果没有变化那么arg-check就为空。
       第248行,“@set -e”告诉bash,如果任何语句的执行结果不为true(也就是执行出错)的话就直接退出。
       第249行,$(echo-cmd)用于打印命令执行过程,比如在链接vmlinux的时候就会输出“LINK vmlinux”。$(cmd_$(1))中的$(1)表示参数,也就是link-vmlinux,因此$(cmd_$(1))表示执行cmd_link-vmlinux的内容。cmd_link-vmlinux在顶层Makefile中有如下所示定义:
示例代码35.5.3.11 顶层Makefile代码段
  1. # Final link ofvmlinux
  2.        cmd_link-vmlinux = $(CONFIG_SHELL) [        DISCUZ_CODE_5356        ]lt; $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)
  3. quiet_cmd_link-vmlinux = LINK    $@
复制代码
    第915行就是cmd_link-vmlinux的值,其中CONFIG_SHELL=/bin/bash,$<表示目标vmlinux的第一个依赖文件,根据示例代码35.5.3.2可知,这个文件为scripts/link-vmlinux.sh。
LD= arm-linux-gnueabihf-ld -EL,LDFLAGS为空。LDFLAGS_vmlinux的值由顶层Makefile和arch/arm/Makefile这两个文件共同决定,最终LDFLAGS_vmlinux=-p --no-undefined -X --pic-veneer --build-id。因此cmd_link-vmlinux最终的值为:
  1. cmd_link-vmlinux = /bin/bashscripts/link-vmlinux.sh arm-linux-gnueabihf-ld -EL -p --no-undefined -X--pic-veneer --build-id
复制代码
       cmd_link-vmlinux会调用scripts/link-vmlinux.sh这个脚本来链接出vmlinux!在link-vmlinux.sh中有如下所示代码:
示例代码35.5.3.12scripts/link-vmlinux.sh代码段
  1. vmlinux_link()
  2. {
  3.   local lds="${objtree}/${KBUILD_LDS}"

  4. if["${SRCARCH}"!="um"]; then
  5.       ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux}-o ${2}                  \
  6. -T ${lds} ${KBUILD_VMLINUX_INIT}                     \
  7. --start-group ${KBUILD_VMLINUX_MAIN}--end-group ${1}
  8. else
  9.       ${CC} ${CFLAGS_vmlinux}-o ${2}                              \
  10. -Wl,-T,${lds} ${KBUILD_VMLINUX_INIT}                 \
  11. -Wl,--start-group                                    \
  12.                ${KBUILD_VMLINUX_MAIN}                      \
  13. -Wl,--end-group                                     \
  14. -lutil ${1}
  15.       rm -f linux
  16.   fi
  17. }
  18. ......
  19. info LD vmlinux
  20. vmlinux_link "${kallsymso}" vmlinux
复制代码
       vmliux_link就是最终链接出vmlinux的函数,第55行判断SRCARCH是否等于“um”,如果不相等的话就执行56~58行的代码。因为SRCARCH=arm,因此条件成立,执行56~58行的代码。这三行代码就应该很熟悉了!就是普通的链接操作,连接脚本为lds=./arch/arm/kernel/vmlinux.lds,需要链接的文件由变量KBUILD_VMLINUX_INIT和KBUILD_VMLINUX_MAIN来决定,这两个变量在示例代码35.5.3.2中已经讲解过了。
第217行调用vmlinux_link函数来链接出vmlinux。
使用命令“make V=1”编译Linux,会有如图35.5.3.1所示的编译信息:
image022.jpg
图35.5.3.1 link-vmlinux.sh链接vmlinux过程
       至此我们基本理清了make的过程,重点就是将各个子目录下的built-in.o、.a等文件链接在一起,最终生成vmlinux这个ELF格式的可执行文件。链接脚本为arch/arm/kernel/vmlinux.lds,链接过程是由shell脚本scripts/link-vmlinux.s来完成的。接下来的问题就是这些子目录下的built-in.o、.a等文件又是如何编译出来的呢?
35.5.4 built-in.o文件编译生成过程
       根据示例代码35.5.3.2第920行可知,vmliux依赖vmlinux-deps,而vmlinux-deps= $(KBUILD_LDS)$(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN),KBUILD_LDS是连接脚本,这里不考虑,剩下的KBUILD_VMLINUX_INIT和KBUILD_VMLINUX_MAIN就是各个子目录下的built-in.o、.a等文件。最终vmlinux-deps的值如下:
  1. vmlinux-deps =      arch/arm/kernel/vmlinux.lds                     arch/arm/kernel/head.o\
  2. init/built-in.o                                           usr/built-in.o\
  3. arch/arm/vfp/built-in.o                       arch/arm/vdso/built-in.o\
  4. arch/arm/kernel/built-in.o                   arch/arm/mm/built-in.o\
  5. arch/arm/common/built-in.o                      arch/arm/probes/built-in.o\
  6. arch/arm/net/built-in.o                       arch/arm/crypto/built-in.o\
  7. arch/arm/firmware/built-in.o                      arch/arm/mach-imx/built-in.o\
  8. kernel/built-in.o                                mm/built-in.o\
  9. fs/built-in.o                                       ipc/built-in.o\
  10. security/built-in.o                              crypto/built-in.o\
  11. block/built-in.o                                 arch/arm/lib/lib.a\
  12. lib/lib.a                                            arch/arm/lib/built-in.o\
  13. lib/built-in.o                                     drivers/built-in.o\
  14. sound/built-in.o                                 firmware/built-in.o\
  15. net/built-in.o
复制代码
       除了arch/arm/kernel/vmlinux.lds以外,其他都是要编译链接生成的。在顶层Makefile中有如下代码:
示例代码35.5.4.1 顶层Makefile代码段
  1. $(sort $(vmlinux-deps)): $(vmlinux-dirs);
复制代码
       sort是排序函数,用于对vmlinux-deps的字符串列表进行排序,并且去掉重复的单词。可以看出vmlinux-deps依赖vmlinux-dirs,vmlinux-dirs也定义在顶层Makefile中,定义如下:
示例代码35.5.4.2 顶层Makefile代码段
  1. vmlinux-dirs   := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
  2.               $(core-y) $(core-m) $(drivers-y) $(drivers-m) \
  3.               $(net-y) $(net-m) $(libs-y) $(libs-m)))
复制代码
       vmlinux-dirs看名字就知道和目录有关,此变量保存着生成vmlinux所需源码文件的目录,值如下:
  1. vmlinux-dirs = init                     usr                              arch/arm/vfp\
  2.                      arch/arm/vdso      arch/arm/kernel            arch/arm/mm \
  3.                      arch/arm/common  arch/arm/probes            arch/arm/net \
  4.                      arch/arm/crypto    arch/arm/firmware               arch/arm/mach-imx\
  5.                      kernel                  mm                             fs \
  6.                      ipc                      security                        crypto \
  7.                      block                   drivers                         sound \
  8.                      firmware             net                              arch/arm/lib \
  9. lib
复制代码
       在顶层Makefile中有如下代码:
示例代码35.5.4.3 顶层Makefile代码段
  1. $(vmlinux-dirs): prepare scripts
  2.     $(Q)$(MAKE) $(build)=$@
复制代码
       目标vmlinux-dirs依赖preparescripts,这两个依赖不去浪费时间了,重点看一下第947行的命令。build前面已经说了,值为“-f ./scripts/Makefile.buildobj”,因此将947行的命令展开就是:
  1. @ make -f ./scripts/Makefile.build obj=$@
复制代码
       $@表示目标文件,也就是vmlinux-dirs的值,将vmlinux-dirs中的这些目录全部带入到命令中,结果如下:
  1. @ make -f ./scripts/Makefile.build obj=init
  2. @ make -f ./scripts/Makefile.build obj=usr
  3. @ make -f ./scripts/Makefile.buildobj=arch/arm/vfp
  4. @ make -f ./scripts/Makefile.buildobj=arch/arm/vdso
  5. @ make -f ./scripts/Makefile.buildobj=arch/arm/kernel
  6. @ make -f ./scripts/Makefile.build obj=arch/arm/mm
  7. @ make -f ./scripts/Makefile.buildobj=arch/arm/common
  8. @ make -f ./scripts/Makefile.buildobj=arch/arm/probes
  9. @ make -f ./scripts/Makefile.buildobj=arch/arm/net
  10. @ make -f ./scripts/Makefile.buildobj=arch/arm/crypto
  11. @ make -f ./scripts/Makefile.buildobj=arch/arm/firmware
  12. @ make -f ./scripts/Makefile.build obj=arch/arm/mach-imx
  13. @ make -f ./scripts/Makefile.build obj=kernel
  14. @ make -f ./scripts/Makefile.build obj=mm
  15. @ make -f ./scripts/Makefile.build obj=fs
  16. @ make -f ./scripts/Makefile.build obj=ipc
  17. @ make -f ./scripts/Makefile.build obj=security
  18. @ make -f ./scripts/Makefile.build obj=crypto
  19. @ make -f ./scripts/Makefile.build obj=block
  20. @ make -f ./scripts/Makefile.build obj=drivers
  21. @ make -f ./scripts/Makefile.build obj=sound
  22. @ make -f ./scripts/Makefile.build obj=firmware
  23. @ make -f ./scripts/Makefile.build obj=net
  24. @ make -f ./scripts/Makefile.buildobj=arch/arm/lib
  25. @ make -f ./scripts/Makefile.build obj=lib
复制代码
       这些命令运行过程其实都是一样的,我们就以“@ make -f./scripts/Makefile.build obj=init”这个命令为例,讲解一下详细的运行过程。这里又要用到Makefile.build这个脚本了,此脚本默认目标为__build,这个在35.5.2小节已经讲过了,我们再来看一下,__build目标对应的规则如下:
示例代码35.5.4.4scripts/Makefile.build代码段
  1. __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
  2.    $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
  3.    $(subdir-ym) $(always)
  4.   @:
复制代码
       当只编译Linux内核镜像文件,也就是使用“makezImage”编译的时候,KBUILD_BUILTIN=1KBUILD_MODULES为空。“make”命令是会编译所有的东西,包括Linux内核镜像文件和一些模块文件。如果只编译Linux内核镜像的话,__build目标简化为:
  1. __build:$(builtin-target) $(lib-target) $(extra-y))$(subdir-ym) $(always)
  2.   @:
复制代码
       重点来看一下builtin-target这个依赖,builtin-target同样定义在文件scripts/Makefile.build中,定义如下:
示例代码35.5.4.5scripts/Makefile.build代码段
  1. ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)
  2. builtin-target := $(obj)/built-in.o
  3. endif
复制代码
       87行就是builtin-target变量的值,为“$(obj)/built-in.o”,这就是这些built-in.o的来源了。要生成built-in.o,要求obj-yobj-mobj-subdir-mlib-target这些变量不能全部为空。最后一个问题:built-in.o是怎么生成的?在文件scripts/Makefile.build中有如下代码:
示例代码35.5.4.6 顶层Makefile代码段
  1. #
  2. # Rule to compilea set of .o files intoone .o file
  3. #
  4. ifdef builtin-target
  5. quiet_cmd_link_o_target = LD      $@
  6. # If the list ofobjects to link is empty, just create an empty built-in.o
  7. cmd_link_o_target = $(if $(strip $(obj-y)),\
  8.                $(LD) $(ld_flags)-r -o $@ $(filter $(obj-y), $^) \
  9.                $(cmd_secanalysis),\
  10.                rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@)

  11. $(builtin-target): $(obj-y) FORCE
  12.      $(call if_changed,link_o_target)

  13. targets += $(builtin-target)
  14. endif # builtin-target
复制代码
       336行的目标就是builtin-target,依赖为obj-y,命令为“$(call if_changed,link_o_target)”,也就是调用函数if_changed,参数为link_o_target,其返回值就是具体的命令。前面讲过了if_changed,它会调用cmd_$(1)所对应的命令($(1)就是函数的第1个参数),在在这里就是调用cmd_link_o_target所对应的命令,也就是第331~334行的命令。cmd_link_o_target就是使用LD将某个目录下的所有.o文件链接在一起,最终形成built-in.o
35.5.5 makezImage过程
1、vmlinux、Image,zImage、uImage的区别
       前面几小节重点是讲vmlinux是如何编译出来的,vmlinuxELF格式的文件,但是在实际中我们不会使用vmlinux,而是使用zImageuImage这样的Linux内核镜像文件。那么vmlinuxzImageuImage他们之间有什么区别呢?
       ①、vmlinux是编译出来的最原始的内核文件,是未压缩的,比如正点原子提供的Linux源码编译出来的vmlinux差不多有16MB,如图35.5.5.1所示:
image024.jpg
35.5.5.1 vmlinux信息
       ②、ImageLinux内核镜像文件,但是Image仅包含可执行的二进制数据。Image就是使用objcopy取消掉vmlinux中的一些其他信息,比如符号表什么的。但是Image是没有压缩过的,Image保存在arch/arm/boot目录下,其大小大概在12MB左右如图35.5.5.2所示:
image026.jpg
35.5.5.2 Image镜像信息
       相比vmlinux16MBImage缩小到了12MB
       ③、zImage是经过gzip压缩后的Image,经过压缩以后其大小大概在6MB左右,如图35.5.5.3所示:
image028.jpg
35.5.5.3 zImage镜像信息
       ④、uImage是老版本uboot专用的镜像文件,uImag是在zImage前面加了一个长度为64字节的“头”,这个头信息描述了该镜像文件的类型、加载位置、生成时间、大小等信息。但是新的uboot已经支持了zImage启动!所以已经很少用到uImage了,除非你用的很古老的uboot
       使用“make”、“makeall”、“makezImage”这些命令就可以编译出zImage镜像,在arch/arm/Makefile中有如下代码:
示例代码35.5.5.1 顶层Makefile代码段
  1. BOOT_TARGETS    = zImage Image xipImage bootpImage uImage
  2. ...
  3. $(BOOT_TARGETS): vmlinux
  4.      $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
复制代码
       310行,变量BOOT_TARGETS包含zImageImagexipImage等镜像文件。
       315行,BOOT_TARGETS依赖vmlinux,因此如果使用“makezImage”编译的Linux内核的话,首先肯定要先编译出vmlinux
       316行,具体的命令,比如要编译zImage,那么命令展开以后如下所示:
  1. @ make -f ./scripts/Makefile.buildobj=arch/arm/boot MACHINE=arch/arm/boot/zImage
复制代码
       看来又是使用scripts/Makefile.build文件来完成vmliuxzImage的转换。

    关于Linux顶层Makefile就讲解到这里,基本和uboot的顶层Makefile一样,重点在于vmlinux的生成。最后将vmlinux压缩成我们最常用的zImageuImage等文件。

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-25 03:24

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表