嵌入式技术william hill官网
直播中

正点原子运营官

5年用户 1793经验值
擅长:模拟技术 嵌入式技术 控制/MCU
私信 关注

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

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同样为"."。

更多回帖

发帖
×
20
完善资料,
赚取积分