1 嵌入式 Linux 基础知识与关键知识点

嵌入式 Linux 指在资源、形态、生命周期约束明确的硬件上运行 Linux 内核用户空间(可裁剪),典型场景包括工业控制、网通、车载网关、消费电子网关等。本文按「宏观栈 → 体系结构底座 → 内核与驱动 → 启动与文件系统 → 工程化与合规」组织,标出实践与面试中反复出现的边界与自检点。


1.1 嵌入式 Linux 在系统栈中的位置

┌─────────────────────────────────────────┐
│ 应用:业务逻辑、协议栈用户态、GUI(可选)   │
├─────────────────────────────────────────┤
│ 用户空间:glibc/musl、BusyBox/systemd、守护进程 │
├─────────────────────────────────────────┤
│ Linux 内核:调度、内存、驱动、网络协议栈…   │
├─────────────────────────────────────────┤
│ Bootloader:U-Boot / Barebox 等         │
├─────────────────────────────────────────┤
│ SoC:CPU + 外设控制器 + BootROM           │
└─────────────────────────────────────────┘

关键认知:「会写用户态 C/C++」≠「能做好嵌入式 Linux」。中间隔着 交叉编译与 sysroot、启动链、设备树、根文件系统、内核配置与驱动模型、可观测与更新(OTA) 等完整一层。


1.2 计算机体系结构(必读底座)

主题关键知识点实践意义
CPU 与 ISAARMv7/AArch32、ARMv8‑A/AArch64、RISC‑V 等差异;Thumb / AArch64 执行状态选对工具链 triplet(如 aarch64-linux-gnu vs arm-none-eabi
内存层次寄存器 → Cache → DDR;外设 MMIO 物理地址区间DMA 与 Cache 一致性dma_map_* / dma_sync_*);对齐与带宽
MMU页表、TLB;用户/内核虚拟地址空间用户指针 ≠ 物理地址;驱动里 copy_to_user / ioremap 等语义
中断IRQ、GIC;硬中断与下半部硬中断上下文不可睡眠;耗时工作下放 workqueue
异常级别(ARM)EL0~EL3、TrustZone(按需)安全启动、TEE 与普通 Linux 驱动的责任边界

应能口述:为何不能在中断里 睡眠;为何 DMA buffer 往往要 cache line 对齐 并走 统一 DMA API 而不是自算物理地址写寄存器(IOMMU 场景下尤其错误)。


1.3 Linux 内核基础(嵌入式最常碰的子集)

1.3.1 进程与调度

  • 进程/线程task_struct;CFS 调度基本概念。
  • 上下文划分:进程上下文、硬中断上下文、软中断上下文;可阻塞不可阻塞 API 边界。

1.3.2 内存管理

  • 物理页、伙伴系统;slabkmalloc / vmalloc 使用场景区分。
  • mmap、缺页、copy_to_user / copy_from_user
  • OOM、水位线与嵌入式小内存板上的行为。

1.3.3 同步

  • spinlockmutexrwlock;RCU(了解即可)。
  • spinlock 持有期间在典型非 RT 内核上不可睡眠;死锁排查:加锁顺序、lockdep(若配置开启)。

1.3.4 设备模型

  • bus / device / driverprobe / remove 与电源管理钩子(按需)。
  • sysfs 属性与 uevent(与 /dev 节点生成相关,理解即可)。

1.3.5 块设备与网络(栈边)

  • 块层与 MTD/UBI 路线和 eMMC 块设备 + ext4 路线的差异(见「存储」一节)。
  • sk_buff、网络分层;与 DPDK 等用户态绕过栈方案正交(按产品选型)。

与设备树衔接:现代 ARM/ AArch64 嵌入式上 platform_driver(及总线驱动)与 DT compatible 匹配 是入门驱动的第一关。


1.4 设备树(Device Tree)

设备树用 .dts / .dtsi 描述硬件:CPU、内存、时钟、GPIO、I2C/SPI 外设等,编译为 .dtb,由内核在启动早期解析并与驱动匹配。

概念说明
compatible驱动匹配字符串,如 "vendor,model";可多个字符串 fallback
regMMIO 或总线地址范围:<addr size …>
interrupts中断 specifier,依赖父节点 interrupt-controller 定义
phandle句柄,用于引用时钟、GPIO、reset 等
status"okay" / "disabled";板级可覆盖 SoC 默认

实践要点

  • 属性合法性以 内核 Documentation/devicetree/bindings(或你使用内核版本的等价文档)为唯一权威
  • 分层:SoC 公版 .dtsi + 板级 .dts 追加/覆盖;减少在板级重复描述。

1.5 Linux 设备驱动(入门到进阶路径)

1.5.1 字符设备

  • cdevfile_operationsopen/read/write/ioctl/mmap/release)。
  • 主次设备号、devtmpfs/dev 节点。

1.5.2 平台与总线型驱动

  • platform_driver + DT 匹配。
  • I2C / SPIi2c_clientspi_device 与核心消息 API。

1.5.3 阻塞与并发

  • wait_queuepoll / epoll 侧非阻塞;环形缓冲区与生产者消费者模型。

1.5.4 中断与下半部

  • request_irqthreaded irqtasklet(历史路径仍可见)、workqueue

1.5.5 DMA

  • dma_alloc_coherentdma_map_single / dma_unmap_singleCache 与 IOMMU 下为何不能假设「VA 线性减常数 = PA」。

1.5.6 常见子系统(按项目选学)

  • GPIO / PinctrlRegulatorCommon ClockPWMIIO(ADC)RTCWatchdog

里程碑:能独立完成 最小内核模块printk + sysfs 属性)或 GPIO 点灯,并会用 dmesg/sysinsmod/rmmod 闭环调试。


1.6 启动流程(Boot Flow)

典型软件链(名称因芯片厂商而异):

  • BootROM:固化在片内,从 SPI NOR、eMMC、UART 等加载下一阶段。
  • SPL / FSBL:极小阶段,完成 DDR 最小初始化等。
  • U-Boot / Barebox:环境变量、bootcmd、加载 Image/zImage + .dtb、可选 initramfs;网络刷机(TFTP/DFU)常在此层。
  • Linux:自解压/入口、子系统初始化、挂载 rootfs。
  • init:BusyBox initsystemd 或自定义 PID 1。

内核命令行(bootargs 常见项:console=root=rootfstype=rootwaitquietmem= 等;earlyprintk正确 console 设备与波特率 是排「黑屏」的第一手段。

量产相关概念FIT ImageA/B 分区recoverySecure Boot 链(ROM→SPL→U-Boot→Kernel 验签思路,细节因平台而异)。


1.7 交叉开发与工具链(概要)

概念说明
Host / Target在 PC 上编译,在板子上运行
交叉前缀aarch64-linux-gnu-bare-metalarm-none-eabi-(无 Linux libc)
sysroot目标根文件系统的头文件与库,供链接与 #include 解析
ABIEABI、硬浮点 hf 等;须与 rootfs、内核模块编译环境一致

实践:会用 readelf -hfile、(在目标机上的)ldd 解释 Machine / ABI / 缺 .so;链接阶段缺库与运行阶段缺库是两类问题。

更长的交叉编译工程化说明可单独成文(CMake toolchain、Meson、PKG_CONFIG_SYSROOT_DIR 等)。


1.8 根文件系统(rootfs)与 init

  • FHS/bin/sbin/etc/lib/usr/var/proc/sys/dev 的职责分工。
  • glibc vs musl:体积、兼容性、生态与合规权衡。
  • BusyBox:单二进制多 applet,适合小系统;systemd 适合复杂依赖与并行启动。
  • 动态链接:目标板需带齐 解释器与依赖 .so,或评估 静态链接 的体积与 NSS 等陷阱。
  • initramfs:早期用户态与 pivot_root 到真实 root 的场景(若使用)。

1.9 构建系统(工程级)

系统特点典型适用
BuildrootKconfig/Makefile,上手快,镜像直出小到中型项目、快速原型
Yocto / OpenEmbeddedrecipe、layer、可复现性强多 SKU、长周期维护、团队分工
厂商 BSP芯片商锁定版本的内核/U-Boot/工具链先跑通再评估升级与上游化

要点defconfig、包选择、post-build/rootfs overlaySPDX/许可证清单 与交付物审计。


1.10 存储与文件系统

介质注意点
Raw NAND坏块、ECC、磨损均衡;常见 UBI + UBIFS(或历史 JFFS2)
eMMC / SD块设备 + FTL;常见 ext4;掉电一致性与 fsync、分区布局
SPI NOR常放 kernel、dtb、小 rootfs 或只读分区
分区MBR/GPT、A/B slot、只读根 + overlay

关键只读 root + overlay双分区回滚 在量产与 OTA 中极常见;UBIFS 与 ext4-on-eMMC 的掉电语义不同,勿混用经验。


1.11 调试手段(建议掌握顺序)

手段用途
串口 console最早期的 printk 与 shell
dmesg / loglevel驱动与内核子系统日志
/proc / /sys运行时状态与调参
gdbserver + 交叉 gdb用户态单步
ftrace / perf延迟与热点(资源允许时)
JTAG/SWD早期启动与硬死机(视硬件)
kgdb、kdump内核在线调试与崩溃转储(进阶)

1.12 实时与确定性(按需)

  • 通用 Linux 调度为 尽力而为;硬实时常落在 MCU / RTOS 侧 或专用协处理器。
  • PREEMPT_RT:缩短不可抢占区,改善最坏情况延迟;需 配置 + 实测(如 cyclictest),避免「感觉实时」。

1.13 网络与安全(现代产品)

  • Socket、TCP/UDP 基础;若做网关需了解 nftables/iptables 边界。
  • SSHOTA 签名Secure Boot 链与密钥管理。
  • capabilities、非 root 运行、最小权限面。
  • 内核 LTS 与 CVE 流程:版本选择是供应链问题,不单是技术偏好。

1.14 许可证与合规

  • GPL:与内核链接的模块/衍生作品的传播义务(具体以法务解释为准);MODULE_LICENSEEXPORT_SYMBOL_GPL 等是内核生态的合规信号,不是装饰。
  • 发布物中 BusyBox、内核、U-Boot、第三方库 的许可证聚合与 manifest

1.15 推荐学习路径(可执行顺序)

分阶段文档见 学习路径索引


1.16 自检清单(初/中级常见要求)

  • 说清从上电到 shell 提示符 的大致软件阶段。1
  • 解释 DT compatible 与驱动 probe 如何对应。2
  • 说明 中断上下文与进程上下文 各能/不能做什么。3
  • 配置并成功使用 串口内核日志 + rootfs 挂载4
  • 独立完成 交叉编译 并在目标板运行,解决常见 动态库缺失5
  • 使用 Buildroot 或 Yocto 之一生成过可启动镜像。6
  • 编写或修改过 简单内核模块,理解 许可证与导出符号7
  • 区分 NAND+UBIeMMC+ext4 在一致性与工具链上的差异。8
  • (选做)了解 PREEMPT_RT 与延迟测量的基本概念。9

1.17 延伸阅读与权威来源

  • 内核源码树 Documentation/(含设备树 bindings)。
  • U-BootBarebox 官方手册。
  • 书籍:Linux Device Drivers(思路仍有用,API 需对照当前内核)、体系结构侧 ARM 官方或经典教材。
  • 社区:kernel.orgelinux.org、SoC 厂商 Wiki。

硬件与 BSP 差异远大于抽象总结;具体寄存器、时钟与启动介质以 SoC TRM 与厂商 BSP 为准。本文标题不叠数字编号,便于配合 Markdown 自动编号插件使用。

Footnotes

  1. 上电 → shell 的软件阶段(典型):SoC 上电后 BootROM 从 SPI NOR / eMMC / UART 等介质加载 SPL/FSBL(若有);SPL 完成最小 DDR 等初始化后加载 U-Boot/Barebox;Bootloader 按环境变量或脚本加载 内核镜像 + .dtb(可选 initramfs),把 bootargs 传给内核;Linux 完成子系统初始化后执行 PID 1(init),挂载 rootfs,再启动 getty/login,最终出现 shell 提示符。能按顺序说出各阶段职责与常见排障点(串口无输出、kernel panic、rootfs mount fail)即达标。

  2. compatibleprobe 的对应:设备树里节点的 compatible 是字符串列表(如 "vendor,model","generic,fallback")。内核 OF(Open Firmware) 子系统在注册 platform_device(或 I2C/SPI 等总线上的设备)时,会拿该字符串与已注册的 of_device_id / platform_driver(或对应总线 driver) 的匹配表比对;第一个匹配成功的驱动被绑定,随后内核调用该驱动的 probe(),传入 struct platform_device *(或对应设备指针),驱动在此解析 DT 属性reginterruptsclocks 等)并完成硬件初始化。无匹配则设备存在但无驱动,probe 不会执行。

  3. 中断上下文 vs 进程上下文硬中断上下文(ISR 顶半部)要求极短、不可睡眠(不能调用可能调度的 API,如 mutex_lock 可能睡眠的路径、kmalloc(GFP_KERNEL) 在内存紧张时也可能阻塞);不能做耗时工作与大量分配。进程上下文(系统调用、内核线程、probe 里大部分路径)可以睡眠,可用 mutexkmalloc(GFP_KERNEL)copy_to/from_user 等。软中断/tasklet 仍不可睡眠;耗时与可阻塞工作常下放到 workqueue 在进程上下文执行。

  4. 串口内核日志 + rootfs 挂载:在 bootargs 中配置 console=ttySx,115200n8(或 ttyAMA0ttyS0 等,以硬件 DTS 与驱动为准)使 printk 与用户态早期输出 出现在串口;同时配置 root=/dev/mmcblk0p2(或 LABEL=PARTUUID=ubi0:rootfs 等)与 rootfstype=ext4 / rootfstype=ubifs,必要时加 rootwait 等待块设备就绪。验证:上电后串口可见内核滚动日志,随后 init 挂载 rootfs 成功 并进入 shell;若卡在 Waiting for root device,多为 设备名/Driver/分区表bootargs 不一致。

  5. 交叉编译与动态库缺失:在宿主机用 目标 triplet 前缀的编译器(如 aarch64-linux-gnu-gcc)与 -sysroot 指向目标 rootfs,保证 ABI(hf/ilp32 等) 与板端一致。常见错误:链接期-lfoo.pc 路径不对;运行期 error while loading shared libraries 多为 rootfs 中缺 libfoo.sold-linux-*.so 解释器路径不对。用 readelf -dNEEDED,在目标机用 ldd 核对;必要时 rpath 或把 .so 安装到 /usr/libldconfig(若镜像支持)。

  6. Buildroot / Yocto 可启动镜像Buildroot 通过 make menuconfig 选板型/内核/包,产出 kernel + dtb + rootfs 镜像(如 sdcard.img 或分离的 Image/rootfs.ext4),能 烧录或 SD 启动 到 shell。Yocto 通过 machineimage recipe 生成 wic/hddimg 等,强调 layer、recipe、可复现构建。自检点:你能说清 镜像里各分区内容从哪个 recipe/defconfig 来,并能做一次 干净重编 仍得到可启动结果。

  7. 简单内核模块与许可证/导出符号:模块需声明 MODULE_LICENSE("GPL") 等,与 是否链接到仅 GPL 导出符号 相关;EXPORT_SYMBOLEXPORT_SYMBOL_GPL 区分「任意模块可用」与 「仅 GPL 兼容模块可用」modpost 会校验。模块必须与 运行内核同版本配置 编译(VERMAGIC 匹配),用 insmod/dmesg/rmmod 闭环;理解 insmod 在进程上下文、模块内 init/exitprobe 驱动 不是同一概念但都要遵守 睡眠规则

  8. NAND+UBI vs eMMC+ext4Raw NAND 无块语义,需 FTL 或 UBI 卷管理UBI+UBIFS 在闪存上提供磨损均衡与坏块管理,工具链常见 ubiformat/ubinize,掉电需关注 UBI 卷原子更新 与挂载参数。eMMC 内置 FTL,对上呈现 块设备,多用 ext4;工具 mkfs.ext4tune2fs,一致性依赖 日志、barrier、fsync 与分区只读策略。二者 不可混用经验:例如不能把 UBI 映像直接当裸块 dd 到无 FTL 的 NAND 上而不走 UBI 流程(具体以硬件与 BSP 为准)。

  9. PREEMPT_RT 与延迟测量(选做)PREEMPT_RT 通过可抢占临界区、线程化中断等降低 最坏情况延迟,但 不保证「每个周期都硬实时」;需 打开对应内核配置 并用 cyclictestftracehistogram实测 延迟分布。产品常见分工:硬实时闭环 放 MCU/RTOS,Linux 侧做 尽力低延迟隔离 CPUisolcpus)等组合策略。