1、linux内核的特点
1. linux内核是完全开源的
作者:linus
git --> 代码版本管理工具
2. linux内核源码支持多种不同的架构,比如arm架构,powerPC,mips,Risc-V,X86等
3. linux内核采用模块化的编译的思想
4. 在linux内核中只允许出现C代码或者汇编代码
5. 在嵌入式设备开发中基本上使用的都是linux内核源码。
2、分析linux内核源码
1> 将linux-stm32mp-5.10.61-stm32mp-r2-r0.7z压缩包在windows下进行解压缩,解压缩之后得到linux-stm32mp-5.10.61-stm32mp-r2-r0文件夹,
linux-stm32mp-5.10.61-stm32mp-r2-r0.7z压缩包上传到qq群中。
2> 拷贝linux-stm32mp-5.10.61-stm32mp-r2-r0文件夹到ubuntu中,并cd到linux-stm32mp-5.10.61-stm32mp-r2-r0文件中
cd linux-stm32mp-5.10.61-stm32mp-r2-r0
3> 分析linux-stm32mp-5.10.61-stm32mp-r2-r0文件夹中的所有的文件
.
├── 0001-ARM-5.10.61-stm32mp1-r2-MACHINE.patch ---> linux内核的补丁文件
├── 0002-ARM-5.10.61-stm32mp1-r2-CLOCK.patch
├── 0003-ARM-5.10.61-stm32mp1-r2-CPUFREQ.patch
├── 0004-ARM-5.10.61-stm32mp1-r2-CRYPTO.patch
├── 0005-ARM-5.10.61-stm32mp1-r2-DMA.patch
├── 0006-ARM-5.10.61-stm32mp1-r2-DRM.patch
├── 0007-ARM-5.10.61-stm32mp1-r2-HWSPINLOCK.patch
├── 0008-ARM-5.10.61-stm32mp1-r2-I2C-IIO-IRQCHIP.patch
├── 0009-ARM-5.10.61-stm32mp1-r2-MAILBOX-REMOTEPROC-RPMSG.patch
├── 0010-ARM-5.10.61-stm32mp1-r2-MEDIA-SOC-THERMAL.patch
├── 0011-ARM-5.10.61-stm32mp1-r2-MFD.patch
├── 0012-ARM-5.10.61-stm32mp1-r2-MMC.patch
├── 0013-ARM-5.10.61-stm32mp1-r2-NET-TTY.patch
├── 0014-ARM-5.10.61-stm32mp1-r2-PERF.patch
├── 0015-ARM-5.10.61-stm32mp1-r2-PHY-USB.patch
├── 0016-ARM-5.10.61-stm32mp1-r2-PINCTRL-REGULATOR-SPI.patch
├── 0017-ARM-5.10.61-stm32mp1-r2-RESET-RTC-WATCHDOG.patch
├── 0018-ARM-5.10.61-stm32mp1-r2-SCMI.patch
├── 0019-ARM-5.10.61-stm32mp1-r2-SOUND.patch
├── 0020-ARM-5.10.61-stm32mp1-r2-MISC.patch
├── 0021-ARM-5.10.61-stm32mp1-r2-CPUIDLE-POWER.patch
├── 0022-ARM-5.10.61-stm32mp1-r2-DEVICETREE.patch
├── 0023-ARM-5.10.61-stm32mp1-r2-CONFIG.patch ---> linux内核的补丁文件
├── fragment-03-systemd.config ---> 碎片化的配置文件
├── fragment-04-modules.config ---> 碎片化的配置文件
├── fragment-05-signature.config ---> 碎片化的配置文件
├── linux-5.10.61.tar.xz ---> linux内核源码的压缩包
├── README.HOW_TO.txt ----> 帮助文件
└── series ---> 补丁文件列表信息
4> 分析README.HOW_TO.txt文件
1. 配置交叉编译器
CROSS_COMPILE=arm-ostl-linux-gnueabi-
2. 准备内核源码
对内恶化源码进行解压缩
$> tar xfJ linux-5.10.61.tar.xz
进入内核源码目录下
$> cd linux-5.10.61
对内核源码进行打补丁
$> for p in `ls -1 ../*.patch`; do patch -p1 < $p; done
3. 配置内核源码
方式1:
配置内核源码时将配置过程生成的中间文件指定到内核源码同一级的目录build目录下,
通过make时给O=参数赋值来指定中间文件的存放路径
$ cd <directory to kernel source code>
$> mkdir -p ../build
$> make ARCH=arm O="$PWD/../build" multi_v7_defconfig fragment*.config
将碎片配置文件写到.config配置文件中
$ scripts/kconfig/merge_config.sh -m -r -O $PWD/../build $PWD/../build/.config ../fragment-01-xxx.config
$ scripts/kconfig/merge_config.sh -m -r -O $PWD/../build $PWD/../build/.config ../fragment-02-xxx.config
...
$ yes '' | make ARCH=arm oldconfig O="$PWD/../build"
或者使用for循环的方式将碎片的配置文件信息写到.config文件中
$> for f in `ls -1 ../fragment*.config`; do scripts/kconfig/merge_config.sh -m -r -O $PWD/../build $PWD/../build/.config $f; done
$> yes '' | make ARCH=arm oldconfig O="$PWD/../build"
方式2:
将配置过程生成的中间文件,放到内核源码目录下:
$ cd <directory to kernel source code>
$ make ARCH=arm multi_v7_defconfig fragment*.config
将碎片配置文件写到.config配置文件中
$ scripts/kconfig/merge_config.sh -m -r .config ../fragment-01-xxxx.config
$ scripts/kconfig/merge_config.sh -m -r .config ../fragment-02-xxxx.config
...
$ yes '' | make oldconfig
或者使用for循环的方式将碎片的配置文件信息写到.config文件中
$ for f in `ls -1 ../fragment*.config`; do scripts/kconfig/merge_config.sh -m -r .config $f; done
$ yes '' | make ARCH=arm oldconfig
4. 编译内核源码
方式1:
将编译内核源码过程产生的中间文件,放到build指定的目录下,
通过执行make时给O=参数指定一个编译的路径即可
$ cd <directory to kernel source code>
编译内核源码生成uImage和vmlinux和设备树文件
$> make ARCH=arm uImage vmlinux dtbs LOADADDR=0xC2000040 O="$PWD/../build"
模块化编译内核源码,对内核中模块化的驱动进行编译
$> make ARCH=arm modules O="$PWD/../build"
方式2:
编译内核源码,将编译内核源码过程生成的中间文件放到内核源码目录下
$ cd <directory to kernel source code>
编译内核源码生成uImage和vmlinux和设备树文件
$ make ARCH=arm uImage vmlinux dtbs LOADADDR=0xC2000040
模块化编译内核源码,对内核中模块化的驱动进行编译
$ make ARCH=arm modules
3、正式开始对linux内核源码进行配置和编译
1> 拷贝linux-stm32mp-5.10.61-stm32mp-r2-r0文件夹到ubuntu中,并cd到linux-stm32mp-5.10.61-stm32mp-r2-r0文件中
$ cd linux-stm32mp-5.10.61-stm32mp-r2-r0
2> 对linux内核源码压缩包进行解压缩,并进入内核源码目录下
$ tar -vxf linux-5.10.61.tar.xz ^C
$ cd linux-5.10.61/
3> 分析linux内核源码目录下的所有的文件
4> 执行make help获取make的帮助信息
1. 清除目标文件
make clean
make mrproper
make distclean
2. 图形化界面配置内核
make menuconfig
3. 配置内核
make <board_name>_defconfig
4. 编译内核源码
make uImage
make vmlinux
make dtbs
make modules
5> 打开内核源码目录下的Makefile文件,配置交叉编译器
将以下内容:
370 ARCH ?= $(SUBARCH)
修改为:
370 ARCH ?= arm
371 CROSS_COMPILE ?= arm-linux-gnueabihf-
6> 对内核源码进行打补丁
$ for p in `ls -1 ../*.patch`; do patch -p1 < $p; done
7> 配置内核源码生成.config的配置文件
$ make multi_v7_defconfig fragment*.config
$ for f in `ls -1 ../fragment*.config`; do scripts/kconfig/merge_config.sh -m -r .config $f; done
$ yes '' | make oldconfig
8> 根据DK1板子的设备树文件拷贝生成FSMP1A板子的设备树文件
1. 使用ls arch/arm/boot/dts/*stm32mp15* 查看DK1板子的设备树文件
arch/arm/boot/dts/stm32mp157a-dk1.dts
arch/arm/boot/dts/stm32mp15xx-dkx.dtsi
2. 根据DK1板子的设备树文件拷贝生成fsmp1a板子的设备树文件
cd arch/arm/boot/dts/
cp stm32mp157a-dk1.dts stm32mp157a-fsmp1a.dts
cp stm32mp15xx-dkx.dtsi stm32mp15xx-fsmp1x.dtsi
cd ../../../../
3. 修改arch/arm/boot/dts/目录下的stm32mp157a-fsmp1a.dts
将以下内容:
13 #include "stm32mp15xx-dkx.dtsi"
14
15 / {
16 model = "STMicroelectronics STM32MP157A-DK1 Discovery Board";
17 compatible = "st,stm32mp157a-dk1", "st,stm32mp157";
修改为:
13 #include "stm32mp15xx-fsmp1x.dtsi"
14
15 / {
16 model = "STMicroelectronics STM32MP157A-fsmp1a Discovery Board";
17 compatible = "st,stm32mp157a-fsmp1a", "st,stm32mp157";
4. 修改arch/arm/boot/dts/目录下的Makefile文件,添加fsmp1a的设备树的编译的信息
1096 stm32mp157a-dhcor-avenger96.dtb \
1097 stm32mp157a-dk1.dtb \
# 以下行是添加的关于fsmp1a设备树的编译的信息
1098 stm32mp157a-fsmp1a.dtb \
9> 编译linux内核源码
make -j4 uImage vmlinux dtbs LOADADDR=0xC2000000
或者是(要想生成uImage镜像文件必须先生成vmlinux文件)
make -j4 uImage dtbs LOADADDR=0xC2000000
或者
make -j4 uImage LOADADDR=0xC2000000
make dtbs
10> 内核源码编译成功之后生成uImage镜像文件,使用开发阶段启动方式测试uImage和内核的设备树文件
1. 编译linux内核成功之后,内核镜像文件和设备树文件的路径
linux内核镜像文件uImage的路径为:arch/arm/boot/uImage
stm32mp157a-fsmp1a.dtb的设备树文件路径为:arch/arm/boot/dts/stm32mp157a-fsmp1a.dtb
2. 使用开发阶段系统的启动方式测试自己编译的内核及设备树文件
2.1>拷贝uImage和stm32mp157a-fsmp1a.dtb文件到~/tftpboot目录下
cp arch/arm/boot/uImage ~/tftpboot
cp arch/arm/boot/dts/stm32mp157a-fsmp1a.dtb ~/tftpboot
2.2> 启动开发板设置u-boot的启动参数,bootcmd,和bootargs
如果bootcmd和bootargs参数设置为开发阶段的启动参数可以忽略此步骤
2.3> 启动开发板,分析linux内核是否可以启动成功。linux内核启动失败,如下图。
4、解决linux内核启动不成功的错误
错误的原因:FSMP1A板子的设备树文件是基于DK1板子的设备树文件进行拷贝的,
DK1板子采用的是电源管理单元,FSMP1A板子采用的是独立电源,部分硬件是由差异的,
因此需要对FSMP1A板子的设备树文件进行修改,本次使用的设备树可以保证FSMP1A的板子
启动,后续根据需要在对设备树中的信息进行添加即可。
1> 修改好的linux内核的设备树文件.7z 压缩包下载之后在window系统中进行解压缩
修改好的linux内核的设备树文件.7z 的压缩文件上传到有道云笔记。
2> 拷贝<修改好的linux内核的设备树文件>文件夹中设备树文件到linux内核源码的arch/arm/boot/dts目录下
自行拷贝
3> 重新对设备树文件进行编译,不需要重新编译生成uImage镜像文件
删除原有生成的.dtb文件
rm arch/arm/boot/dts/stm32mp157a-fsmp1a.dtb
在执行编译设备树的命令
make dtbs
4> 拷贝和stm32mp157a-fsmp1a.dtb文件到~/tftpboot目录下
cp arch/arm/boot/dts/stm32mp157a-fsmp1a.dtb ~/tftpboot
5> 启动开发板,linux内核可以启动成功,但是出现以下错误。
5、解决hotplug的错误
1> 执行make menuconfig对内核源码进行配置
Device Drivers --->
Generic Driver Options --->
[*] Support for uevent helper # 此选项配置为*
(hotplug) path to uevent helper # 回车进入输入界面,输入hotplug
配置完成之后保存退出。
2> 编译linux内核源码
make -j4 uImage LOADADDR=0xC2000000
3> 内核源码编译成功之后生成uImage镜像文件,拷贝文件到~/tftpboot目录下
cp arch/arm/boot/uImage ~/tftpboot
4> 重启开发板进行测试,此时hotplug错误解决。
6、根据.config文件生成fsmp1a板子的默认配置文件
1. 根据.config文件拷贝生成fsmp1a板子的默认配置文件
cp .config arch/arm/configs/stm32mp15_fsmp1a_defconfig
2. 编写内核源码的使用的手册
1> 拷贝linux内核源码压缩包到ubuntu中
2> 对linux源码进行解压缩
3> 进入linux内核源码目录下
4> 配置交叉编译器
5> 执行make distclean清除中间文件
6> 执行make stm32mp15_fsmp1a_defconfig生成板子的配置文件.config
7> 执行make menuconfig,对linux内核源码进行配置
8> 编译linux内核源码生成uImage镜像文件:make -j4 uImage LOADADDR=0xc2000000
9> 编译fsmp1a板子的设备树文件:make dtbs
10> 拷贝uImage和stm32mp157a-fsmp1a.dtb文件到~/tftpboot目录下
11> 使用开发板阶段开发启动方式或者产品阶段的启动方式,启动开发板。
开发阶段开发板启动方式和产品阶段开发板启动方式具体的步骤,参考第二天的笔记
7、分析make <board_name>_defconfig执行过程详解
1. 打开linux内核源码目录下Makefile文件,搜索“%config”字符串,得到以下信息:
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
将$(Q),$(MAKE),$(build)变量的值使用echo命令打印:
执行make stm32mp15_fsmp1a_defconfig,分析打印的结果,
make -f ./scripts/Makefile.build obj=scripts/kconfig stm32mp15_fsmp1a_defconfig
解析:
make : 执行的make命令
-f ./scripts/Makefile.build : -f后边的文件看出一个Makefile文件
obj=scripts/kconfig : 执行make命令时给obj变量赋值
stm32mp15_fsmp1a_defconfig : make命令对应的目标
最终实现的效果是:在scripts/kconfig目录下有一个Makefile文件,
找Makefile文件中的目标为stm32mp15_fsmp1a_defconfig的规则下的命令执行。
2. 打开scripts/kconfig目录下Makefile文件,搜索“_defconfig”字符串,得到以下信息:
%_defconfig: $(obj)/conf
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
将命令前边的$(Q)去除,重新执行make stm32mp15_fsmp1a_defconfig,查看命令的结果:
scripts/kconfig/conf --defconfig=arch/../configs/stm32mp15_fsmp1a_defconfig Kconfig
解析:
scripts/kconfig/conf -----> 使用file命令查看conf文件的属性可知
conf文件是一个elf的可执行文件
--defconfig=arch/arm/configs/stm32mp15_fsmp1a_defconfig --->
arch/arm/configs/目录下板子的默认的配置文件
conf可执行程序的第一个参数
Kconfig ----> linux内核源码目录下的Kconfig文件
conf可执行程序的第二个参数
conf程序解析arch/arm/configs/stm32mp15_fsmp1a_defconfig和
linux内核源码目录下的Kconfig文件,
最终在linux内核源码目录下生成.config文件。
3. 执行make <board_name>_defconfig命令会解析arch/arm/configs目录下的默认的配置文件,
最终在linux内核源码目录下生成.config配置文件。
.config文件给Makefile文件使用,Makefile文件根据.config文件中的配置信息,
最终决定了linux内核源码哪些文件被编译,哪些文件不被编译。
8、分析make menuconfig执行过程详解
1. 打开linux内核源码目录下Makefile文件,搜索“%config”字符串,得到以下信息:
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
将$(Q)去除,重新执行make menuconfig命令,分析打印的结果,
make -f ./scripts/Makefile.build obj=scripts/kconfig menuconfig
解析:
make : 执行的make命令
-f ./scripts/Makefile.build : -f后边的文件看出一个Makefile文件
obj=scripts/kconfig : 执行make命令时给obj变量赋值
menuconfig : make命令对应的目标
最终实现的效果是:在scripts/kconfig目录下有一个Makefile文件,
找Makefile文件中的目标为menuconfig的规则下的命令执行。
2. 打开scripts/kconfig目录下Makefile文件,搜索“menuconfig”字符串,得到以下信息:
menuconfig: $(obj)/mconf
$< $(silent) $(Kconfig)
以上命令的解析的结果为:
scripts/kconfig/mconf Kconfig
scripts/kconfig/mconf ---> 使用file命令查看mconf程序可知,
mconf是一个elf格式的可执行的程序。
Kconfig ---> linux内核源码目录下的Kconfig文件,作为mconf程序的第一个参数
最终的效果:mconf程序解析linux内核源码目录下的Kconfig文件,
最终调用图形化界面的库(ncurses),生成基于菜单选项的图形化配置界面。
3. 打开linux内核源码目录下的Kconfig文件
主菜单:主菜单可以包含子菜单和菜单选项,类似于windows窗口中菜单栏
子菜单:可以包含子子菜单和菜单选项
菜单选项:菜单选项为最后一级
1> mainmenu关键字
作用:修饰主菜单
格式:mainmenu "主菜单的名字"
2> menu关键字
作用:修饰子菜单
格式:
menu "子菜单的名字"
endmenu
3> bool关键字
作用:修饰菜单选项
格式:
bool "菜单选项的名字"
4> default关键字
作用:菜单选项的默认的状态
格式:default y/n
5> help关键字
作用:菜单选项对应的帮助信息
格斯:
help
帮助信息对应的描述字符串
6> config关键字
作用:修饰配置选项的
格式:config 配置选项的名字
注:配置选项的名字一般都大写,多个单词之间使用“_”分隔
当config对应的菜单选项被配置为[*],配置选项对应的信息,
"CONFIG_配置选项的名字=y"的信息被写到.config文件中。
当config对应的菜单选项被配置为[],配置选项对应的信息,
"# CONFIG_配置选项的名字 is not set"的信息被写到.config文件中。
.config文件中定义的很多变量给Makefile文件使用,Makefile文件根据
.config文件中变量的定义最终决定了那些.c文件被编译,哪些.c文件不被编译。
7> source关键字
作用:包含下一级目录下的Kconfig文件的
格式:source "Kconfig文件及路径"
9、分析<boar_name>_defconfig,.config,Kconfig,Makefile文件的关系及作用。
9.1 4个文件的作用
<baord_name>_defconfig : 板子指定的默认的配置文件
.config : 板子配置文件,给Makefile使用
Kconfig : 生成基于菜单选项的图形化配置界面的文件
Makefile : 工程配置和管理编译的文件