3. 开发环境搭建

实时内核 PREEMPT_RT 的移植与调优离不开一套可靠、可复现的开发环境。本章将指导你从零搭建完整的嵌入式 Linux 实时开发栈,涵盖交叉编译工具链、QEMU 模拟器、物理目标板(Raspberry Pi / BeagleBone)以及 TFTP/NFS 网络引导环境。每个环节都经过实测验证,确保后续章节的例程可以无缝运行。

环境对齐建议: 推荐使用 Ubuntu 22.04 LTS 作为宿主机。所有工具链和依赖包均通过 apt 或源码编译安装,避免使用容器化环境带来的内核模块加载限制。
PREEMPT_RT 开发环境整体架构 宿主机 (Ubuntu 22.04) 交叉编译工具链 QEMU 模拟器 TFTP / NFS 服务 UART/网络 QEMU 模拟目标 virt / versatilepb PREEMPT_RT 内核 根文件系统 (initramfs) SD卡/串口/网络 物理目标板 RPi 3B+ / BeagleBone Black 网络引导服务 TFTP: 内核 + DTB NFS: 根文件系统 DHCP: IP 分配 PXE/BOOTP 可选 开发工作流 ① 宿主机编译内核 ② 部署到目标/模拟器 ③ 串口/网络调试

3.1 交叉编译工具链安装

嵌入式目标板资源受限,无法本地编译大型内核源码,必须在宿主机上使用交叉编译工具链生成目标架构的二进制文件。本节以 aarch64(Raspberry Pi 3B+)和 armv7l(BeagleBone Black)为例。

3.1.1 工具链选择对比

工具链 目标架构 C库 适用场景 安装方式
gcc-arm-10.3-2021.07 armv7l / aarch64 glibc 通用 Linux 内核 + 应用 tar.xz 解压
Linaro GCC 7.5 armv7l glibc BeagleBone 传统 BSP apt / 源码
Buildroot 工具链 armv7l / aarch64 glibc / uclibc 定制根文件系统 Buildroot 编译
Yocto SDK aarch64 glibc 企业级 BSP 开发 Yocto 构建
推荐选择: 对于 PREEMPT_RT 内核移植,使用 ARM 官方发布的 gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu 工具链,兼容性最好,且支持 C11/C++17 标准。

3.1.2 安装步骤(以 aarch64 为例)

  1. 下载工具链:从 ARM 开发者网站获取预编译包。
  2. 解压到指定目录:建议统一放在 /opt/toolchains/ 下。
  3. 设置环境变量:将工具链的 bin 目录加入 PATH。
  4. 验证安装:运行 aarch64-none-linux-gnu-gcc --version。
# 下载 aarch64 工具链
wget https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz

# 解压到 /opt
sudo tar -xf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt/toolchains/

# 设置环境变量(建议写入 ~/.bashrc)
export PATH=/opt/toolchains/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH
export CROSS_COMPILE=aarch64-none-linux-gnu-
export ARCH=arm64

# 验证
aarch64-none-linux-gnu-gcc --version
# 输出示例:aarch64-none-linux-gnu-gcc (GCC) 10.3.1 20210621
避坑提示: 不要使用 Ubuntu 自带 gcc-arm-linux-gnueabihf 编译内核,其版本通常较旧(7.5),且缺少 __atomic 支持,会导致 PREEMPT_RT 的 futex 相关代码编译失败。

3.2 QEMU 模拟器配置

QEMU 可以在没有物理开发板的情况下验证 PREEMPT_RT 内核的启动和实时性。本节配置 qemu-system-aarch64 模拟 virt 平台,支持 GICv3 中断控制器,适合测试 PREEMPT_RT 的抢占延迟。

3.2.1 安装 QEMU 及相关依赖

# 安装 QEMU 完整包
sudo apt update
sudo apt install qemu-system-arm qemu-efi-aarch64 qemu-utils libvirt-daemon-system

# 验证安装
qemu-system-aarch64 --version
# 输出示例:QEMU emulator version 7.2.0

3.2.2 启动脚本模板

以下脚本启动一个带有 2 个 CPU 核心、2GB 内存的 virt 机器,使用 PREEMPT_RT 内核和 initramfs 根文件系统。

#!/bin/bash
# qemu_rt.sh - 启动 PREEMPT_RT 内核

KERNEL=./arch/arm64/boot/Image
DTB=./arch/arm64/boot/dts/arm/virt.dtb
INITRAMFS=./rootfs.cpio.gz

qemu-system-aarch64 \
    -machine virt,gic-version=3 \
    -cpu cortex-a72 \
    -smp 2 \
    -m 2G \
    -kernel ${KERNEL} \
    -dtb ${DTB} \
    -initrd ${INITRAMFS} \
    -append "console=ttyAMA0 root=/dev/ram0 rw earlycon" \
    -nographic \
    -netdev user,id=net0,hostfwd=tcp::2222-:22 \
    -device virtio-net-pci,netdev=net0
调试技巧: 添加 -s -S 参数可让 QEMU 在启动时暂停,等待 GDB 连接(端口 1234),便于单步调试内核启动流程。
QEMU 启动 PREEMPT_RT 内核流程 QEMU 初始化 加载 Kernel Image 解压 initramfs PREEMPT_RT 启动 关键配置参数 gic-version=3 | cpu=cortex-a72 | smp=2 | 串口重定向

3.3 目标板准备:Raspberry Pi 3B+ 与 BeagleBone Black

物理目标板用于最终验证实时性能。以下步骤确保板卡可以从 SD 卡启动自定义 PREEMPT_RT 内核。

3.3.1 Raspberry Pi 3B+ 准备

组件 要求 备注
SD 卡 ≥ 16GB,Class 10 建议使用 SanDisk Extreme
固件 raspberrypi-firmware 包含 bootcode.bin / start4.elf
串口线 3.3V USB-TTL 连接 GPIO 14/15 (UART0)
电源 5V / 2.5A USB-C 或 MicroUSB

3.3.2 制作可启动 SD 卡

# 1. 分区(假设 SD 卡为 /dev/sdb)
sudo fdisk /dev/sdb
# 创建 FAT32 分区(100MB),剩余为 ext4 分区

# 2. 格式化
sudo mkfs.vfat -F 32 /dev/sdb1
sudo mkfs.ext4 /dev/sdb2

# 3. 挂载并复制固件
sudo mount /dev/sdb1 /mnt/boot
sudo mount /dev/sdb2 /mnt/rootfs

# 4. 复制 Raspberry Pi 固件
git clone --depth=1 https://github.com/raspberrypi/firmware
sudo cp -r firmware/boot/* /mnt/boot/

# 5. 复制 PREEMPT_RT 内核
sudo cp arch/arm64/boot/Image /mnt/boot/kernel8.img
sudo cp arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dtb /mnt/boot/

# 6. 配置 config.txt
echo "kernel=kernel8.img" | sudo tee /mnt/boot/config.txt
echo "arm_64bit=1" | sudo tee -a /mnt/boot/config.txt

# 7. 卸载
sudo umount /mnt/boot /mnt/rootfs
注意: Raspberry Pi 3B+ 的 GPU 固件要求 kernel8.img 为 64 位内核。如果使用 32 位内核,需命名为 kernel7.img 并设置 arm_64bit=0。

3.4 TFTP/NFS 网络引导环境搭建

网络引导(Netboot)允许目标板从宿主机加载内核和根文件系统,极大加速开发迭代。本节搭建 DHCP + TFTP + NFS 三件套。

3.4.1 服务端配置(宿主机)

服务 端口 配置文件 作用
dnsmasq 67/68/69 /etc/dnsmasq.conf DHCP + TFTP 一体化
nfs-kernel-server 2049 /etc/exports 导出根文件系统

3.4.2 dnsmasq 配置

# 安装 dnsmasq
sudo apt install dnsmasq

# 编辑 /etc/dnsmasq.conf
interface=eth0
dhcp-range=192.168.1.100,192.168.1.200,12h
dhcp-option=3,192.168.1.1
dhcp-option=6,192.168.1.1
enable-tftp
tftp-root=/srv/tftp
dhcp-boot=Image

# 创建 TFTP 根目录并复制内核
sudo mkdir -p /srv/tftp
sudo cp arch/arm64/boot/Image /srv/tftp/
sudo cp arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dtb /srv/tftp/

# 重启服务
sudo systemctl restart dnsmasq

3.4.3 NFS 根文件系统导出

# 安装 NFS 服务器
sudo apt install nfs-kernel-server

# 编辑 /etc/exports
/srv/nfs/rootfs 192.168.1.0/24(rw,no_root_squash,no_subtree_check,sync)

# 创建根文件系统目录(使用 debootstrap 或 Buildroot)
sudo mkdir -p /srv/nfs/rootfs
sudo debootstrap --arch=arm64 bullseye /srv/nfs/rootfs

# 导出并重启 NFS
sudo exportfs -a
sudo systemctl restart nfs-kernel-server

3.4.4 目标板 U-Boot 引导参数

在目标板的 U-Boot 提示符下设置以下环境变量,实现网络引导:

setenv serverip 192.168.1.10
setenv ipaddr 192.168.1.101
setenv netmask 255.255.255.0
setenv gatewayip 192.168.1.1
setenv bootfile Image
setenv fdtfile bcm2710-rpi-3-b-plus.dtb
setenv rootpath /srv/nfs/rootfs

# 从 TFTP 加载内核和设备树
tftp ${kernel_addr_r} ${bootfile}
tftp ${fdt_addr_r} ${fdtfile}

# 设置内核命令行
setenv bootargs console=ttyAMA0,115200 root=/dev/nfs nfsroot=${serverip}:${rootpath},v3,tcp rw ip=${ipaddr}:::${gatewayip}:${netmask}::eth0:off

# 启动
booti ${kernel_addr_r} - ${fdt_addr_r}
快速验证: 在宿主机上使用 tftp 192.168.1.10 命令,然后执行 get Image,确认 TFTP 服务正常。NFS 可用 showmount -e 192.168.1.10 检查导出目录。
TFTP/NFS 网络引导交互流程 目标板 U-Boot 启动 DHCP 请求 IP 宿主机服务 dnsmasq (DHCP+TFTP) NFS 服务器 内核 + DTB + 根文件系统 ① DHCP Discover ② DHCP Offer (IP + TFTP) ③ TFTP 获取 Image + DTB ④ NFS 挂载根文件系统 网络引导优势 无需反复插拔 SD 卡 内核/DTB 秒级更新 根文件系统共享编辑

3.5 环境验证清单

完成上述搭建后,执行以下验证步骤,确保所有组件正常工作:

  • 工具链验证:编译一个简单的 Hello World 程序,在目标板上运行。
  • QEMU 验证:启动 PREEMPT_RT 内核,检查 /proc/sys/kernel/sched_rt_runtime_us 是否存在。
  • 目标板启动:从 SD 卡启动,确认内核日志包含 PREEMPT_RT 字样。
  • 网络引导验证:拔掉 SD 卡,目标板通过 TFTP/NFS 成功进入系统。
# 在目标板上验证实时内核
uname -a
# 输出应包含:PREEMPT RT

# 检查实时调度器参数
cat /proc/sys/kernel/sched_rt_runtime_us
# 输出:950000 (表示 95% 的 CPU 时间可分配给实时任务)

# 测试 cyclictest(需提前编译)
cyclictest -l 1000 -m -n -p 99
# 输出最大延迟应 < 50us(QEMU)或 < 20us(物理板)
环境就绪标志: 当你在目标板上执行 cyclictest 并看到稳定的微秒级延迟数据时,说明整个开发环境已成功搭建,可以进入下一阶段的实时内核配置与编译。