专业的编程技术博客社区

网站首页 > 博客文章 正文

「正点原子Linux连载」第三十五章Linux内核顶层Makefile详解(一)

baijin 2024-08-08 23:01:23 博客文章 12 ℃ 0 评论


1)实验平台:正点原子Linux开发板

2)摘自《正点原子I.MX6U嵌入式Linux驱动开发指南

关注官方微信号公众号,获取更多资料:正点原子


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


35.1 Linux内核获取

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

图35.1.1 linux官网

从图35.1.1可以看出最新的稳定版Linux已经到了5.1.4,大家没必要追新,因为4.x版本的Linux和5.x版本没有本质上的区别,5.x更多的是加入了一些新的平台、新的外设驱动而已。

NXP会从https://www.kernel.org下载某个版本的Linux内核,然后将其移植到自己的CPU上,测试成功以后就会将其开放给NXP的CPU开发者。开发者下载NXP提供的Linux内核,然后将其移植到自己的产品上。本章的移植我们就使用NXP提供的Linux源码,NXP提供Linux源码已经放到了开发板光盘中,路径为:1、例程源码-》4、NXP官方原版Uboot和Linux-》linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2。

35.2 Linux内核编译初次编译

先看一下如何编译Linux源码,这里编译一下I.MX6U-ALPHA开发板移植好的Linux源码,已经放到了开发板光盘中,路径为:1、例程源码-》3、正点原子修改后的Uboot和Linux-》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文件夹中并解压,命令如下:

tar -vxjf linux-imx-4.1.15-2.1.0-g8a006db.tar.bz2

解压完成以后的Linux源码根目录如图35.2.1所示:

图35.2.1 正点原子提供的Linux源码根目录

以EMMC核心板为例,讲解一下如何编译出对应的Linux镜像文件。新建名为"mx6ull_alientek_emmc.sh"的shell脚本,然后在这个shell脚本里面输入如下所示内容:

示例代码35.2.1 mx6ull_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脚本,命令如下:

./mx6ull_alientek_emmc.sh

编译的时候会弹出Linux图形配置界面,如图35.2.3所示:

图35.2.3 Linux图形配置界面

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

图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所示:

图35.2.5 lzop未找到

图35.2.5中的错误提示lzop未找到,原因是没有安装lzop库,输入如下命令安装lzop库即可解决:

sudoapt-getinstalllzop

lzop库安装完成以后在重新编译一下Linux内核即可。

看一下编译脚本mx6ull_alientek_emmc.sh的内容,文件内容如下:

示例代码35.2.1 mx6ull_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所示:

图35.3.1未编译的Linux源码目录

图35.3.1就是正点原子提供的未编译的Linux源码目录文件,我们在分析Linux之前一定要先在Ubuntu中编译一下Linux,因为编译过程会生成一些文件,而生成的这些恰恰是分析Linux不可或缺的文件。编译完成以后使用tar压缩命令对其进行压缩并使用Filezilla软件将压缩后的uboot源码拷贝到Windows下。

编译后的Linux目录如图35.3.2所示:

图35.3.2 编译后的Linux目录

图35.3.2中重要的文件夹或文件的含义见表35.3.1所示:

表35.3.1 Linux目录

表35.3.1中的很多文件夹和文件我们都不需要去关心,我们要关注的文件夹或文件如下:

1、arch目录

这个目录是和架构有关的目录,比如arm、arm64、avr32、x86等等架构。每种架构都对应一个目录,在这些目录中又有很多子目录,比如boot、common、configs等等,以arch/arm为例,其子目录如图35.3.2所示:

图35.3.2 arch/arm子目录

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

图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所示:

图35.3.4 正点原子I.MX6U开发板对应的设备树

arch/arm/boot目录下会保存编译出来的Image和zImage镜像文件,而zImage就是我们要用的linux镜像文件。

arch/arm/mach-xxx目录分别为相应平台的驱动和初始化文件,比如mach-imx目录里面就是I.MX系列CPU的驱动和初始化文件。

2、block目录

block是Linux下块设备目录,像SD卡、EMMC、NAND、硬盘等存储设备就属于块设备,block目录中存放着管理块设备的相关文件。

3、crypto目录

crypto目录里面存放着加密文件,比如常见的crc、crc32、md4、md5、hash等加密算法。

4、Documentation目录

此目录里面存放着Linux相关的文档,如果要想了解Linux某个功能模块或驱动架构的功能,就可以在Documentation目录中查找有没有对应的文档。

5、drivers目录

驱动目录文件,此目录根据驱动类型的不同,分门别类进行整理,比如drivers/i2c就是I2C相关驱动目录,drivers/gpio就是GPIO相关的驱动目录,这是我们学习的重点。

6、firmware目录

此目录用于存放固件。

7、fs目录

此目录存放文件系统,比如fs/ext2、fs/ext4、fs/f2fs等,分别是ext2、ext4和f2fs等文件系统。

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/* 屏蔽不用的架构相关的文件 */

11"arch/alpha":true,

12"arch/arc":true,

13"arch/arm64":true,

14"arch/avr32":true,

15"arch/[b-z]*":true,

16"arch/arm/plat*":true,

17"arch/arm/mach-[a-h]*":true,

18"arch/arm/mach-[n-z]*":true,

19"arch/arm/mach-i[n-z]*":true,

20"arch/arm/mach-m[e-v]*":true,

21"arch/arm/mach-k*":true,

22"arch/arm/mach-l*":true,

23

24/* 屏蔽排除不用的配置文件 */

25"arch/arm/configs/[a-h]*":true,

26"arch/arm/configs/[j-z]*":true,

27"arch/arm/configs/imo*":true,

28"arch/arm/configs/in*":true,

29"arch/arm/configs/io*":true,

30"arch/arm/configs/ix*":true,

31

32/* 屏蔽掉不用的DTB文件 */

33"arch/arm/boot/dts/[a-h]*":true,

34"arch/arm/boot/dts/[k-z]*":true,

35"arch/arm/boot/dts/in*":true,

36"arch/arm/boot/dts/imx1*":true,

37"arch/arm/boot/dts/imx7*":true,

38"arch/arm/boot/dts/imx2*":true,

39"arch/arm/boot/dts/imx3*":true,

40"arch/arm/boot/dts/imx5*":true,

41"arch/arm/boot/dts/imx6d*":true,

42"arch/arm/boot/dts/imx6q*":true,

43"arch/arm/boot/dts/imx6s*":true,

44"arch/arm/boot/dts/imx6ul-*":true,

45"arch/arm/boot/dts/imx6ull-9x9*":true,

46"arch/arm/boot/dts/imx6ull-14x14-ddr*":true,

47},

48"files.exclude":{

49"**/.git": true,

50"**/.svn": true,

51"**/.hg": true,

52"**/CVS": true,

53"**/.DS_Store": true,

54"**/*.o":true,

55"**/*.su":true,

56"**/*.cmd":true,

57"Documentation":true,

58

59/* 屏蔽不用的架构相关的文件 */

60"arch/alpha":true,

61"arch/arc":true,

62"arch/arm64":true,

63"arch/avr32":true,

64"arch/[b-z]*":true,

65"arch/arm/plat*":true,

66"arch/arm/mach-[a-h]*":true,

67"arch/arm/mach-[n-z]*":true,

68"arch/arm/mach-i[n-z]*":true,

69"arch/arm/mach-m[e-v]*":true,

70"arch/arm/mach-k*":true,

71"arch/arm/mach-l*":true,

72

73/* 屏蔽排除不用的配置文件 */

74"arch/arm/configs/[a-h]*":true,

75"arch/arm/configs/[j-z]*":true,

76"arch/arm/configs/imo*":true,

77"arch/arm/configs/in*":true,

78"arch/arm/configs/io*":true,

79"arch/arm/configs/ix*":true,

80

81/* 屏蔽掉不用的DTB文件 */

82"arch/arm/boot/dts/[a-h]*":true,

83"arch/arm/boot/dts/[k-z]*":true,

84"arch/arm/boot/dts/in*":true,

85"arch/arm/boot/dts/imx1*":true,

86"arch/arm/boot/dts/imx7*":true,

87"arch/arm/boot/dts/imx2*":true,

88"arch/arm/boot/dts/imx3*":true,

89"arch/arm/boot/dts/imx5*":true,

90"arch/arm/boot/dts/imx6d*":true,

91"arch/arm/boot/dts/imx6q*":true,

92"arch/arm/boot/dts/imx6s*":true,

93"arch/arm/boot/dts/imx6ul-*":true,

94"arch/arm/boot/dts/imx6ull-9x9*":true,

95"arch/arm/boot/dts/imx6ull-14x14-ddr*":true,

96}

97}

创建好VSCode工程以后就可以开始分析Linux的顶层Makefile了。

35.5 顶层Makefile详解

Linux的顶层Makefile和uboot的顶层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代码段

16 MAKEFLAGS +=-rR --include-dir=$(CURDIR)

3、命令输出

Linux编译的时候也可以通过"V=1"来输出完整的命令,这个和uboot一样,相关代码如下所示:

示例代码35.5.3 顶层Makefile代码段

69 ifeq ("$(origin V)","command line")

70 KBUILD_VERBOSE = $(V)

71 endif

72 ifndef KBUILD_VERBOSE

73 KBUILD_VERBOSE =0

74 endif

75

76 ifeq ($(KBUILD_VERBOSE),1)

77 quiet =

78 Q =

79else

80 quiet=quiet_

81 Q = @

82 endif

4、静默输出

Linux编译的时候使用"make-s"就可实现静默编译,编译的时候就不会打印任何的信息,同uboot一样,相关代码如下:

示例代码35.5.4 顶层Makefile代码段

87 ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4

88 ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)

89 quiet=silent_

90 endif

91else # make-3.8x

92 ifneq ($(filter s%-s%,$(MAKEFLAGS)),)

93 quiet=silent_

94 endif

95 endif

96

97 export quiet Q KBUILD_VERBOSE

5、设置编译结果输出目录

Linux编译的时候使用"O=xxx"即可将编译产生的过程文件输出到指定的目录中,相关代码如下:

示例代码35.5.5 顶层Makefile代码段

116 ifeq ($(KBUILD_SRC),)

117

118 # OK, Make called in directory where kernel src resides

119 # Do we want to locate output files in a separate directory?

120 ifeq ("$(origin O)","command line")

121 KBUILD_OUTPUT := $(O)

122 endif

6、代码检查

Linux也支持代码检查,使用命令"make C=1"使能代码检查,检查那些需要重新编译的文件。"make C=2"用于检查所有的源码文件,顶层Makefile中的代码如下:

示例代码35.5.6 顶层Makefile代码段

172 ifeq ("$(origin C)","command line")

173 KBUILD_CHECKSRC = $(C)

174 endif

175 ifndef KBUILD_CHECKSRC

176 KBUILD_CHECKSRC =0

177 endif

7、模块编译

Linux允许单独编译某个模块,使用命令"make M=dir"即可,旧语法"make SUBDIRS=dir"也是支持的。顶层Makefile中的代码如下:

示例代码35.5.7 顶层Makefile代码段

179 # Use make M=dir to specify directory of external module to build

180 # Old syntax make ... SUBDIRS=$PWD is still supported

181 # Setting the environment variable KBUILD_EXTMOD take precedence

182 ifdef SUBDIRS

183 KBUILD_EXTMOD ?= $(SUBDIRS)

184 endif

185

186 ifeq ("$(origin M)","command line")

187 KBUILD_EXTMOD := $(M)

188 endif

189

190 # If building an external module we do not care about the all: rule

191 # but instead _all depend on modules

192 PHONY += all

193 ifeq ($(KBUILD_EXTMOD),)

194 _all: all

195else

196 _all: modules

197 endif

198

199 ifeq ($(KBUILD_SRC),)

200 # building in the source tree

201 srctree :=.

202else

203 ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))

204 # building in a subdirectory of the source tree

205 srctree :=..

206else

207 srctree := $(KBUILD_SRC)

208 endif

209 endif

210 objtree :=.

211 src := $(srctree)

212 obj := $(objtree)

213

214 VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))

215

216 export srctree objtree VPATH

外部模块编译过程和uboot也一样,最终导出srctree、objtree和VPATH这三个变量的值,其中srctree=.,也就是当前目录,objtree同样为"."。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表