裸机和嵌入式 Linux 的本质区别

前面聊的 STM32、FreeRTOS 都是“裸机或 RTOS”路线:你的程序直接跑在 CPU 上,自己管所有资源。嵌入式 Linux 完全换了个思路:Linux 内核先启动,它来管 CPU、内存、外设,你的程序只是内核之上的一个普通进程。

裸机是“你直接开一辆车”,嵌入式 Linux 是“你打网约车”——车由司机(内核)开,你只要说去哪(调用系统调用)。代价是多了一层开销,好处是操作系统替你把难的事都做了。
嵌入式 Linux 配图:开发板数据流指向企鹅图标与齿轮
嵌入式 Linux 把内核和驱动从业务代码里剥出来,应用层只管写业务

什么时候该上 Linux?需要跑网络协议栈、数据库、多进程 GUI、或者硬件资源够(几百 MB 内存以上)的时候,Linux 就是正确选择。如果是电池供电、KB 级内存、微秒级实时响应的传感器节点,裸机更合适。

交叉编译:在 PC 上编译 ARM 程序

开发板性能弱,不会在板子上跑编译器。常规做法是在 x86 的开发机上编译出 ARM 架构的可执行文件,再拷到板子上跑——这就是交叉编译。工具链里 gcc 前面会带个前缀,比如 arm-linux-gnueabihf-gcc。

# 用交叉编译器编译一个 hello world
arm-linux-gnueabihf-gcc -o hello hello.c

# 拷到开发板运行
scp hello root@192.168.1.100:/root/
ssh root@192.168.1.100
# 在板子上
./hello
Hello, Embedded Linux!
交叉编译常见报错
  • arm-linux-gnueabihf-gcc: not found工具链没装或没加到 PATH。
  • 报错找不到 libxxx.so依赖的库没交叉编译,要把库也一起编进去。
  • 板子上报 Exec format error架构对不上,编译的不是板子用的 ARM 版本。
  • Yocto 方案生产环境用 Yocto/Buildroot 统一构建整个根文件系统,省心但学习曲线陡。

Bootloader、内核与根文件系统

一块嵌入式 Linux 板子上电后,三个东西按顺序跑:Bootloader(U-Boot)先初始化硬件、加载内核;内核解压启动、挂载根文件系统;根文件系统上跑 init,拉起用户态进程。这三段任何一个坏了,板子都起不来。

组件职责常见位置
U-Boot初始化 DDR、加载内核、引导启动SD/eMMC 前几扇区
Kernel管理硬件、调度进程、提供系统调用boot 分区 zImage
DTB描述板子硬件资源boot 分区 .dtb
Rootfs根文件系统、/bin、/lib、配置root 分区

调试板子起不来,就是按这三段分段排查:U-Boot 有没有打印、内核有没有解压到一半卡住、根文件系统挂载失败在哪一步。

设备树:告诉内核板子上有什么

以前内核源码里写死板子硬件,换个板子就得改内核。设备树(DTS)把硬件描述从内核代码里剥出来,写成一个独立的 .dtb 文件。内核启动时读 DTB,就知道这块板子接了什么芯片、挂在哪个总线、用哪些引脚。

// 一个简化的设备树片段
&uart4 {
    pinctrl-names = "default";
    pinctrl-0 = &uart4_pins;
    status = "okay";       /* 改成 "disabled" 就关掉这个串口 */
};

&i2c1 {
    clock-frequency = <100000>;
    status = "okay";
    sensor@48 {
        compatible = "ti,tmp1075";
        reg = <0x48>;      /* I2C 地址 */
    };
};

板级硬件改动(换个传感器、调个引脚)改 DTS 重新编译就行,不用动内核源码。这是嵌入式 Linux 比老派 BSP 灵活得多的地方。