嵌入式 Linux 移植与系统启动方法
1、Linux系统启动与U-Boot
所谓移植就是把程序代码从一种运行环境转移到另一种运行环境。对于内核移植来说,主要是从一种硬件平台转移到另一种硬件平台上运行。
体系结构级别的移植是指在不同体系结构平台上Linux内核的移植,例如,在ARM、MIPS、PPC等不同体系结构上分别都要对每个体系结构进行特定的移植工作。一个新的体系结构出现就需要进行这个层次上的移植。
SoC级别的移植是指在具体的SoC处理器平台上Linux内核的移植,例如,ARM i.MX6Dual处理器要进行SoC特定的移植工作,主要包括处理器相关的内核修改、集成外设驱动。
主板级别的移植是指在具体的目标主板上Linux内核的移植,例如,在i.MX6DualFS2410目标板上,需要进行主板特定的移植工作,主要包括特定目标板系统启动与主板扩展外设相关的外设驱动等。
基于同一款处理器的不同嵌入式设备并不是所有的外部设备都相同,不同的开发板可以使用不同的SDRAM、Flash、以太网接口芯片等。这就需要根据硬件修改或者开发驱动程序。
一个最基本的Linux操作系统应该包括:引导程序、内核与根文件系统三部分。因此,需要移植一个Linux系统的话,那么需要以下4个步骤:
-
搭建交叉开发环境;
-
BootLoader的选择和移植;
-
kernel的配置、编译、和移植;
-
根文件系统的制作。
(1)BootLoader简介
引导加载程序(Boot Loader)就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
如下图所示:
为什么系统移植之前要先移植BootLoader?
BootLoader的任务是引导操作系统,所谓初始化CPU运行环境,引导操作系统。就是启动内核,让内核运行就是把内核加载到内存RAM中去运行。
是谁把CPU运行环境初始化的?
是谁把内核搬到内存中去运行?
SRAM只要系统上电就可以运行,而SDRAM需要软件进行初始化才能运行,那么内存是由谁来初始化的呢?
(2)Bootloader的执行过程
uboot 启动流程详细分析
-
初始化SDRAM;
-
初始化串口;
-
检测处理器类型;
-
设置Linux启动参数;
-
调用Linux内核映像。
(3)BootLoader的分类
很多人说BootLoader就是U-Boot,这种说法是错误的,确切来说是U-Boot是BootLoader的一种。u-boot和bootloader到底有什么区别。
如下图所示。
可以知道如果使用开发板board/<board_name>,就先执行“make <board_name>_config”命令进行配置,然后执行“make all”, 就可以生成如下3个文件:
-
u-boot.bin:二进制可执行文件,它就是可以直接烧入eMMC中的文件。
-
u-boot: ELF格式的可执行文件。
-
u-boot.srec: 原摩托罗拉格式的可执行文件。
对于imx6dlsabresd开发板,可以根据开发板的型号选择执行“make imx6dlsabresd_defconfig”、“make"进行编译。
编译后生成的u-boot.imx镜像可以烧入SD卡中执行,具体命令如下:
sudo dd if=u-boot.imx of=/dev/sdb bs=512 seek=2
其中sdb代表SD卡在系统中对应的设备。
(4)u-boot配置过程
在配置之前,为了使编译后的u-boot在开发板上运行,首先需要安装交叉编译链。并使用如下命令配置环境变量:
source /opt/fsl-imx-fb/4.14-sumo/environment-setup-cortexa9hf-neon-poky- linux-gnueabi
u-boot启动流程分析如下
第一阶段:(注明下每个步骤的作用)
a -- 设置cpu工作模式为SVC模式。需要SVC权限对CPU的状态寄存器进行操作
b -- 关闭中断,mmu,,cache。需要直接物理地址访问
v -- 关看门狗
d -- 初始化内存,串口。用于初始化基本的存储空间与通信接口,用于启动信息交互
e -- 设置栈。用于系统启动临时数据交换和初始化栈指针
f -- 代码自搬移。用于拷贝系统启动代码
g -- 清bss
h -- 跳c
第二阶段:
a -- 初始化外设,进入超循环
b -- 超循环处理用户命令
函数前期执行流程如下:
1)_start(arch/arm/lib/vector.S)
b reset
2)reset(arch/arm/cpu/$CPU/start.S)
bl cpu_init_cp15
bl cpu_init_crit
bl _main
3)_main(arch\arm\lib\crt0.S)
board_init_f(common\Board_f.c)
b relocate_code
ldr lr, =board_init_r(common/Board_r.c)
4)main_loop()
5)启动内核:
main_loop->autoboot_command->run_command_list->cli_simple_run_ command_list->cli_simple_run_command->cmd_process->find_cmd/cmd_call(result=(cmdtp->cmd)(cmdtp, flag, argc, argv))
uboot的最终目的是引导内核,在此之前uboot需要完成一系列初始化操作,包括设置时钟、初始化DDR、Flash、串口、网卡等等。
这时uboot有两条路走
(1)通过按键,触发uboot进入命令行模式,等待处理命令。
(2)引导内核
2、Linux系统裁剪与移植
内核编译相关文件主要包括顶层Makefile与子目录下的Makefile、各级目录Kconfig文件。
a--在内核配置之前先使用make mrproper命令清除以前的内核。
b--详细配置 make menuconfig
c--编译
make zImage ---生成内核镜像 /arch/arm/boot/zImage
make dtbs ---生成设备树文件 /arch/arm/boot/dts/imx6dl-sabresd.dtb
make modules ---把配置值选成M的代码编译生成模块文件。(.ko) 放在对应的源码目录下。
可以看出,内核编译主要包括两部分:一部分是内核配置;另一部分是内核编译。如下图所示。
内核的Kconfig分析:
a -- 我们解压内核后需要先修改内核顶层目录下的Makefile,配置好交叉编译工具
b -- 然后导入默认配置(使用make imx_v7_defconfig 或者
cp arch/arm/configs/imx_v7_deconfig .config)
c -- 配置内核
如下图所示。
内核中的哪些文件将被编译?它们是怎样被编译的?它们连接时的顺序如何确定?哪个文件在最前面?哪些文件或函数先执行?这些都是通过Makefile来管理的。
从最简单的角度来总结Makefile的作用,有以下3点:
1)-- 决定编译哪些文件?
2)-- 怎样编译这些文件?
3)-- 怎样连接这些文件,最重要的是它们的顺序如何?
1)顶层Makefile 决定内核根目录下哪些子目录将被编进内核;
2)arch/$(ARCH)/Makefile 决定arch/$(ARCH)目录下哪些文件、哪些目录将被编进内核;
3)各级子目录下的Makefile决定所在目录下哪些文件将被编进内核,哪些文件将被编程模块(即驱动程序),进入哪些子目录继续调用它们的Makefile。
与移植U-Boot的过程相似,在移植Linux之前,先了解它的启动过程。
Linux 的过程可以分为两部分:架构/开发板相关的引导过程、后续的通用启动过程。对于uImage、zImage ,它们首先进行自解压得到vmlinux ,然后执行 vmlinux 开始“正常的”启动流程。
引导阶段通常使用汇编语言编写,它首先检查内核是否支持当前架构的处理器,然后检查是否支持当前开发板。
通过检查后,就为调用下一阶段的start_kernel函数作准备了。
这主要分如下两个步骤:
1) 连接内核时使用的虚拟地址,所以要设置页表、使能MMU;
2)调用C 函数 start_kernel 之前的常规工作,包括复制数据段、清除BSS段、调用start_kernel 函数。
第二阶段的关键代码主要使用C语言编写。
它进行内核初始化的全部工作,最后调用 rest_init 函数启动init 过程,创建系统第一个进程:init 进程。在第二阶段,仍有部分架构/开发板相关的代码,比如重新设置页表、设置系统时钟、初始化串口等。
内核自解压阶段:
Linux内核有两种映像:一种是非压缩内核,叫Image,另一种是它的压缩版本,叫zImage。根据内核映像的不同,Linux内核的启动在开始阶段也有所不同。
zImage是Image经过压缩形成的,所以它的大小比Image小。但为了能使用zImage,必须在它的开头加上解压缩的代码,将zImage解压缩之后才能执行,因此它的执行速度比Image要慢。
但考虑到嵌入式系统的存储空容量一般比较小,采用zImage可以占用较少的存储空间,因此牺牲一点性能上的代价也是值得的。所以一般的嵌入式系统均采用压缩内核的方式。
内核自解压阶段依次完成以下工作:开启MMU和Cache,调用decompress_kernel()解压内核,最后通过调用call_kernel()进入非压缩内核Image的启动。
内核引导阶段:
内核引导阶段是内核启动第一阶段,该部分代码实现在arch/arm/kernel的 head.S中,该文件中的汇编代码通过查找处理器内核类型和机器码类型调用相应的初始化函数,再建 立页表,最后跳转到start_kernel()函数开始内核的初始化工作。如下图所示:
内核初始化阶段:
Linux内核启动的第二阶段从start_kernel()函数开始。start_kernel()是所有Linux平台进入系统内核初始化后的入口函数,它主要完成剩余的与硬件平台相关的初始化工作,在进行一系列与内核相关的初始化后,调用第一个用户进程-init进程并等待用户进程的执行,这样整个Linux内核便启动完毕。
BusyBox初始化阶段:
除了基本的命令之外,BusyBox还支持init功能,如同其它的init一样,busybox的init也是完成系统的初始化工作,关机前的工作等等。
BusyBox的init进程会依次进行以下工作:
-
为init设置信号处理过程;
-
初始化控制台;
-
分析 inittab文件,/etc/inittab;
-
执行系统初始化脚本,缺省情况下会使用/etc/init.d/rcS;
-
执行所有导致init暂停的inittab命令(动作类型:wait);
-
执行所有仅执行一次的inittab(动作类型:once)。
3、Linux根文件系统移植
(1)文件系统与根文件系统
根,可以理解为基础的意思。根文件系统是一种最基础的文件系统。
Linux系统也可以将磁盘或Flash等存储设备划分为若干个分区,在不同的分区存放不同类型的文件,在某个分区存放u-boot的可执行文件;在某个分区存放内核映像文件,在另一分区存放根文件系统映像文件等。
Linux也需要在一个分区上存放系统启动的必要文件,比如内核启动运行后的第一个程序(init进程)、用于挂接文件系统的脚本、给用户提供操作界面的shell程序,应用程序所要依赖的库等,这些必要的基本文件的集合称为根文件系统(一般也叫做rootfs)。
Linux系统启动后首先会挂载这个分区,这称为挂载(mount)根文件系统。其他分区上的所有目录、文件的集合,称为文件系统。
为什么需要根文件系统?
1)init进程的应用程序在根文件系统上;
2)根文件系统提供了根目录/;
3)内核启动后的应用层配置(etc目录)在根文件系统上。可以认为:发行版=内核+rootfs。
4)shell命令程序在根文件系统上。譬如ls、cd等命令。
因此,一套Linux体系,只有内核本身是不能工作的,必须要rootfs(上的etc目录下的配置文件、/bin与/sbin等目录下的shell命令,还有/lib目录下的库文件等)相配合才能工作。
(2)根文件系统概述
Linux中的根文件系统更像是一个文件夹或者叫做目录(特殊的文件夹),在这个目录里面会有很多的子目录。
根目录下和子目录中会有很多的文件,这些文件是Linux运行所必须的,比如库、常用的软件和命令、设备文件、配置文件等等。
根文件系统和Linux内核是分开的,单独的Linux内核是没法正常工作的,必须要搭配根文件系统。
(3)根文件系统的结构
Linux的根文件系统是采用级层式的树状目录结构,在此结构中的最上层是根目录“/”,然后在此目录下再创建其他的目录。树的根结点为根目录root。
其中:
1)/root 系统管理员的主目录
2)/bin 存放二进制可执行命令的目录
3)/boot 存放的是启动Linux时使用的一些核心文件,包括一些连接文件以及镜像文件。
4)/dev 存放设备文件的目录
5)/etc 存放系统管理和配置文件的目录
6)/home 用户主目录
7)/lib 存放动态链接共享库的目录
8)/sbin存放系统管理员使用的管理程序的目录
9)/mnt 系统提供这个目录是让用户临时挂载其他的文件系统
10)/proc 虚拟文件系统
11)/usr 最庞大的目录
12)/var某些大文件的溢出区
13)/tmp 公用的临时文件存储点
一般我们在Linux驱动开发的时候都是通过nfs挂载根文件系统的,当产品最终上市开卖的时候才会将根文件系统烧写到EMMC或者NAND中。
1)修改Makefile,添加编译器;
2)配置busybox ;
3)编译busybox ;
4)向根文件系统添加lib库(完成基本根文件系统);
5)创建其他文件夹;
6)完善根文件系统。
Linux系统挂载完根文件系统之后,就会执行init程序,创建init进程。执行过程大概如下图所示。
相关文章:

嵌入式 Linux 移植与系统启动方法
1、Linux系统启动与U-Boot 所谓移植就是把程序代码从一种运行环境转移到另一种运行环境。对于内核移植来说,主要是从一种硬件平台转移到另一种硬件平台上运行。 体系结构级别的移植是指在不同体系结构平台上Linux内核的移植,例如,在ARM、MI…...

代码随想录算法训练营|五十六天
回文子串 647. 回文子串 - 力扣(LeetCode) dp含义:表示区间内[i,j]是否有回文子串,有true,没有false。 递推公式:当s[i]和s[j]不相等,false;相等时,情况一,…...

基于django水果蔬菜生鲜销售系统
基于django水果蔬菜生鲜销售系统 摘要 基于Django的水果蔬菜生鲜销售系统是一种利用Django框架开发的电子商务平台,旨在提供高效、便捷的购物体验,同时支持水果蔬菜生鲜产品的在线销售。该系统整合了用户管理、产品管理、购物车、订单管理等核心功能&…...

【数据结构】快速排序算法你会写几种?
👦个人主页:Weraphael ✍🏻作者简介:目前正在学习c和算法 ✈️专栏:数据结构 🐋 希望大家多多支持,咱一起进步!😁 如果文章有啥瑕疵 希望大佬指点一二 如果文章对你有帮助…...

C#访问修饰符
C#中的访问修饰符用于控制类型成员(如字段、属性、方法等)的访问级别。以下是C#中常用的访问修饰符: public:公共访问级别,没有任何访问限制。在任何其他类或程序集中都可以访问标记为 public 的成员。 private&#…...

anaconda中安装pytorch和TensorFlow环境并在不同环境中安装kernel
❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️ 👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…...

记一次解决Pyqt6/Pyside6添加QTreeView或QTreeWidget导致窗口卡死(未响应)的新路历程,打死我都想不到是这个原因
文章目录 💢 问题 💢🏡 环境 🏡📄 代码💯 解决方案 💯⚓️ 相关链接 ⚓️💢 问题 💢 我在窗口中添加了一个 QTreeWidget控件 ,但是程序在运行期间,只要鼠标进入到 QTreeWidget控件 内进行操作,时间超过几秒中就会出现窗口 未响应卡死的 状态 🏡 环境 �…...

用照片预测人的年龄【图像回归】
在图像分类任务中,卷积神经网络 (CNN) 是非常强大的神经网络架构。 然而,鲜为人知的是,它们同样能够执行图像回归任务。 图像分类和图像回归任务之间的基本区别在于分类任务中的目标变量(我们试图预测的东西)不是连续…...

Fork项目新分支如何同步
这里以seata项目为示例: 一、添加Fork仓库的源仓库 git remote add seata gitgithub.com:seata/seata.git二、fetch git fetch seata...

Linux 常用压缩格式
Linux 常用压缩格式简介 Linux系统用户可以根据自己的需求选择合适的压缩工具来进行文件压缩和解压操作。Linux系统中常用的压缩软件都有相应的命令行工具,并且可以通过软件包管理器进行安装。主要有gzip、bzip2、zip、tar、7z。 gzip:gzip是一个广泛使…...

高效背单词——单词APP安利
大英赛,CET四六级,以及考研英语,都在不远的未来再度来临,年复一年的考试不曾停息,想要取得好成绩,需要我们的重视并赋予相应的努力。对于应试英语,词汇量是不可忽略的硬性要求。相比于传统默写&…...

力扣 字母异位词分组 哈表 集合
👨🏫 力扣 字母异位词分组 ⭐ 思路 由于互为字母异位词的两个字符串包含的字母相同,因此对两个字符串分别进行排序之后得到的字符串一定是相同的,故可以将排序之后的字符串作为哈希表的键。 🍑 AC code class Solut…...

⑩⑤【DB】详解MySQL存储过程:变量、游标、存储函数、循环,判断语句、参数传递..
个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客 学习社区:进去逛一逛~ MySQL存储过程 1. 介绍2. 使用3. 变量①系统变…...

使用SpringBoot进行游戏服务器开发
背景: 之前一直只考虑用JavaSe进行游戏服务器开发,目前项目使用了Spring,发现还是非常好的,好处如下: 好处1:依赖注入非常方便,我们只使用Spring最基本的功能即可,这样子就算是有一些模块不使用Spring管理…...

数据结构——树状数组
文章目录 前言问题引入问题分析树状数组lowbit树状数组特性初始化一个树状数组更新操作前缀和计算区间查询 总结 前言 原题的连接 最近刷leetcode的每日一题的时候,遇到了一个区间查询的问题,使用了一种特殊的数据结构树状数组,学习完之后我…...

Untiy 使用RotateAround()方法实现物体围绕某个点或者某个物体旋转
Untiy 实现物体围绕指定点或者某个物体旋转,可使用RotateAround()方法。 语法: public void RotateAround(Vector3 point, Vector3 axis, float angle); 其中,point:旋转中心点位置; axis:要围绕的轴,如x,y,z angel…...

图像分类(五) 全面解读复现ResNet
解读 Abstract—摘要 翻译 更深的神经网络往往更难以训练,我们在此提出一个残差学习的框架,以减轻网络的训练负担,这是个比以往的网络要深的多的网络。我们明确地将层作为输入学习残差函数,而不是学习未知的函数。我们提供了非…...

使用html2canvas转换table为图片时合并单元格rowspan失效,无边框显示问题解决(React实现)
最近使用 html2canvas导出Table表单为图片,但是转换出的图片被合并的单元格没有显示边框 查了原因是因为我为tr设置了背景色,然后td设置了rowspan,设置了rowspan的单元格就会出现边框不显示的问题。 解决方法就是取消tr的背景色,然…...

pandas教程:Time Series Basics 时间序列基础
文章目录 11.2 Time Series Basics(时间序列基础)1 Indexing, Selection, Subsetting(索引,选择,取子集)2 Time Series with Duplicate Indices(重复索引的时间序列) 11.2 Time Seri…...

【C++初阶】STL详解(四)vector的模拟实现
本专栏内容为:C学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:C 🚚代码仓库:小小unicorn的代码仓库&…...

Zookeeper学习笔记(2)—— Zookeeper API简单操作
前置知识:Zookeeper学习笔记(1)—— 基础知识-CSDN博客 Zookeeper集群搭建部分 前提:保证zookeeper集群处于启动状态 环境搭建 依赖配置 <dependencies><dependency><groupId>junit</groupId><arti…...

YOLOv8-Seg改进:Backbone改进 |Next-ViT堆栈NCB和NTB 构建先进的CNN-Transformer混合架构
🚀🚀🚀本文改进:Next-ViT堆栈NCB和NTB 构建先进的CNN-Transformer混合架构,包括nextvit_small, nextvit_base, nextvit_large,相比较yolov8-seg各个版本如下: layersparametersgradientsGFLOPsnextvit_small61033841075...

DocCMS keyword SQL注入漏洞复现 [附POC]
文章目录 DocCMS keyword SQL注入漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 DocCMS keyword SQL注入漏洞复现 [附POC] 0x01 前言 免责声明:请勿利用文章内的相关技术从事非法测…...

利用(Transfer Learning)迁移学习在IMDB数据上训练一个文本分类模型
1. 背景 有些场景下,开始的时候数据量很小,如果我们用一个几千条数据训练一个全新的深度机器学习的文本分类模型,效果不会很好。这个时候你有两种选择,1.用传统的机器学习训练,2.利用迁移学习在一个预训练的模型上训练…...

pom.xml格式化快捷键
在软件开发和编程领域,"格式化"通常指的是将代码按照一定的规范和风格进行排列,以提高代码的可读性和维护性。格式化代码有助于使代码结构清晰、统一,并符合特定的编码规范。 格式化可以包括以下方面: 缩进:…...

【短文】【踩坑】可以在Qt Designer给QTableWidge添加右键菜单吗?
2023年11月18日,周六上午 今天早上在网上找了好久都没找到教怎么在Qt Designer给QTableWidge添加右键菜单的文章 答案是:不可以 在Qt Designer中无法直接为QTableWidget添加右键菜单。 Qt Designer主要用于创建界面布局和设计,无法直接添加…...

Git常用配置
git log 美化输出 全局配置参数 git config --global alias.lm "log --no-merges --color --dateformat:%Y-%m-%d %H:%M:%S --authorghost --prettyformat:%Cred%h%Creset - %Cgreen(%cd)%C(yellow)%d%Cblue %s %C(bold blue)<%an>%Creset --abbrev-commit"…...

力扣每日一题-数位和相等数对的最大和-2023.11.18
力扣每日一题:数位和相等数对的最大和 开篇 这道每日一题还是挺需要思考的,我绕晕了好久,根据题解的提示才写出来。 题目链接:2342.数位和相等数对的最大和 题目描述 代码思路 1.创建一个数组存储每个数位的数的最大值,创建一…...

【win32_001】win32命名规、缩写、窗口
整数类型 bool类型 使用注意: 一般bool 的false0;true1 | 2 | …|n false是为0,true是非零 不建议这样用: if (result TRUE) // Wrong! 因为result不一定只返回1(true),当返回2时,…...

机器学习第8天:SVM分类
文章目录 机器学习专栏 介绍 特征缩放 示例代码 硬间隔与软间隔分类 主要代码 代码解释 非线性SVM分类 结语 机器学习专栏 机器学习_Nowl的博客-CSDN博客 介绍 作用:判别种类 原理:找出一个决策边界,判断数据所处区域来识别种类 简单…...