大家好!我是大聪明-PLUS

有一天,我上班时接到一个任务,需要在单板电脑上构建并运行 Linux。作为一名微控制器软件开发人员,我有点不知所措——这个任务显然不能通过安装 IDE 并点击“构建项目”来解决。谷歌帮我找到了一个叫做 Buildroot 的工具。相关资料让整个过程看起来非常简单:下载、配置、运行几个命令,将结果上传到单板电脑,就可以开始了!所以,这个过程不会比在普通电脑上安装 Linux 或 Windows 发行版复杂多少?当然不会。

网上有很多信息,但都很零散,而且大多是英文的。要了解各个功能以及在哪里配置,需要翻阅大量的资料。而且,你很难保证所有东西都能在脑子里拼凑起来。

因此,在经历了漫长而艰辛的知识积累、系统化和实践应用的旅程后,我决定撰写这一系列文章。在本系列文章中,我将尽可能清晰、透彻地解释嵌入式 Linux 以及 Buildroot 的具体内容。

我将描述一个基于嵌入式 Linux (EL)的通用片上系统(SoC)场景,避免涉及具体芯片的具体细节。由于这些文章面向的是 Linux 初学者,我事先并不了解他们的 Linux 水平,因此我可能会对某些要点进行过于详细的解释。

     

    1. 简介

     

    1.1 什么是片上系统?

    简单来说,SoC就是包含处理器、接口和硬件组件的微芯片。熟悉微控制器的人自然会问:“那有什么区别?微控制器不是一回事吗?”

    事实上,任何微控制器都具备上述所有特性。但主要区别不在于硬件,而在于编程方法和开发人员使用的抽象层次。为了理解微控制器和SoC的区别,让我们比较一下创建嵌入式解决方案的三种主要方法——从最简单到最复杂:

    1. 无操作系统的微控制器

      这是最便宜、最简单的解决方案,非常适合高度专业化的任务,例如控制伺服驱动器或从传感器读取数据。这些系统的特点是功耗极低、响应速度快。

      这类微控制器的编程几乎总是低级的:直接访问寄存器,即使有封装器,也只需要极少的封装。有时,你唯一可用的就是.h描述寄存器的头文件和数据手册。每个特定的微控制器型号都有自己的一套外设和寄存器,因此代码通常高度依赖于平台。

      这里的一个例外是,例如 STM 的 HAL,它允许在 STM32 系列内从一个芯片到另一个芯片进行相对轻松的代码传输。

    2. 带 RTOS 的微控制器

      下一步是使用实时操作系统 (RTOS),例如 FreeRTOS 或 SYS-BIOS。这将引入多任务处理(不要与多线程混淆)、驱动程序和硬件抽象。

      这样的系统可以以更少的开销执行“繁重”的任务,因为操作系统的存在允许开发更复杂、更灵活的代码。例如,与传感器的交互可以分解为由任务调度程序管理的不同任务。

      代码不再局限于特定平台——只要 RTOS 支持,大部分代码都可以在不同的微控制器上复用。然而,局限性仍然存在:资源仍然有限,通常内存空间有限,而且没有完善的文件系统。

    3. 片上系统

      在这个层面上,真正的“成人”生活开始了:多核处理器、兆字节的 RAM、千兆字节的存储空间、硬件视频处理模块、各种接口(如 HDMI、蓝牙和 USB)——所有这些都集成在一个芯片中。

      从外部来看,SoC 可能看起来只是一个“强大的微控制器”,但实际上它已经是一台可以安装嵌入式 Linux 的功能齐全的计算机。

      从这一点开始,开发人员不再直接使用寄存器,而是通过 Linux 内核、驱动程序、DeviceTree 文件和大量工具(从编译器到包管理器)进行操作。

      尤其重要的是,代码尽可能地可移植。任何与特定芯片不​​直接相关的功能(例如 GPIO 控制)都可以在不同的 SoC 之间保持一致。硬件差异在 Linux 内核和设备树层面被抽象化。

      不可移植的部分通常是制造商的 SDK——一组用于处理特定 SoC 的独特硬件块的特定库。

      需要澄清的是,我们讨论的是专为运行成熟操作系统而设计的 SoC。当然也有一些更简单的 SoC,例如 ESP32,其本质更接近微控制器,并且通常无需 Linux 即可运行。

    因此,SoC 不仅仅是一个“高级微控制器”,而是一个完全不同架构的设备类别。它能够解决需要资源、多线程、成熟操作系统和复杂数据处理的任务。

    主要区别在于与硬件“捆绑”的软件级别。

    如果微控制器是程序员直接控制硬件的设备,那么 SoC 就是程序员通过成熟的操作系统和驱动程序模型工作的平台。

     

    1.2 什么是嵌入式Linux?

    EL 不是一个特定的发行版,甚至不是一个产品。它是一个完整的概念:使用 Linux 内核及其相关的嵌入式软件堆栈来管理除传统计算机之外的设备的想法。

    通常 EL 包括:

    • 装载机

    • Linux内核

    • 根文件系统

    • 库、服务

    最终结果是一个独立的操作系统,但它是专门针对特定设备的需求而定制的——无论是路由器、工业控制器,还是智能扬声器。

    一个合乎逻辑的问题出现了:如果可以使用像 Debian 或 Ubuntu 这样的现成发行版,为什么还要手动构建呢?

    以下是主要区别:

    1. 硬件平台支持

      并非所有 SoC 都提供现成的 Linux 镜像。即使你找到了兼容的镜像,也并不意味着它能开箱即用或与你的硬件完美兼容。

    2. 硬件功能可能不会被披露

      许多 SoC 都有特定的硬件组件:视频加速器、神经网络协处理器、硬件加密模块等等。在标准发行版中,这些功能通常根本不启用或根本不受支持。

    3. 冗余和资源强度

      桌面发行版包含大量的后台服务、通用驱动程序和库。所有这些都会消耗资源——内存、CPU 时间和电量。然而,嵌入式解决方案的资源几乎总是有限的。EL 允许您删除所有不必要的组件,只留下真正必要的组件。

    因此,嵌入式 Linux 与 Linux 相同,但并非通用发行版,而是为特定任务组装的套件。它是一个一切都在掌控之中的系统:从内核到最后的启动脚本。它专为特定设备打造,运行高效、稳定,并且完全按照设计目标运行——没有任何额外功能。

    然而,这并不意味着所有 EL 组件都与传统 PC 中使用的组件完全相同。例如,在典型系统中,启动是通过 BIOS 和 GRUB 进行的,而在 SoC 中,启动是通过一组专门的引导加载程序进行的。因此,为了了解这种系统的结构以及其中包含的组件,让我们先来了解一下它的组件以及启动过程的工作原理。

     

    2. 嵌入式Linux的内部结构是怎样的?

     

    2.1. 开机到登录的启动流程

    也许我会延续比较的传统——这样差异就会变得特别明显。

     

    2.1.1. PC启动流程

    让我们从许多人都熟悉的 PC 启动过程开始:

    1. 通电后,基本固件(BIOS,现代平台上称为 UEFI)会启动。它会检查关键组件(例如处理器、RAM 等)是否存在以及功能是否正常。接下来,它会轮询连接到主板的设备。如果在其中一个设备上检测到引导扇区,则该设备会被添加到引导队列中。

    2. BIOS(或 UEFI)将引导扇区(例如 MBR)从第一个合适的设备复制到 RAM 中,并将控制权移交给该扇区中包含的代码。这段代码称为第一阶段引导加载程序。

    3. 该引导加载程序知道其延续位置(通常在/boot),并加载主引导加载程序 — 通常是 GRUB。GRUB 显示一个菜单(如果指定)并将控制权转移到配置中指定的 Linux 内核。

    4. 内核挂载启动参数中指定的根文件系统(RootFS),并启动init进程。

    5. 从此时直到登录屏幕出现为止发生的一切都取决于具体的分布和设置:它可能涉及设置网络、启动图形环境和其他任务。

     

    2.1.2. SoC启动过程

    现在我们来看一下基于SoC的嵌入式系统的启动:

    1. 通电后,主程序加载器 (PPL)(位于 ROM 中的引导加载程序)启动。它会初始化基本外设,例如静态 RAM (SRAM),并开始搜索下一阶段——辅助程序加载器 (SPL)。如果找到,则将其代码上传到 SRAM,然后 PPL 将控制权移交给它。

      第一阶段的引导加载程序没有特定的术语。它被称为引导 ROM、ROM 代码、内部引导加载程序和 PPL。不过,从现在开始我将使用 PPL 这个术语。

    2. SPL 会初始化更复杂的外设,尤其是 RAM。然后,它会搜索下一阶段——第三程序加载器 (TPL),或者更确切地说,是内核。找到的代码会被加载到 RAM 中,并将控制权移交给内核。

    3. TPL(如果存在)会执行额外的硬件初始化并搜索内核。一旦找到,它会将其加载到 RAM 中并转移控制权。

    4. 内核根据设备树重新配置外设,挂载 RootFS,并启动 Init 进程。与标准 PC 的其他区别在于 RootFS 和 Init 进程中具体包含的内容。

    需要注意的是,每个阶段并不会触发下一个阶段,而是会转移控制权。无法回退——你只能重启整个设备,重新开始。

     

    2.1.3 相似之处和主要区别

    乍一看,加载过程很相似,而且似乎同样令人困惑。某种程度上来说,确实如此。但是!

    在 PC 中,微控制器负责处理许多任务:例如,内存和总线会自动初始化——BIOS 仅负责协调它们的运行。每个系统组件都是独立的,并包含各自的固件。在这种情况下,启动操作系统更多的是组织独立组件之间的交互,而不是手动启动每个组件。

    SoC 的情况则有所不同。它是一种单片芯片,没有单独的控制器或不必要的空间——一切都必须尽可能紧凑、可靠且直观。唤醒系统的任务完全落在 SoC 的肩上。因此需要级联的引导加载程序,每个程序都执行各自的任务。

    • PPL是 ROM 中最小、高度可靠且不可变的代码。它仅执行基本初始化并启动下一阶段。其紧凑的尺寸使其易于验证且可靠。损坏 PPL 意味着摧毁整个系统:芯片将变砖。

    • SPL 的功能略有增加:特别是它可以初始化 RAM。然而,SRAM 太小,无法容纳复杂的逻辑,因此 SPL 通常仅作为下一步的桥梁。

    • TPL已经在完整的 RAM 中运行,并且可以提供丰富的脚本:例如,在哪里加载操作系统、如何显示启动过程、如何响应连接的设备等。

    这种架构不会使事情变得复杂——它能提供保护。如果 SPL 或 TPL 级别发生故障,可以拦截启动过程,从备份驱动器启动,并恢复系统。关键在于预见这种可能性。然而,PPL 发生故障则意味着设备完全失效。因此,它是不可改变的,并且极其简单。

     

    2.2. 嵌入式Linux组成

    现在,我们已经确定了要使用的内容,让我们仔细看看每个组件。

     

    2.2.1. 引导加载程序

    U-Boot 是一款功能强大且用途广泛的开源引导程序,常用于在 SoC 上加载操作系统。我们将要使用的就是 U-Boot。它的优势如下:

    • 支持多种架构和SoC平台

    • 允许您从各种来源启动内核和 RootFS:USB、SD 卡、SATA 驱动器、网络、NAND/NOR 内存

    • 可以运行脚本:例如,先尝试从 USB 启动,然后从 eMMC 启动,最后才从网络启动

    • 支持交互模式(通过控制台)和计时器自动加载

    • 它可以通过环境变量进行灵活的配置。

    您可以从官方存储库下载 U-Boot 。

    如果 SPL 不适合 SRAM,它可以被“拆分”成多个部分 - 有关这方面的更多信息请参阅 SoC 加载部分

     

    2.2.2. 内核

    这就是 Linux 本身——控制硬件、抽象资源并确保程序之间交互的内核。

    您可以从官方存储库或针对特定 SoC 定制的分支下载内核。

     

    2.2.3. 根文件系统

    RootFS 不仅仅是目录的集合。它是一个完整的环境,包括:

    • 实用程序,例如 init、sh、ifconfig

    • 配置文件

    • 运行程序的依赖项

    与内核和引导加载程序不同,RootFS 不是以二进制文件的形式构建的,而是以目录结构的形式创建的。您可以手动创建 RootFS(例如,从主机系统复制必要的二进制文件和库),但这需要谨慎操作并透彻理解系统结构。

     

    2.3. 嵌入式 Linux 构建工具和辅助工具

    至此,我们已经介绍了“代表”。有些人甚至可能已经开始查看源代码了。但我不建议你着急,因为我们还需要掌握构建工具和媒体。

     

    2.3.1. 工具链

    工具链是一组特定于目标架构的编译器、头文件和实用程序,即:

    • 交叉编译器 gcc、g++、ld、as — 构建代码所需的一切

    • Linux 内核头文件

    • 库和头文件 libc、libstdc++、libm 和其他系统库

    • 调试实用程序、分析器、系统助手

    工具链的主要要求是符合主机(编译发生的地方)和目标(代码运行的地方)系统的架构。

    您可以下载现成的解决方案,也可以自行构建(例如,使用 CrossTool-NG)。后者值得另写一篇系列文章来阐述,因此我们假设只能使用现成的解决方案。

    它们可以从您的设备制造商或第三方工具链制造商的网站下载。

    嗯,你仍然可以自己安装它......

    等等,我不是说过我们不会这么做吗?这么说吧,我撒谎了。我们会构建和配置工具链,但不是自己做的;我们会让构建工具来帮我们做。

     

    2.3.2. 装配工具

    为了避免手动构建所有内容,通常使用专门的构建系统。其中最受欢迎的两个是 Buildroot 和 Yocto 项目。

    Yocto 项目

    Yocto 是一个强大且灵活的嵌入式 Linux 构建环境。它允许您:

    • 构建内核、引导加载程序、RootFS 和工具链

    • 在系统中启用对包管理器(opkg、rpm、dpkg)的支持

    • 精确管理版本、补丁和配置

    然而,Yocto 的学习曲线相当高。它使用自己的配方系统 (bitbake) 以及多层和多个概念。作为交换,您可以获得最大的灵活性和控制力。

    Buildroot

    Buildroot 更简单:它由一组 Makefile、Kconfig 和 bash 脚本组成,用于构建一个极简但可立即使用的 Linux 系统。与 Yocto 类似,它允许您构建内核、引导加载程序、RootFS 和工具链。优点:

    • 进入门槛低

    • 简单的界面(menuconfig)

    • 系统受到运行时保护,不会被更改——RootFS 默认为只读。虽然可以毫不费力地嵌入包管理器,但操作极其困难,因此不建议这样做。

    本指南将使用 Buildroot。您可以从官方仓库下载。

    至此,我们可以完成介绍部分并从理论转向实践。

     

    结果

    因此,我们已经介绍了基础知识:嵌入式 Linux 是什么、它与常规发行版有何不同、它由什么组成、如何启动以及可以使用哪些工具来构建它。

    在下一篇文章中,我们将使用 Docker 准备工作环境,检查 U-Boot、Linux、Buildroot 及其外层的结构,并在我们的板上配置、构建和运行嵌入式 Linux。

    因此我强烈建议购买某种带有 SoC 的主板。

    感谢您的时间!再见!

     

    Logo

    openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

    更多推荐