系统移植

系统:操作系统,一个程序,一堆代码(管理对应资源和任务)

包含:Linux,window,uinx,freertos Linux操作系统

移植:将源代码(Linux操作系统源码)从一种环境(开发环境)下放到另一种环境(产品环境)下运行也可以称为移植。

一.启动流程

pc机

(1)启动BIOS(英文"Basic Input Output System"的缩略词,直译过来后中文名称就是"基本输入输出系统"。是一组固化到计算机内主板上一个ROM芯片上的程序,它保存着计算机最重要的基本输入输出的程序、开机后自检程序和系统自启动程序,它可从CMOS中读写系统设置的具体信息。其主要功能是为计算机提供最底层的、最直接的硬件设置和控制。)

1、初始化时钟,初始化内存,基本硬件的初始化
2、判断启动方式(usb/硬盘/光驱)

(2)引导程序
固化在硬盘(存储数据)最前面的部分
识别对应的操作系统
加载对应操作系统


(3)操作系统:(windows,linux,Mac OS,没有图形界面程序)
(4)文件系统(NTFS,FAT(16/32),window文件系统格式 日志文件系统)
(5)应用程序(桌面应用程序)

启动的流程:
首先pc机启动先启动的BIOS(系统时钟和内存)、选择对应的启动方式(usb、硬盘、光驱)。对应启动盘的最开始的程序就是引导程序(内存中),引导程序是首先识别有多少系统(内存中),如果只有一个系统直接加载系统,当有多个操作系系统时,会让你选择系统启动,系统起来之后,挂载文件系统,启动我们程序。操作系统是个非图形化界面的程序,桌面是对应的应用程序。

嵌入式设备启动流程

(1)厂家固化在IROM代码
1.基本硬件初始化(它需要的时钟和设备)
2.判断对应的启动方式(eMMC(Flash的一个变种)/USB/SD卡)
3.把Bootloader第一个阶段的代码加载IRAM(芯片内部运行内存(缺点芯片贵))
(2)在IRAM里面去运行对应的bootloader第一阶段的代码(SPL(Secondary Program Loader,二级程序加载器)第二阶段程序的加载)
1.初始化整个系统时钟
2.初始化内存
3.把完整的bootloader搬移到内存中

Bootloader分两个阶段运行的原因:芯片内部存储空间有限,如果内存空间做大,成本提高。

不在IROM中初始化内存的原因:因为芯片厂家不知道开发板采用什么样的内存,没有办法进行初始化。

(3)在内存运行bootloader第二个阶段的代码
1.初始化一下硬件设备(根据开发板去定义(网卡串口usb))
2.加载对应操作系统(内存)
(4)运行操作系统(没有图形界面的)
(5)挂载文件系统(EXT(2,3,4))
(6)启动应用程序

Bootloader=BIOS+引导程序。

二.U-boot和Linux 内核之间的参数传递

Linux内核是被U-Boot被动加载到内存中运行的,没有初始化过内存等硬件,对当前硬件环境一无所知,如果U-Boot不传递参数,内核无法正常运行。

(u_boot和Linux之间在传递参数的时候,是按照双方约定的格式传递,格式包含:1.struct parm__struct结构体,2.tag列表,3.设备树)

参数传递的规则

寄存器
r0规定为0,不传递有效信息
r1开发板的ID
r2其他参数在内存中开始地址

r1的参数,是让Linux内核自己去判断,它是否支持当前的开发板

r2是其他参数在内存中的开始地址,单个寄存器只有4个字节,无法存放大量参数,因此大量参数存放在内存中,将内存起始地址记录在r2。

r2存放的信息:

(1)内存的信息

内存的开始地址和内存的大小

(2)命令行参数:bootargs的内容

高诉Linux内核启动后,挂载文件系统的方式(例如:NFS方式)

(3)其他信息

bootcmd

u-boot启动完成读秒后,自动执行从参数(命令)

把文件从 TFTP 服务器加载到内存(RAM)(临时的),然后从内存启动

bootcmd=tftp 41000000 uImage;tftp 42000000 exynos4412-fs4412.dtb;tftp 43000000 ramdisk.img;bootm 41000000 43000000 42000000

uImage:是 U-Boot 引导加载程序专用的、经过特殊格式封装的 Linux 内核镜像文件。

exynos4412-fs4412.dtb:设备树文件。

ramdisk.img:自己的系统映象文件。

bootargs

setenv bootargs root=/dev/nfs nfsroot=192.168.2.5:/home/hqyj/fs4412/rootfs,proto=tcp,nfsvers=3,
console=ttySAC2,115200 init=/linuxrc,ip=192.168.2.29

root=/dev/nfs指定根文件系统(rootfs)不是从本地块设备(如 emmc/SD 卡)挂载,而是使用 NFS 网络文件系统。内核会因此启用 NFS 根文件系统支持。
nfsroot=192.168.2.5:/home/hqyj/fs4412/rootfs

192.168.2.5:linux的IP地址

/home/hdyj/fs4412/rootfs:主机上存放根文件系统的位置

proto=tcpNFS 使用 TCP 协议
nfsvers=3使用 NFS 版本 3
no_lock禁用 NFS 文件锁(lockd)。避免在无锁服务时出现警告或延迟,常用于简单开发环境。
rw根文件系统以 读写(read-write) 方式挂载,允许在开发过程中修改文件。
console=ttySAC2,115200内核控制台输出到 串口 2(ttySAC2),波特率 115200。这是开发板与主机间调试信息的输出通道
init=/linuxrc内核启动后执行的第一个用户态程序是 /linuxrc(通常是 BusyBox 提供的初始化脚本或软链接到 /sbin/init)。
ip=192.168.2.29给开发板自身指定静态 IP 地址192.168.2.29),用于 NFS 挂载及网络通信。

三.Kconfig Makefile .config menuconfig之间的关系

menuconfig是Linux内核提供的菜单选项,用于表示Linux内核有哪些功能,类比餐馆的菜单,汇总了当前内核所有支持的功能选项供用户选择。
.config是Linux当前内核选择了哪些功能的记录,类比点菜的凭证,记录了用户最终选定的内核功能。
Makefile给Linux内核提供对应的功能实现,类比餐馆所有厨师会做的菜,只有Makefile中支持的功能才能出现在menuconfig的菜单选项里。
Kconfig用于配置对应的宏,将用户选择的功能配置项通过宏的形式确定下来,比如配置为Y代表将对应功能直接编译进内核。M代表编译进模块。n表示不编译。
配置项核心逻辑
内核菜单的选项来源是Kconfig,Kconfig的内容是一级一级包含的,从最内层的硬件相关配置(如DM9000)开始,先被物理网卡相关配置包含,再被网络相关配置包含,继续向上被更上层的功能配置包含,最终汇总成menuconfig的完整菜单。
将功能编译进内核依靠make流程中定义的宏配置,部分功能没有默认宏配置,需要手动配置宏,将宏设置为Y代表将该功能直接编译进内核。

四.U-boot启动过程分析

ARM核的设置

1.设置CPU为SVC模式

2.设置异常向量表

3.关闭Cache和MMU(内存管理单元:A.内存访问权限设置 B.物理地址和虚地址的映射)

SOC芯片的设置

4.处理唤醒的条件

5.判断是否在内存中运行

6.如果不在内存中运行,则初始化系统时钟和内存控制器(第一阶段)

如果在内存中运行,则不需要初始化系统时钟和内存控制器(第二阶段)

7.初始化uart

8.设置了sp

9.调用board_init_f

第一阶段代码:

board_init_f 功能将u_boot从存储器搬移到内存,跳到内存中运行

第二阶段代码:

board_init_f

(1)执行了初始化序列表 (板子相关的初始化操作)

(2)为u_boot重定向预留内存

10.对u_boot重定向 (从低端内存搬移到高端内存)

11.清bss段

12.board_init_r

[1]外围硬件初始化

emmc 、网卡

[2]让串口作为标准输入输出设备

[3]main_loop

探测自动启动是否被打断,如果没有被打断,则执行bootcmd指定的命令

如果被打断了,则进入u_boot交互界面

五.Linux 启动过程分析

(1)内核启动首先读取CPU内部ID:芯片厂商生产芯片时会为其生成唯一ID,类似身份证,内核读取该ID后与内部保存的ID匹配,确认当前运行的CPU是内核支持的款型,匹配失败会打印错误信息,该阶段代码为汇编实现,还未进入C语言阶段。

(2)接着获取bootloader传递的板子ID:每块开发板也有独立ID,与CPU内部ID相互独立,开发板ID需要向Linux内核官方申请

(3)MMU使能与页表映射阶段

1.两个ID验证完成后,内核打开MMU,MMU的功能是完成物理地址和虚拟地址的映射,这是操作系统中地址为虚拟地址的原因。

2.初期采用一级页表映射,物理地址和虚拟地址一一对应,例如物理地址4000万对应虚拟地址4000万,此时仅使能MMU,还未完全接管整个内存,后续会重新建立页表映射。

(4)C语言运行条件准备阶段

1.执行清空BSS段、设置SP指针两步操作,是C语言程序可以正常运行的必要条件:清空BSS段后,未初始化的全局变量和static修饰的未初始化静态变量默认值为0,若未做该操作这类变量值为随机值。

2.该阶段还会保存板子ID、CPU ID及其他相关参数。

(3)start_kernel阶段(C语言运行阶段)

1.完成C语言运行条件准备后,进入start_kernel函数,正式切换到C语言执行内核代码,此时终端开始输出大量打印信息。

2.该阶段具体操作为:打印Linux内核版本、处理bootnode传递给Linux内核的参数、初始化控制台(默认控制台为窗口,用于输入输出)、获取内存信息、重新建立页表,完成物理内存和虚拟内存的完整映射,后续可使用虚拟地址,支持多个虚拟地址映射到同一物理内存,32位机器的每个进程可拥有4G独立虚拟地址空间,实际占用物理内存远小于该数值,依赖物理地址和虚拟地址的映射实现。

3.之后依次初始化异常处理、加载所有驱动模块、挂载根文件系统、执行根文件系统中的init程序,启动init进程,完成整个内核启动流程。

六.Busybox  init启动过程分析

1. 启动 init 进程

Linux 内核完成根文件系统挂载后,执行 init 进程(进程 ID 为 1)。在 Busybox 中,这个 init 就是 /sbin/init,由 Busybox 提供。

2.读取并解析 /etc/inittab

inittab文件格式

id编号:权限等级:动作:进程

id编号和权限等级一般忽略

动作
sysinit表示进程在系统启动后最先执行,只执行一次,init 进程等待它结束才继续执行其他动作
wait表示进程在执行 sysinit 后执行,只执行一次,init 进程等待它结束才继续执行其他动作
once表示进程在执行 wait 后执行,只执行一次,init 进程不等待它结束
respawn表示进程在启动 once 后执行,init 进程一旦发现该进程死掉,就会重新启动
askfirst表示进程在启动完 respawn 后执行,与 respawn 类似,不过 init 进程会先输出提示 "Please press Enter to activate this console",等用户输入回车后才启动子进程
restart如果 BusyBox 中配置了 CONFIG_FEATURE_USE_INITTAB,并且 init 进程收到 SIGHUP 信号时,先重新读取/解析 /etc/inittab 文件,再执行该进程
shutdown表示进程在系统收到重启、关闭系统命令时运行
ctrlaltdel表示进程在按下 Ctrl+Alt+Del 组合键时运行

3. 执行 /etc/init.d/rcS 脚本

这是由 init 进程第一个调用的脚本,作为系统启动时的初始化脚本。

4. 挂载 /etc/fstab 文件指定的设备

device:要挂载的设备,mount-point:挂载点,type:文件系统类型,options:挂载参数,dump程序根据这个字段来决定这个文件系统是否需要备份。fsck order:fsck程序根据这个字段来决定是否对磁盘做检查

proc —  Linux内核的资源(进程资源) → /proc 这个目录

tmpfs — 临时存储的空间 → /tmp 这个目录

sysfs —  Linux子系统(GPIO、PWM、I2C) → /sys 这个目录

tmpfs — Linux的设备挂载 → /dev 目录

5. 运行 shell 程序

当 init 按照 inittab 配置启动到 askfirst 或 respawn 的 shell 进程时,用户会看到一个命令行提示符。此时 shell 已经运行,但环境变量尚未完全设置好,因此接下来需要执行 /etc/profile 来初始化环境。

6. 执行 /etc/profile 脚本

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐