嵌入式Linux系统的安装和配置

大家好,我是良许。

最近有不少读者朋友问我关于嵌入式 Linux 系统如何安装和配置的问题。

作为一名从事嵌入式开发多年的程序员,我深知这个话题对于初学者来说可能有些复杂,但只要掌握了正确的方法和步骤,其实并没有想象中那么困难。

今天我就结合自己这些年的实战经验,给大家详细讲解一下嵌入式 Linux 系统的安装和配置全过程。

1. 嵌入式 Linux 系统概述

1.1 什么是嵌入式 Linux 系统

嵌入式 Linux 系统是指运行在嵌入式设备上的 Linux 操作系统。

与我们平时在 PC 上使用的 Ubuntu、CentOS 等桌面 Linux 系统不同,嵌入式 Linux 系统通常针对特定硬件平台进行了裁剪和优化,去掉了很多不必要的功能模块,使其能够在资源受限的嵌入式设备上高效运行。

在我之前做汽车电子项目的时候,我们使用的就是基于 ARM 架构的嵌入式 Linux 系统。

整个系统的根文件系统只有几十 MB 大小,但却能够稳定运行各种车载应用程序。

这种精简高效的特性,正是嵌入式 Linux 系统的核心优势。

1.2 嵌入式 Linux 系统的组成

一个完整的嵌入式 Linux 系统通常由以下几个核心部分组成:

Bootloader(引导加载程序): 这是系统启动时最先运行的程序,负责初始化硬件、加载内核到内存并启动内核。

常见的 Bootloader 有 U-Boot、Barebox 等。

我在项目中最常用的就是 U-Boot,它功能强大且支持的硬件平台非常广泛。

Linux 内核: 这是整个系统的核心,负责管理硬件资源、提供系统调用接口、调度进程等。

嵌入式 Linux 内核通常会根据具体硬件平台进行裁剪和配置,只保留必要的功能模块。

根文件系统(Root Filesystem): 包含了系统运行所需的各种库文件、配置文件、应用程序等。

根文件系统可以存储在不同的介质上,比如 NOR Flash、NAND Flash、SD 卡、eMMC 等。

应用程序: 这是运行在用户空间的各种应用软件,实现具体的业务功能。

2. 开发环境准备

2.1 宿主机环境搭建

在开始嵌入式 Linux 系统的安装配置之前,我们首先需要准备一台运行 Linux 系统的宿主机作为开发环境。

我个人习惯使用 Ubuntu 作为开发主机,版本建议选择 18.04 LTS 或 20.04 LTS,这两个版本稳定性好且社区支持完善。

安装完 Ubuntu 系统后,需要安装一些必要的开发工具和依赖库:

sudo apt-get update
sudo apt-get install build-essential git vim
sudo apt-get install libncurses5-dev libssl-dev
sudo apt-get install bison flex bc
sudo apt-get install u-boot-tools device-tree-compiler
sudo apt-get install nfs-kernel-server tftp-hpa tftpd-hpa

这些工具包括编译工具链、版本控制工具、内核配置工具、设备树编译器以及网络调试工具等。

在我的实际工作中,这些工具几乎每天都会用到。

2.2 交叉编译工具链

由于嵌入式设备的处理器架构(如 ARM、MIPS 等)与开发主机(通常是 x86 架构)不同,我们需要使用交叉编译工具链来编译目标平台的代码。

交叉编译工具链可以从芯片厂商官网下载,也可以使用开源的工具链如 Linaro、Buildroot 等。

以 ARM 平台为例,下载并安装交叉编译工具链:

# 下载Linaro工具链
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
​
# 解压
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
​
# 配置环境变量
echo "export PATH=$PATH:/path/to/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin" >> ~/.bashrc
source ~/.bashrc
​
# 验证安装
arm-linux-gnueabihf-gcc --version

配置好环境变量后,我们就可以使用 arm-linux-gnueabihf-gcc 等命令来编译 ARM 平台的程序了。

3. Bootloader 的编译与烧写

3.1 U-Boot 源码获取与配置

U-Boot 是目前最流行的开源 Bootloader,支持众多硬件平台。

首先从官方仓库获取 U-Boot 源码:

git clone https://github.com/u-boot/u-boot.git
cd u-boot
git checkout v2021.01  # 选择一个稳定版本

获取源码后,需要根据目标硬件平台进行配置。

U-Boot 为不同的开发板提供了默认配置文件,存放在 configs 目录下。以 STM32MP157 开发板为例:

# 查看支持的配置
ls configs/ | grep stm32mp1
​
# 使用默认配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- stm32mp15_basic_defconfig
​
# 进行详细配置(可选)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

在 menuconfig 界面中,我们可以根据实际需求配置各种功能选项,比如网络支持、存储设备支持、环境变量存储位置等。

我在项目中通常会启用网络功能,这样可以通过 TFTP 方式快速下载和调试内核镜像。

3.2 编译 U-Boot

配置完成后,执行编译命令:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8

其中 -j8 表示使用 8 个线程并行编译,可以根据主机 CPU 核心数调整。

编译成功后,会在当前目录生成 u-boot.binu-boot.img 等文件,这就是我们需要的 Bootloader 镜像。

3.3 烧写 U-Boot 到目标板

将编译好的 U-Boot 烧写到目标板的方法有多种,常见的有:

通过 SD 卡烧写: 适用于支持 SD 卡启动的开发板。使用 dd 命令将 U-Boot 写入 SD 卡的特定位置:

sudo dd if=u-boot.bin of=/dev/sdb bs=1k seek=8 conv=fsync

通过 USB/串口烧写: 使用芯片厂商提供的烧写工具,通过 USB 或串口接口将 U-Boot 烧写到 Flash 中。

通过 JTAG 调试器烧写: 使用 J-Link、ST-Link 等调试器,通过 JTAG 接口烧写。

我在实际项目中,通常会先使用 SD 卡方式快速验证 U-Boot 功能,确认无误后再烧写到板载 Flash 中。

4. Linux 内核的编译与部署

4.1 内核源码获取

Linux 内核源码可以从 kernel.org 官网下载,也可以使用芯片厂商提供的定制版本。

对于 STM32MP1 系列,ST 官方提供了专门优化的内核:

git clone https://github.com/STMicroelectronics/linux.git
cd linux
git checkout v5.10-stm32mp  # 选择对应版本

使用厂商提供的内核版本的好处是,很多硬件驱动已经集成好了,可以大大减少我们的开发工作量。

4.2 内核配置

内核配置是一个非常重要的步骤,直接影响到系统的功能和性能。

首先使用默认配置:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- multi_v7_defconfig

然后进入 menuconfig 进行详细配置:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

在配置界面中,有几个关键选项需要注意:

文件系统支持: 根据实际使用的文件系统类型,启用相应的支持,如 ext4、jffs2、ubifs 等。

我在项目中经常使用 ext4 文件系统,它稳定可靠且性能良好。

网络协议栈: 如果设备需要联网,必须启用 TCP/IP 协议栈以及相应的网络设备驱动。

设备驱动: 根据硬件配置,启用对应的驱动程序,如 I2C、SPI、UART、USB、以太网等。

这一步非常关键,如果驱动没有正确配置,相应的硬件就无法正常工作。

电源管理: 对于电池供电的设备,需要配置好电源管理选项,以降低功耗。

4.3 设备树配置

设备树(Device Tree)是描述硬件配置的数据结构,Linux 内核通过解析设备树来识别硬件资源。

设备树源文件(dts)位于 arch/arm/boot/dts 目录下。

以 STM32MP157 为例,可能需要修改或创建自己的设备树文件:

// stm32mp157c-myboard.dts
/dts-v1/;
#include "stm32mp157.dtsi"
#include "stm32mp15xc.dtsi"
#include "stm32mp15-pinctrl.dtsi"

/ {
    model = "My Custom Board";
    compatible = "st,stm32mp157c-myboard", "st,stm32mp157";

    chosen {
        stdout-path = "serial0:115200n8";
    };

    memory@c0000000 {
        device_type = "memory";
        reg = <0xc0000000 0x20000000>; // 512MB DDR
    };
};

&uart4 {
    pinctrl-names = "default";
    pinctrl-0 = <&uart4_pins_a>;
    status = "okay";
};

&ethernet0 {
    status = "okay";
    pinctrl-0 = <&ethernet0_rgmii_pins_a>;
    pinctrl-names = "default";
    phy-mode = "rgmii";
};

设备树的配置需要根据实际硬件电路来编写,包括内存大小、外设接口、引脚复用等信息。

在我做汽车电子项目时,设备树的调试占据了不少时间,因为任何一个小错误都可能导致系统无法启动或外设无法工作。

4.4 编译内核和设备树

配置完成后,开始编译:

# 编译内核镜像
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage -j8

# 编译设备树
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs

# 编译内核模块
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules -j8

编译成功后,会生成以下重要文件:

  • arch/arm/boot/zImage: 压缩的内核镜像
  • arch/arm/boot/dts/*.dtb: 编译后的设备树二进制文件
  • 各个目录下的 *.ko 文件: 内核模块

5. 根文件系统的制作

5.1 使用 Buildroot 构建根文件系统

Buildroot 是一个自动化构建工具,可以帮助我们快速生成定制的根文件系统。

首先获取 Buildroot 源码:

git clone https://github.com/buildroot/buildroot.git
cd buildroot
git checkout 2021.02  # 选择稳定版本

配置 Buildroot:

make menuconfig

在配置界面中,需要设置以下关键选项:

Target options: 选择目标架构(如 ARM)、CPU 类型、浮点运算方式等。

Toolchain: 选择使用外部工具链还是让 Buildroot 自动构建工具链。

我通常选择使用外部工具链,因为这样编译速度更快。

System configuration: 配置系统主机名、root 密码、启动脚本等。

Filesystem images: 选择生成的文件系统格式,如 ext4、tar、cpio 等。

Package Selection: 选择需要包含的软件包,如 busybox、网络工具、开发库等。

配置完成后执行编译:

make -j8

编译过程可能需要较长时间,因为 Buildroot 会下载并编译所有选中的软件包。

编译完成后,生成的根文件系统位于 output/images 目录下。

5.2 手动构建根文件系统

除了使用 Buildroot,我们也可以手动构建根文件系统。

这种方式更加灵活,但需要更多的工作量。基本步骤如下:

# 创建根文件系统目录结构
mkdir rootfs
cd rootfs
mkdir -p bin sbin lib usr/bin usr/sbin usr/lib etc dev proc sys tmp var home root

# 安装busybox
cd /path/to/busybox
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- CONFIG_PREFIX=/path/to/rootfs install

# 复制必要的库文件
cd /path/to/toolchain/arm-linux-gnueabihf/libc/lib
cp -a *.so* /path/to/rootfs/lib/

# 创建设备节点
cd /path/to/rootfs/dev
sudo mknod console c 5 1
sudo mknod null c 1 3

5.3 配置根文件系统

创建基本的配置文件,如 /etc/inittab/etc/fstab 等:

# /etc/inittab
::sysinit:/etc/init.d/rcS
::respawn:/sbin/getty -L ttySTM0 115200 vt100
::shutdown:/bin/umount -a -r
::restart:/sbin/init

# /etc/fstab
proc    /proc   proc    defaults    0   0
sysfs   /sys    sysfs   defaults    0   0
tmpfs   /tmp    tmpfs   defaults    0   0

# /etc/init.d/rcS
#!/bin/sh
mount -a
echo "System initialization complete"

这些配置文件定义了系统启动流程、文件系统挂载等基本行为。

6. 系统烧写与启动

6.1 制作 SD 卡启动盘

将编译好的各个组件烧写到 SD 卡中:

# 分区(假设SD卡为/dev/sdb)
sudo fdisk /dev/sdb
# 创建两个分区:
# 第一个分区: 100MB, FAT32, 用于存放内核和设备树
# 第二个分区: 剩余空间, ext4, 用于根文件系统

# 格式化分区
sudo mkfs.vfat /dev/sdb1
sudo mkfs.ext4 /dev/sdb2

# 挂载分区
sudo mount /dev/sdb1 /mnt/boot
sudo mount /dev/sdb2 /mnt/rootfs

# 复制文件
sudo cp zImage /mnt/boot/
sudo cp stm32mp157c-myboard.dtb /mnt/boot/
sudo cp -a rootfs/* /mnt/rootfs/

# 卸载
sudo umount /mnt/boot
sudo umount /mnt/rootfs

6.2 配置 U-Boot 启动参数

将 SD 卡插入开发板,上电启动,进入 U-Boot 命令行,配置启动参数:

# 设置bootargs
setenv bootargs 'console=ttySTM0,115200 root=/dev/mmcblk0p2 rootwait rw'

# 设置bootcmd
setenv bootcmd 'fatload mmc 0:1 0xc2000000 zImage; fatload mmc 0:1 0xc4000000 stm32mp157c-myboard.dtb; bootz 0xc2000000 - 0xc4000000'

# 保存环境变量
saveenv

# 启动系统
boot

如果一切配置正确,系统应该能够正常启动,最终进入登录界面。

7. 系统优化与调试

7.1 启动时间优化

在很多应用场景中,快速启动是一个重要需求。

可以通过以下方法优化启动时间:

精简内核: 去掉不必要的驱动和功能模块,减小内核体积。

并行启动: 修改启动脚本,让多个服务并行启动而不是串行启动。

使用静态链接: 减少动态库加载时间。

延迟加载: 将非关键服务延迟到系统启动后再加载。

我在一个车载项目中,通过这些优化手段,将系统启动时间从 15 秒缩短到了 5 秒以内。

7.2 调试技巧

在开发过程中,难免会遇到各种问题。以下是一些常用的调试技巧:

串口调试: 这是最基本也是最重要的调试手段。

通过串口可以看到内核启动日志、应用程序输出等信息。

网络调试: 配置好网络后,可以通过 SSH 远程登录系统,使用 gdb 进行远程调试。

内核日志: 使用 dmesg 命令查看内核日志,分析驱动加载、硬件初始化等信息。

procfs 和 sysfs: 通过 /proc/sys 目录可以查看系统运行状态、硬件信息等。

# 查看CPU信息
cat /proc/cpuinfo

# 查看内存使用情况
cat /proc/meminfo

# 查看设备树信息
ls /proc/device-tree/

# 查看GPIO状态
cat /sys/class/gpio/export

7.3 性能优化

对于性能要求较高的应用,可以从以下几个方面进行优化:

编译器优化: 使用 -O2-O3 优化级别编译应用程序。

实时性优化: 如果需要实时性,可以使用 RT-Preempt 补丁,或者配置内核为低延迟模式。

内存管理: 合理配置内存分配器,使用内存池等技术减少内存碎片。

I/O 优化: 使用 DMA 传输、异步 I/O 等技术提高数据传输效率。

在我之前的项目中,通过这些优化,将 CAN 总线的响应延迟从 10ms 降低到了 2ms 以内,大大提升了系统的实时性能。

8. 总结

嵌入式 Linux 系统的安装和配置是一个系统工程,涉及到 Bootloader、内核、根文件系统等多个组成部分。

虽然过程看起来复杂,但只要按照正确的步骤一步步来,并在实践中不断积累经验,就能够熟练掌握。

我从最初接触嵌入式 Linux 到现在,也是经历了无数次的失败和调试,才逐渐摸索出了一套高效的开发流程。

希望这篇文章能够帮助大家少走一些弯路,更快地掌握嵌入式 Linux 系统的开发技能。

在实际项目中,我们还需要根据具体的硬件平台和应用需求,对系统进行深度定制和优化。

这需要我们不断学习新的技术,积累更多的实战经验。

嵌入式 Linux 的世界博大精深,值得我们持续探索和钻研。

更多编程学习资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

良许Linux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值