一、系统分层
移植的目的
不同架构的处理器指令集不兼容,即便是相同的处理器架构,板卡不同驱动代码也不兼容
Linux是一个通用的内核并不是为某一个特定的处理器架构或板卡设计的,所以从官方获取Linux源码后我们要先经过相应的配置使其与我们当前的硬件平台相匹配后才能进行编译和安装
系统移植过程
二、开发板启动过程
开发板上电后首先运行SOC内部iROM中固化的代码(BL0),这段代码先对基本的软硬件环境(时钟等...)进行初始化,然后再检测拨码开关位置获取启动方式,然后再将对应存储器中的uboot搬移到内存,然后跳转到uboot运行;
uboot开始运行后首先对开发板上的软硬件环境做进一步初始化,然后将linux内核、设备树(dtb)、根文件系统(rootfs)从外部存储器(或网络)搬移到内存,然后跳转到linux运行;
linux开始运行后先对系统环境做初始化,当系统启动完成后,Linux再从内存中(或网络)挂载根文件系统。
系统移植步骤:uboot移植、linux内核移植(包含设备树)和根文件系统移植。
三、交叉开发环节搭建
1.ubuntu网络环境配置
2.tftp服务器环境搭建
ntftp(Trivial File Transfer Protocol)即简单文件传输协议是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。端口号为69。
3.nfs服务器环境搭建
nnfs(Network File System)即网络文件系统,其基于UDP/IP使用nfs能够在不同计算机之间通过网络进行文件共享,能使使用者访问网络上其它计算机中的文件就像在访问自己的计算机一样。
nfs是网络文件系统,不是用来传东西的,是把嵌入式的根文件系统通过nfs放在网络上的某个介质中,nfs是通过以太网中的udp传送命令的。tftp是通过TCP/IP协议用来在客户机与服务器之间进行简单文件传输的协议。
四、uboot
uboot详细介绍:http://t.csdn.cn/mYHye
1.开发板启动过程
Bootloader
在操作系统运行之前运行的一小段代码,用于将软硬件环境初始化到一个合适的状态,为操作系统的加载和运行做准备(其本身不是操作系统)
Bootloader基本功能
-> 初始化软硬件环境
-> 引导加载linux内核
-> 给linux内核传参
-> 执行用户命令
注:bootloader是启动引导程序的统称,嵌入式linux常用的bootloader是uboot
常见的Bootloader
2.SD卡启动盘制作
五、uboot的使用
1.uboot模式
自启动模式:uboot启动后若没有用户介入,倒计时结束后会自动执行自启动
环境变量(bootcmd)中设置的命令(一般作加载和启动内核)
交互模式:倒计时结束之前按下任意按键uboot会进入交互模式,交互模式下
用户可输入uboot命令
2.uboot帮助命令
help:查看uboot支持的所有命令
help 命令:查看当前命令的使用方法
3.uboot环境变量命令
printenv:打印uboot中所有的环境变量
setenv:设置指定的环境变量(保存在RAM中)
setenv 环境变量 环境变量的值
saveenv:保存所有环境变量到EMMC中(保存到外存里,diao电不丢失)
4.uboot常用环境变量
ipaddr:uboot的IP地址
serverip:服务器的IP地址(即ubuntu的IP)
bootdelay:进入自启动模式之前倒计时的秒数
5.uboot网络传输命令
loadb:通过Kermit协议下载文件到指定的内存地址
loadb 地址
tftp:通过tftp协议下载文件到指定的内存地址
tftp 地址 文件名
注:使用tftp之前要配置好网络及tftp服务器
6.uboot存储器访问命令
mmc read:将EMMC中指定扇区中的内容读取到内存中指定的地址
mmc read <addr> <blk#> <cnt>
addr: 内存地址
blk#: EMMC中的扇区编号
cnt: 读取的扇区的个数
mmc write
将内存中指定地址中的内容写入到EMMC中指定的扇区
mmc write <addr> <blk#> <cnt>
7.uboot自启动环境变量
bootcmd
自启动的环境变量
该环境变量可以设置成一到多个uboot命令的集合(若有多个使用\;分割)
自启动模式下uboot就会按照bootcmd中命令的顺序逐条执行
eg:
setenv bootcmd tftp 40008000 interface.bin\;go 40008000
saveenv
bootm uboot内核启动命令
启动指定内存地址上的Linux内核并为内核传递参数bootm kernel-addr ramdisk-addr dtb-addr
注:
kernel-addr: 内核的下载地址
ramdisk-addr: 根文件系统的下载地址
dtb-addr: 设备树的下载地址
若不使用相应的地址,对应的位置写“-”
eg:
bootm 0x41000000 - 0x42000000
bootargs 自启动参数环境变量
eg:
setenv bootargs root=/dev/nfs nfsroot=xxx.xxx.xxx.xxx:/opt/4412/rootfs
rw console=ttySAC2,115200 init=/linuxrc ip=***.***.***.***
注:
root 根文件系统类型(nfs)
nfsroot 网络文件系统路径(xxx.xxx.xxx.xxx:/opt/4412/rootfs)
rw 操作网络文件系统的权限(rw)
console 控制台(使用串口2,波特率115200)
init init进程的位置(/linuxrc)
ip linux启动后自身的IP(***.***.***.***)
8.Linux 内核的安装与加载
通过 tftp 加载内核和根文件系统
set serverip 192.168.1.100
set ipaddr 192.168.1.200
set gatewayip 192.168.1.1
setenv bootcmd tftp 0x41000000 uImage\;tftp 0x42000000 exynos4412-fs4412.dtb\;tftp 0x43000000 ramdisk.img\;bootm 0x41000000 0x43000000 0x42000000
setenv bootargs=root=/dev/nfs nfsroot=192.168.100.200:/opt/4412/rootfs rw console=ttySAC2,115200 init=/linuxrc ip=192.168.100.230
通过 EMMC 加载内核和根文件系统
set serverip 192.168.1.100
set ipaddr 192.168.1.200
set gatewayip 192.168.1.1
setenv bootcmd 'mmc read 0 0x41000000 0x800 0x2000;mmc read 0 0x42000000 0x2800 0x800;mmc read 0 0x43000000 0x3000 0x2000;bootm 0x41000000 0x43000000 0x42000000'
setenv bootargs=root=/dev/nfs nfsroot=192.168.100.200:/opt/4412/rootfs rw console=ttySAC2,115200 init=/linuxrc ip=192.168.100.230
通过 tftp 加载内核通过 nfs 挂载根文件系统
set serverip 192.168.1.100
set ipaddr 192.168.1.200
set gatewayip 192.168.1.1
setenv bootcmd tftp 0x41000000 uImage\;tftp 0x42000000 exynos4412-fs4412.dtb\;bootm 0x41000000 - 0x42000000
setenv bootargs=root=/dev/nfs nfsroot=192.168.100.200:/opt/4412/rootfs rw console=ttySAC2,115200 init=/linuxrc ip=192.168.100.230
六、交叉编译工具链
1.交叉编译
机器码(二进制)是处理器能直接识别的语言,不同的机器码代表不同的运算指令,处理器能够识别哪些机器码是由处理器的硬件设计所决定的,不同的处理器机器码不同,所以机器码不可移植
汇编语言是机器码的符号化,即汇编就是用一个符号来代替一条机器码,所以不同的处理器汇编也不一样,即汇编语言也不可移植
C语言在编译时我们可以使用不同的编译器将C源码编译成不同架构处理器的汇编,所以C语言可以移植
机器码(二进制)是处理器能直接识别的语言,不同的机器码代表不同的运算指令,处理器能够识别哪些机器码是由处理器的硬件设计所决定的,不同的处理器机器码不同,所以机器码不可移植
汇编语言是机器码的符号化,即汇编就是用一个符号来代替一条机器码,所以不同的处理器汇编也不一样,即汇编语言也不可移植
C语言在编译时我们可以使用不同的编译器将C源码编译成不同架构处理器的汇编,所以C语言可以移植
交叉编译工具链
交叉编译工具链的获取:
1) 官网获取(不推荐,需要自己进行复杂配置与编译)
http://ftp.gnu.org/gnu/gcc/
2) BSP板级开发支持包(推荐)
samsung、全志...
交叉编译工具链的内容
1) 交叉编译工具
gcc/readelf/size/nm/strip/objcopy/objdump/addr2line
2) 库
ARM架构的库
2.ELF文件格式
ELF格式是Linux平台上应用最广泛的二进制工业标准之一
ELF格式的文件内包含了很多个段不同的段存储了不同的信息;因为ELF格式的文件要通过Linux系统的加载和管理才能运行,所以除了最基本的代码段和数据段之外,其中还存储了很多其它的信息,如符号表、调试信息等
ELF文件相关命令
file
file + 文件名 查看文件的详细信息
readelf
readelf -h + 文件名 列出elf文件的头部信息
readelf -a + 文件名 列出elf文件的所有信息
BIN文件格式
BIN
BIN文件一般是直接运行在CPU之上的可执行文件,文件内只包含了CPU能够直接识别和运行的指令和数据,不包含其它系统相关的信息
3.交叉编译工具链常用工具
size 列出目标文件每一段的大小以及总体的大小
size + 文件名
nm 列出目标文件中的符号表(标示符)
nm + 文件名
strip 丢弃目标文件中的符号
strip + 文件名
注:对于嵌入式开发,这个命令很重要
objdump 从目标文件中显示信息
eg:
objdump -d + 文件名 将目标文件反汇编(机器码->汇编)
objcopy 对目标文件进行复制和转换
eg:
objcopy --gap-fill=0xff -O binary a.out a.bin
将目标文件转换为bin格式
七、uboot源码配置编译
1.uboot源码结构
(1)uboot源码获取
uboot源码下载:http://www.denx.de/wiki/U-Boot/
uboot版本命名:前期:uboot-1.2.3 现在:uboot-2008.01
uboot版本选择:支持对应的硬件平台;相对成熟的版本(资料多)
(2)uboot特点
代码结构清晰
支持丰富的处理器与开发板,易于移植
支持丰富的用户命令
支持丰富的网络协议
支持丰富的文件系统
支持丰富的设备驱动
更新活跃、用户较多、资料丰富
开放源代码
较高的稳定性
不具有通用性(不同的处理器、开发板uboot不可通用)
(3)uboot源码结构
平台相关代码
即与CPU架构或开发板硬件相关的源码,硬件的改动对应的代码也需要进行修改
arch:与CPU架构相关的源代码
board:与开发板相关的源代码,包含各种官方评估板对应的源码
平台无关代码
api: 应用接口
common: uboot命令源码
disk: 对磁盘设备的支持
drivers: 设备驱动源码
fs: 对文件系统的支持
include: 头文件
lib: 库
net: 对网络协议的支持
post: 上电自检程序
... ...
配置文件、帮助文档、示例程序、工具等:
README: 说明文档
doc: 帮助文档
Makefile: 编译管理
CREDITS: 开发者
COPYING: 版权
examples: 示例程序
tools: 工具
... ...
2.uboot的配置与编译
(1)uboot配置
指定当前使用的硬件平台
make <board_name>_config
注1:<board_name>为当前使用的开发板的名字
注2:执行该命令的前提是uboot源码支持该开发板
注3:该命令必须在uboot源码的顶层目录下执行
指定编译uboot源码使用的编译器
在uboot源码顶层目录下的Makefile中指定(CROSS_COMPILE变量)
(2)uboot编译
编译uboot
make
注1:该命令必须在uboot源码的顶层目录下执行
注2:该命令执行后在uboot源码顶层目录下生成u-boot.bin
清除编译过程中生成的中间文件
make clean
make distclean
注1:该命令必须在uboot源码的顶层目录下执行
八、Linux内核移植
1.Linux内核概述
(1)内核与操作系统
内核是一个操作系统的核心,提供了操作系统最基本的功能,是操作系统工作的基础,决定着整个系统的性能和稳定性
操作系统是在内核的基础上添加了各种工具集、桌面管理器、库、shell、应用程序等
(2)Linux层次结构
(3)Linux内核特点
代码结构清晰、模块化设计
支持丰富的硬件平台
较高的稳定性
轻量化及较强的裁剪性
开放源代码
更新活跃、用户较多、资料丰富
支持丰富的网络协议
... ...
2.Linux内核源码结构
(1)Linux内核源码获取
Linux内核源码下载:https://www.kernel.org/
Linux内核版本命名:主版本号.次版本号.修订版本
Linux内核版本选择:支持对应的硬件平台;相对成熟的版本(资料多);稳定版本(次版本号为偶数的版本一般都是稳定版)
(2)Linux内核源码结构
平台相关代码
arch: 与CPU架构相关的源代码
平台无关代码
block:磁盘设备的支持 crypto:加密相关
drivers:设备驱动 firmware:固件
fs:文件系统 include:头文件
init:内核初始化 ipc:进程间通信
kernel:内核核心调度机制等 lib:库
mm:内存管理 net:网络协议
scripts:工具、脚本等 security:安全
usr:打包与压缩 virt:虚拟
帮助文档、示例程序、工具等
COPYING: 版权
CREDITS: 内核贡献者
README: 说明文档
Documentation: 帮助文档
Makefile: 编译管理
samples: 示例
tools: 工具
... ...
3.Linux内核的配置与编译
(1)Linux内核源码配置
指定处理器架构及编译工具
在Linux内核源码顶层目录下的Makefile中指定(ARCH、CROSS_COMPILE)
导入当前处理器的默认配置
make <soc_name>_defconfig
注1:soc_name为当前使用的处理器的名字
注2:内核源码的arch/arm/configs下对各个厂商的soc都有一个默认配置文件,执行该命令后就会将对应的配置文件中的信息导入到源码顶层目录下的.config文件中,CONFIG_xxx=y表示内核选中了该功能,内核编译时就会将该功能对应的代码编译,内核的体积也会增大。#CONFIG_xxx is not set表示内核没有选中该功能,内核编译时该功能对应的代码不会被编译,内核的体积也会减小。
修改配置
默认配置只能保证内核拥有最基本的功能,我们需要根据自己的实际需求对内核做进一步的配置
方法1:直接修改.config文件(不推荐)
方法2:make menuconfig
make menuconfig
修改配置
[ ] 有两种状态
输入Y,显示“*”,内核中该功能被选中,相关代码会被编译进内核
输入N,显示“ ”,内核中该功能不被选中,相关代码不会被编译进内核
< > 有三种状态
输入Y,显示“*”,内核中该功能被选中,相关代码会被编译进内核
输入N,显示“ ”,内核中该功能不被选中,相关代码不会被编译进内核
输入M,显示“M”,内核中该功能被选为模块(被编译为独立的模块)
注:使用make menuconfig配置的本质还是修改.config文件
(2)Linux内核源码编译
内核编译(以下命令均在内核源码的顶层目录下执行)
make uImage 编译内核(编译选为“*”的选项到内核)
make modules 编译内核模块(编译选为“M”的选项为独立模块)
make dtbs 编译设备树(将设备树源文件dts编译为二进制文件dtb)
make clean 删除编译过程中产生的中间文件
九、设备树
1.设备树
设备树是一种描述硬件信息的数据结构,Linux内核运行时可以通过设备树将硬件信息直接传递给Linux内核,而不再需要在Linux内核中包含大量的冗余编码
2.设备树语法概述
(1)设备树文件
dts 设备树源文件
dtsi 类似于头文件,包含一些公共的信息,可被其它设备树文件引用
dtb 编译后的设备树文件
(2)设备树语法
设备树的语法为树状结构,由一系列的节点和属性组成,根节点下包含子节点
子节点下还可以包含子节点,节点内部包含了对应设备的属性
3.Linux内核驱动移植
在make menuconfig界面中选中要安装的驱动
在设备树中添加/修改相应的设备信息
重新编译内核/设备树
十、Linux内核调试及rootfs移植
1.根文件系统
根文件系统是内核启动后挂载的第一个文件系统系统引导程序会在根文件系统挂载后从中把一些基本的初始化脚本和服务等加载到内存中去运行
2.根文件系统内容
bin shell命令(elf格式)(通过busybox编译生成)
dev 设备文件(内核启动后会将设备信息写入该目录)
etc 内核配置文件
lib 共享库(elf格式)(从交叉编译工具链中获取)
linuxrc 内核运行的第一个应用程序(通过busybox编译生成)
mnt 挂载目录(非必要)
proc 进程相关文件(内核启动后会将进程信息写入该目录)
root 超级用户家目录(非必要)
sbin 系统管理shell命令(elf格式)(通过busybox编译生成)
sys 驱动相关文件(内核启动后会将驱动信息写入该目录)
usr shell命令(elf格式)(通过busybox编译生成)
3. BusyBox将很多常用的工具集成到一个很小的可执行文件中,为普通用户提供大多数常用的命令,BusyBox实现的命令都是精简版的,很多扩展都不支持。BusyBox被称为Linux工具里的瑞士军刀
BusyBox的获取:https://busybox.net/downloads/
使用BusyBox制作的Linux工具有哪些特点,为什么嵌入式领域一般都使用BusyBox?
BusyBox是一个开源项目,它提供了一个精简的Unix工具集合,适用于嵌入式系统和嵌入式Linux环境。以下是BusyBox的一些特点:
-
精简和轻量:BusyBox将多个常用的Unix工具(如ls、cp、grep等)合并为一个可执行文件,通过共享代码和函数来减少二进制文件的大小。这使得BusyBox成为嵌入式系统中的理想选择,因为它可以大大减小系统的存储空间和内存占用。
-
可配置性:BusyBox提供了许多可配置选项,可以根据嵌入式系统的需求选择性地编译所需的工具。这使得系统开发人员能够根据具体的应用场景和硬件要求,灵活地构建和定制自己的嵌入式Linux环境。
-
兼容性:BusyBox是以兼容POSIX标准为目标开发的,它提供了与传统Unix工具相似的接口和功能。这使得已经熟悉标准Unix工具的开发人员能够快速上手并在嵌入式系统中使用BusyBox提供的工具。
-
开源和活跃的社区支持:BusyBox是一个开源项目,拥有庞大的社区支持和活跃的开发团队。这意味着用户可以从社区中获得帮助、修复错误和安全漏洞,并及时获得新的功能和改进。
为什么嵌入式领域一般都使用BusyBox呢?
主要原因有以下几点:
-
资源限制:嵌入式系统通常具有有限的资源,包括存储空间和内存。BusyBox的精简和轻量特性使得它成为在资源受限的环境下高效利用资源的选择。
-
定制性:BusyBox提供了可配置选项,使得开发人员可以根据具体的需求和硬件平台选择所需的工具,从而定制自己的嵌入式Linux环境。这种定制性使得BusyBox适应性强,可以满足各种嵌入式系统的需求。
-
兼容性:BusyBox提供了与传统Unix工具相似的接口和功能,这使得已经熟悉标准Unix工具的开发人员能够无缝迁移到BusyBox提供的工具。这简化了开发人员的学习曲线,并提高了开发效率。
-
开源社区支持:BusyBox作为一个开源项目,拥有庞大的社区支持和活跃的开发团队。这意味着用户可以从社区中获得支持、修复错误和漏洞,并及时获得新的功能和改进。这为嵌入式系统开发者提供了一个可靠的资源和知识库。