当前位置:首页 > 工业控制 > 工业控制
[导读]嵌入式Linux上电后是一条接力链:BootROM→SPL→U-Boot→Kernel→rootfs→init→应用。任一阶段无串口输出,都要按阶段二分,而不是盲目重烧整包。


嵌入式Linux上电后是一条接力链:BootROM→SPL→U-Boot→Kernel→rootfs→init→应用。任一阶段无串口输出,都要按阶段二分,而不是盲目重烧整包。

一、BootROM与SPL:把运行环境搭起来

SoC上电先跑片内ROM,按拨码/熔丝选择SD、eMMC、NAND、SPI;多数A核再搬SPL到SRAM,SPL只做最少量工作:关看门狗、配时钟、初始化DDR、把完整U-Boot搬到DDR并跳转。SPL阶段几乎没有日志,卡住优先查供电、晶振、DDR时序、启动介质脚。

U-Boot Proper起来后做重定位、环境变量、串口、存储/网络外设,最后进main_loop。自动启动靠两个变量:

printenv bootcmd

printenv bootargs

bootcmd是“怎么加载启动”,bootargs是“传给内核什么参数”。 SD/eMMC ARM样机示例:

setenv loadaddr 0x42000000

setenv fdtaddr  0x43000000

setenv bootcmd 'fatload mmc 0:1 ${loadaddr} zImage; fatload mmc 0:1 ${fdtaddr} board.dtb; bootz ${loadaddr} - ${fdtaddr}'

setenv bootargs 'console=ttyS0,115200 earlycon=uart8250,mmio32,0x44e09000 root=/dev/mmcblk0p2 rw rootwait initcall_debug=1'

saveenv

bootz用于zImage+dtb;uImage用bootm;ARM64裸Image用booti。U-Boot会把bootargs写进DTB的/chosen/bootargs,再把DTB地址通过寄存器交内核,ARM32常走r2、ARM64走x0。

二、内核解压与start_kernel

U-Boot跳进内核入口后,压缩镜像先自解压,再跑体系结构汇编,进入C函数start_kernel():初始化固定内存memblock、MMU页表、中断向量、调度器、timer,再setup_arch()解析DTB,建立platform/device/driver模型。 早期没控制台时靠earlycon看日志,正式控制台由console=激活。

设备树决定“有什么硬件”:CPU、内存、UART、I2C、SPI、网卡、 regulators、clock、gpio都在dts里;驱动按compatible匹配probe。若卡在“Starting kernel…”之后无输出,先确认earlycon地址与UART时钟,再用initcall_debug看卡在哪个initcall:

# 内核cmdline追加

initcall_debug printk.time=1 loglevel=7 ignore_loglevel

# 启动后

dmesg | grep -A3 -B1 "initcall"

initcall_debug会按调用顺序打印各子系统初始化耗时,驱动probe死等、I2C时钟未起、regulator死锁都能在这里暴露。

三、挂载根文件系统

内核按root=找根设备,常见有四类:

eMMC/SD:root=/dev/mmcblk0p2,ext4/squashfs均可,rootwait等设备枚举完再挂;

NAND/UBI:root=ubi0:rootfs,需内置UBI/UBIFS驱动,不能等模块;

网络调试:root=/dev/nfs,配合ip=、nfsroot=,适合样机迭代;

内存临时根:initramfs内嵌cpio,先跑/init做驱动、解密、RAID,再switch_root到真根。

若报“VFS: Unable to mount root fs”,按三件事查:root=设备名是否和分区一致、对应块/文件系统驱动是否编进内核而非模块、DTB里存储控制器和时钟是否probe成功。只读系统可先ro挂根,进init后再remount rw,降低掉电风险。

initramfs最小切换思路:

mount -t proc proc /proc

mount -t sysfs sysfs /sys

mount /dev/mmcblk0p2 /new_root

exec switch_root /new_root /sbin/init

四、init到应用层

内核找PID1顺序受init=覆盖,默认/sbin/init→/etc/init→/bin/init→/bin/sh。轻量系统用BusyBox init,读/etc/inittab:

::sysinit:/etc/init.d/rcS

::respawn:/sbin/getty 115200 ttyS0

::restart:/sbin/init

::ctrlaltdel:/sbin/reboot

rcS里挂proc/sys/devpts、配置网络、调时钟、启动业务守护。工业网关常把业务拆成独立脚本,先网络再MQTT再采集,避免依赖倒置。

用systemd时看target和服务单元:

[Unit]

Description=Edge Collector

After=network-online.target

Wants=network-online.target


[Service]

Type=simple

ExecStart=/usr/bin/collector --conf /etc/collector.conf

Restart=on-failure


[Install]

WantedBy=multi-user.target

启用并查启动耗时:

systemctl enable collector.service

systemctl start collector.service

systemctl --failed

systemd-analyze blame

systemd-analyze critical-chain collector.service

blame看单服务耗时,critical-chain看依赖关键路径;启动慢常是网络等待、文件系统fsck、数据库恢复或非必要服务并行度不够。

五、全链路排查清单

完全无串口:BootROM介质、SPL DDR、U-Boot环境变量、串口stdout/stderr是否设置;

进内核即停:earlycon是否匹配、DTB地址是否和bootcmd一致、内存节点是否准确;

驱动不跑:dmesg看deferred、clock/regulator/gpio依赖,of_match是否拼错;

根挂不上:root=、rootfstype、rootwait、块驱动内置、initramfs是否缺模块;

init起不来:用init=/bin/sh进救援,检查/sbin/init存在、动态库完整、inittab/rcS权限;

应用自起后崩:单独跑二进制看ldd、配置文件、/var日志、systemd Restart与journalctl -u。

把bootargs、DTB、initramfs、rcS/systemd单元全部版本化,每次只改一个变量复测,启动问题基本都能收敛到“某一行参数或某一个probe”。



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭