
1. 项目缘起为什么要在边缘设备上折腾实时内核最近在搞一个基于NVIDIA Jetson的边缘计算项目涉及到高速视觉检测和机械臂的协同控制。项目原型在标准Linux内核上跑得还行但一到高负载、多线程并发的时候偶尔就会出现几毫秒甚至十几毫秒的延迟抖动。对于需要精确时序控制的场景比如要求每10毫秒稳定触发一次拍照和计算这种抖动是致命的——它直接导致图像采集与机械臂动作不同步轻则产品良率下降重则可能引发设备碰撞。排查了一圈从应用层代码优化到系统负载监控最终问题指向了内核调度器的非确定性延迟。标准Linux内核比如Ubuntu默认的通用内核为了追求整体吞吐量和公平性其调度、中断处理、内存管理等行为并非完全“实时”。这就引出了我们今天要折腾的主角PREEMPT_RTReal-Time补丁。简单说它通过一系列深度修改将Linux内核变成一个“硬实时”或“软实时”系统大幅降低任务调度的最坏情况延迟使其变得可预测。我手头的设备是Seeed推出的reComputer Jetson系列搭载了NVIDIA最新的JetPack 6.2.1基于Ubuntu 22.04。官方镜像默认不包含RT内核。虽然NVIDIA为部分Jetson模块提供了预编译的RT内核镜像但对于reComputer这类载板或者需要自定义内核配置的情况从源码开始交叉编译和烧录是更稳妥、也更深入理解系统的方式。这个过程涉及BSPBoard Support Package获取、内核配置、设备树编译、模块打包等一系列步骤任何一个环节出错都可能导致设备无法启动。接下来我就把在reComputer Jetson Orin NX上成功烧录PREEMPT_RT内核的完整过程、踩过的坑以及核心原理梳理出来。2. 环境准备与核心概念澄清在动手之前我们需要准备好“战场”并理解几个关键概念这能帮你避开很多后续的迷惑。2.1 硬件与软件清单目标设备Target: Seeed reComputer Jetson Orin NX16GB版本。其核心是Jetson Orin NX模组。不同Jetson模组如Orin NX, Orin Nano, AGX Orin的BSP和内核配置可能有差异本文流程主要针对Orin NX系列但思路通用。开发主机Host: 一台x86_64架构的Ubuntu 22.04 LTS电脑。这是NVIDIA官方推荐的开发环境。虚拟机理论上可行但涉及USB烧录和大量编译物理机或性能强大的虚拟机更佳。关键软件:JetPack 6.2.1 BSP 和根文件系统: 这是为你的特定Jetson模组定制的软件包包含U-Boot引导程序、内核源码、设备树、驱动模块和基本的根文件系统。你需要从NVIDIA开发者网站下载。L4TLinux for Tegra工具链: 主要是nvflash和nv_boot_control等工具用于将编译好的镜像烧录到设备。ARM64交叉编译工具链: 因为Jetson是ARM64aarch64架构我们需要在x86主机上安装对应的gcc等工具来编译内核。PREEMPT_RT补丁: 对应特定Linux内核版本的实时补丁文件。注意确保开发主机有充足的磁盘空间建议至少100GB空闲因为内核源码、编译中间文件和根文件系统会占用大量空间。2.2 PREEMPT_RT 补丁与内核版本绑定这是第一个容易踩坑的点。PREEMPT_RT补丁高度依赖特定的Linux内核版本。你不能随便拿一个5.15版本的补丁打到5.10的内核上。JetPack 6.2.1 默认使用的内核版本是5.10.120-tegra可以通过uname -r在设备上查看。因此我们必须寻找与5.10.120版本号精确匹配的PREEMPT_RT补丁。补丁通常以.patch.xz或.patch.gz格式发布在 kernel.org 或实时内核的维基页面上。你需要找到类似patch-5.10.120-rt.patch.xz这样的文件。如果找不到完全一致的版本可能需要寻找最接近的版本如5.10.119-rt并尝试手动调整但这会引入风险强烈建议使用完全匹配的版本。2.3 烧录模式Force Recovery Mode 是关键Jetson设备有两种主要的启动模式正常模式Normal Mode: 从eMMC或NVMe存储中加载系统。强制恢复模式Force Recovery Mode: 设备上电时按住Force Recovery按钮在reComputer上通常是一个标有“REC”或“FRC”的小按钮不松然后短按一下Reset按钮再松开Force Recovery按钮。此时设备会进入一个特殊的USB恢复状态等待主机通过nvflash工具发送镜像。我们整个烧录过程包括刷写引导程序、内核、设备树等都必须在强制恢复模式下进行。这是与普通PC刷BIOS或树莓派烧录SD卡截然不同的地方。3. 一步步获取与准备源码有了清晰的概念我们开始实操。第一步是获取所有必要的源代码和工具。3.1 下载 JetPack 6.2.1 BSP访问 NVIDIA 开发者网站的 Jetson 下载中心。找到JetPack 6.2.1的下载链接。对于 Orin NX你需要下载Jetson Orin NX/T234 BSP包文件名可能类似于Jetson_Linux_R36.2.1_aarch64.tbz2。这个压缩包包含了我们需要的所有基础组件。同时下载对应的根文件系统Root Filesystem例如Tegra_Linux_Sample-Root-Filesystem_R36.2.1_aarch64.tbz2。# 在开发主机上创建一个工作目录 mkdir -p ~/jetson-rt-kernel cd ~/jetson-rt-kernel # 假设你将下载的BSP和根文件系统放到了当前目录 # 解压BSP包这会创建一个 Linux_for_Tegra 目录 tar -xjf Jetson_Linux_R36.2.1_aarch64.tbz2 # 解压根文件系统到指定位置 cd Linux_for_Tegra/rootfs/ sudo tar -xjf ../../Tegra_Linux_Sample-Root-Filesystem_R36.2.1_aarch64.tbz2 cd ..3.2 获取并应用 PREEMPT_RT 补丁进入内核源码目录。在Linux_for_Tegra/source/public/下你应该能找到kernel_src.tbz2解压它。cd ~/jetson-rt-kernel/Linux_for_Tegra/source/public tar -xjf kernel_src.tbz2 cd kernel/kernel-5.10这个kernel-5.10目录就是标准内核源码。下载对应版本的 PREEMPT_RT 补丁。你可以从 https://cdn.kernel.org/pub/linux/kernel/projects/rt/5.10/ 寻找。假设我们找到了patch-5.10.120-rt.patch.xz。wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/5.10/patch-5.10.120-rt.patch.xz应用补丁。务必在干净的源码上操作。# 解压补丁文件 xz -cd patch-5.10.120-rt.patch.xz | patch -p1如果补丁应用成功你会看到大量文件被修改的提示。如果出现大量“FAILED”或“Hunk”错误说明补丁版本不匹配需要解决冲突这非常棘手。因此版本匹配至关重要。3.3 配置交叉编译环境我们需要安装ARM64的交叉编译工具链。NVIDIA推荐使用Linaro或Bootlin的工具链。这里以Bootlin的稳定版本为例# 安装必要的依赖 sudo apt-get update sudo apt-get install build-essential bc kmod cpio flex libncurses5-dev libssl-dev # 下载并解压aarch64工具链 wget https://toolchains.bootlin.com/downloads/releases/toolchains/aarch64/tarballs/aarch64--glibc--stable-2023.08-1.tar.bz2 tar -xjf aarch64--glibc--stable-2023.08-1.tar.bz2 -C ~/ # 将工具链路径加入环境变量临时或写入~/.bashrc export CROSS_COMPILE~/aarch64--glibc--stable-2023.08-1/bin/aarch64-linux- export ARCHarm644. 内核配置、编译与打包这是最核心也最耗时的步骤配置选项直接决定了内核的特性和能否正常启动。4.1 获取并调整内核配置Jetson BSP通常提供了一个默认的配置文件。我们基于它进行修改。# 回到内核源码目录 cd ~/jetson-rt-kernel/Linux_for_Tegra/source/public/kernel/kernel-5.10 # 获取NVIDIA提供的默认配置 make O$PWD/build-tegra ARCHarm64 CROSS_COMPILE$CROSS_COMPILE tegra_defconfig # 进入交互式配置菜单在默认配置上启用RT特性 make O$PWD/build-tegra ARCHarm64 CROSS_COMPILE$CROSS_COMPILE menuconfig在menuconfig界面中你需要重点关注以下几个配置项General setup - Preemption Model:这是RT内核的核心。将选项从默认的Preemptible Kernel (Low-Latency Desktop)修改为Fully Preemptible Kernel (Real-Time)。这个选项会强制开启一系列子选项为RT内核铺平道路。Kernel Features - Timer frequency:提高定时器中断频率HZ可以降低调度粒度提高响应性。对于实时系统通常设置为1000 HZ。但注意更高的频率会增加一点系统开销。在Jetson上1000 HZ是个不错的起点。确保关键驱动被编译:使用/键搜索你的Jetson模组关键驱动如TEGRA、PCIe、USB、GPUNVIDIA Tegra GPU、ETHERNET等。确保它们被启用y或编译为模块m。特别是显示、网络和存储驱动必须包含。排查冲突选项:应用RT补丁后有些调试或特性选项可能与实时性冲突。留意配置中的警告信息。一个常见的需要关闭的选项是CONFIG_DEBUG_PREEMPT它在生产环境可能会引入额外开销可以先关掉。配置完成后保存退出。配置文件会保存在build-tegra/.config。4.2 编译内核与模块接下来开始漫长的编译过程。利用你主机所有核心可以加快速度-j$(nproc)。# 编译内核Image和压缩的Image.gz make O$PWD/build-tegra ARCHarm64 CROSS_COMPILE$CROSS_COMPILE -j$(nproc) Image Image.gz # 编译设备树Device Tree Blobs。设备树描述了硬件的拓扑结构对启动至关重要。 make O$PWD/build-tegra ARCHarm64 CROSS_COMPILE$CROSS_COMPILE -j$(nproc) dtbs # 编译所有内核模块 make O$PWD/build-tegra ARCHarm64 CROSS_COMPILE$CROSS_COMPILE -j$(nproc) modules # 创建模块安装的临时目录 mkdir -p $PWD/build-tegra/modules_install # 安装模块到临时目录 make O$PWD/build-tegra ARCHarm64 CROSS_COMPILE$CROSS_COMPILE modules_install INSTALL_MOD_PATH$PWD/build-tegra/modules_install编译成功后关键产出物在build-tegra/arch/arm64/boot/下Image和Image.gz设备树在build-tegra/arch/arm64/boot/dts/nvidia/下如tegra234-p3767-0000-p3768-0000-a0.dtb模块则在build-tegra/modules_install/lib/modules/5.10.120-rt-...下。4.3 替换BSP中的内核文件并打包模块现在我们需要用新编译的文件替换掉BSP准备的标准文件。# 回到Linux_for_Tegra根目录 cd ~/jetson-rt-kernel/Linux_for_Tegra # 备份原始内核可选但建议 cp kernel/Image kernel/Image.orig cp kernel/Image.gz kernel/Image.gz.orig cp kernel/dtb/tegra234-p3767-0000-p3768-0000-a0.dtb kernel/dtb/tegra234-p3767-0000-p3768-0000-a0.dtb.orig # 复制新编译的内核Image和设备树 cp source/public/kernel/kernel-5.10/build-tegra/arch/arm64/boot/Image kernel/ cp source/public/kernel/kernel-5.10/build-tegra/arch/arm64/boot/Image.gz kernel/ cp source/public/kernel/kernel-5.10/build-tegra/arch/arm64/boot/dts/nvidia/tegra234-p3767-0000-p3768-0000-a0.dtb kernel/dtb/ # 处理内核模块先清理根文件系统中的旧模块再安装新模块 sudo rm -rf rootfs/lib/modules/* sudo cp -rav source/public/kernel/kernel-5.10/build-tegra/modules_install/lib/modules/* rootfs/lib/modules/5. 烧录镜像到 reComputer Jetson所有文件准备就绪进入最紧张的烧录环节。5.1 进入强制恢复模式并连接断开reComputer Jetson的电源。用Micro-USB数据线注意是用于烧录的USB口不是普通的USB-A口将reComputer的Recovery USB口连接到开发主机。按住reComputer板子上的Force Recovery按钮不放。保持按住Force Recovery按钮短按一下Reset按钮。等待2秒左右松开Force Recovery按钮。在开发主机上执行lsusb命令你应该能看到一个NVIDIA Corp.相关的设备ID可能为0955:7323这表明设备已成功进入恢复模式并被主机识别。5.2 执行烧录脚本NVIDIA BSP提供了方便的烧录脚本。在烧录前建议先尝试“试运行”模式检查配置是否正确。cd ~/jetson-rt-kernel/Linux_for_Tegra # 首先运行flash.sh脚本但指定--no-flash参数它会准备所有镜像文件但不真正烧写 sudo ./flash.sh jetson-orin-nx-devkit mmcblk0p1 # 如果上一步没有报错并且生成了所有必要的镜像文件则可以开始正式烧录。 # 确保设备处于强制恢复模式然后执行不加--no-flash sudo ./flash.sh jetson-orin-nx-devkit mmcblk0p1flash.sh脚本会自动调用nvflash工具依次烧写引导加载程序U-Boot、内核、设备树、根文件系统等到设备的eMMC存储中。整个过程会在终端显示进度耗时几分钟。期间绝对不要断开USB连接或给设备断电。5.3 首次启动与验证烧录完成后脚本通常会提示你给设备重新上电。断开USB烧录线连接显示器和键盘或通过串口调试然后给reComputer上电。观察启动日志如果一切顺利你会看到U-Boot的启动信息接着内核开始解压、加载驱动最后进入Ubuntu登录界面。验证内核版本登录系统后打开终端执行uname -r输出应该包含rt字样例如5.10.120-rt-...这表明实时内核正在运行。测试实时性基础安装cyclictest工具进行简单的延迟测试。sudo apt-get update sudo apt-get install rt-tests # 运行一个简单的测试持续10秒优先级80 sudo cyclictest -t -p 80 -n -i 1000 -l 10000观察输出的Max Latency最大延迟值。在标准内核上这个值可能在几十到几百微秒甚至毫秒级波动。在正确配置的RT内核上最大延迟应该显著降低且更加稳定理想情况下在几十微秒以内。注意cyclictest本身运行在高优先级可能会影响系统其他任务这只是一个初步的定性测试。6. 烧录后的关键配置与性能调优成功启动RT内核只是第一步。要让实时应用真正受益还需要进行一系列系统配置。6.1 隔离CPU核心为了防止非实时任务如桌面环境、后台服务干扰实时线程标准的做法是将一个或多个CPU核心隔离出来专供实时任务使用。修改内核启动参数。编辑/boot/extlinux/extlinux.conf在Jetson上通常是这个路径sudo vim /boot/extlinux/extlinux.conf在APPEND那一行找到root参数后面添加isolcpus1-3假设你想隔离CPU1, CPU2, CPU3。例如APPEND ${cbootargs} quiet root/dev/mmcblk0p1 rw rootwait rootfstypeext4 consolettyTCU0,115200n8 consoletty0 fbconmap:0 isolcpus1-3重启生效。使用taskset将实时进程绑定到隔离的CPU上。例如将你的实时应用进程绑定到CPU1taskset -c 1 ./your_rt_application6.2 调整进程调度策略与优先级对于关键的实时线程在编程时应该使用SCHED_FIFO或SCHED_RR调度策略并赋予较高的静态优先级1-99数字越大优先级越高。// C语言示例代码片段 #include sched.h #include pthread.h void set_realtime_priority() { struct sched_param param; param.sched_priority 80; // 设置优先级为80 if (sched_setscheduler(0, SCHED_FIFO, param) -1) { perror(sched_setscheduler failed); // 处理错误可能需要CAP_SYS_NICE能力 } }记得给执行此代码的程序赋予相应的Linux能力Capability或者直接以root权限运行不推荐生产环境。6.3 禁用频率调节与Turbo模式CPU频率的动态调节DVFS和自动降频会引入不可预测的延迟。对于实时核心可以将其调节器设置为performance模式并禁用Turbo Boost如果存在以获得更稳定的性能。# 查看所有CPU的当前调节器 cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor # 将隔离的CPU例如cpu1设置为performance模式 echo performance | sudo tee /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor # 对于Jetson Orin可能需要通过nvpmodel来固定功耗和频率模式 sudo /usr/sbin/nvpmodel -m 0 # 模式0通常是MAX-N模式所有核心最高性能注意固定高性能模式会显著增加功耗和发热需要做好散热。6.4 内存锁定与避免换出实时任务的内存页应该被锁定在物理RAM中防止被换出到交换分区否则换入换出操作会导致巨大的延迟。在程序中使用mlockall()系统调用#include sys/mman.h if (mlockall(MCL_CURRENT | MCL_FUTURE) -1) { perror(mlockall failed); }同样这需要CAP_IPC_LOCK能力或root权限。7. 实战排坑我遇到的那些“坑”与解决方案没有一次成功的烧录是不踩坑的。下面是我在过程中遇到的几个典型问题及其解决方法。7.1 坑一设备树编译错误或版本不匹配现象make dtbs时出现大量未定义的引用错误或者烧录后设备无法启动串口日志卡在某个驱动初始化。根因内核源码目录下的设备树源文件.dts或.dtsi可能与当前内核版本或BSP不兼容。特别是如果你从其他渠道获取了内核源码或补丁。解决使用BSP包内的原生源码这是最稳妥的。确保你的kernel-5.10目录完全来自kernel_src.tbz2没有混入其他版本的代码。检查补丁应用确保RT补丁应用成功没有因为冲突而部分失败。可以尝试patch -p1 --dry-run patchfile先进行试运行。对比官方配置使用tegra_defconfig作为基础只增量修改RT相关选项不要随意改动其他平台相关的配置。7.2 坑二烧录后卡在“Starting kernel ...”或黑屏现象U-Boot阶段正常显示“Starting kernel ...”后就没有任何输出屏幕黑屏。根因这通常是由编译的内核或设备树与硬件不匹配导致。最常见的是错误的设备树文件flash.sh脚本烧录了错误的.dtb文件。Jetson Orin NX 和 Orin Nano 的设备树名称不同reComputer的载板也可能有细微差别。显示驱动问题内核中显示驱动如NVIDIA Tegra DRM未正确编译或配置。解决确认设备树名称查看Linux_for_Tegra/bootloader/t186ref/或相关目录下的配置文件确认你的设备对应的确切设备树名称。对于reComputer Jetson Orin NX通常是tegra234-p3767-0000-p3768-0000-a0.dtb。检查内核配置重新运行make menuconfig确保以下关键驱动已启用CONFIG_TEGRA_HOST1X,CONFIG_DRM_TEGRACONFIG_PCIE_TEGRA194(对于PCIe设备)你的具体存储控制器驱动如CONFIG_MMC_SDHCI_TEGRA使用串口调试通过UART串口连接reComputer的调试口通常标有DEBUG在电脑上用串口工具如minicom,screen查看更详细的启动日志这能精准定位卡在哪一步。7.3 坑三系统启动后cyclictest延迟依然很高现象RT内核成功启动但cyclictest测试的最大延迟Max Latency仍在几百微秒以上没有达到预期效果。根因实时内核只是提供了低延迟的基础但系统上运行的其他中断和任务仍然会抢占CPU。常见干扰源包括其他高优先级中断如网络中断irq/eth0、USB控制器中断等。内核线程一些内核后台线程ksoftirqd,rcu_sched等可能运行在隔离的CPU上。电源管理特性intel_idlex86或ARM的CPU空闲状态cpuidle在进入深睡眠状态C-state后唤醒需要时间。BIOS/Firmware设置在某些平台上需要禁用C-state和P-state调节。解决中断隔离将除了实时任务所需之外的所有中断都绑定到非隔离的CPU上。例如将网络中断irq/eth0绑定到CPU0echo 1 | sudo tee /proc/irq/eth0_irq_num/smp_affinity需要先cat /proc/interrupts找到对应中断号。禁用看门狗/dev/watchdog相关的内核线程可能会定时唤醒。可以尝试临时禁用或将其绑定到非实时CPU。调整内核启动参数尝试添加rcu_nocbs1-3将RCU回调从隔离CPU卸载、nohz_full1-3在隔离CPU上启用完全无滴答、irqaffinity0将所有中断默认绑定到CPU0。使用tuna或chrt工具在运行时动态调整进程的调度策略和优先级确保实时进程获得最高调度权。进行更专业的基准测试cyclictest是基础工具。对于更严谨的评估可以考虑使用stress工具施加系统负载同时运行cyclictest来观察在最坏情况下的延迟表现。烧录PREEMPT_RT内核到嵌入式边缘设备如Jetson是一个从系统底层提升确定性的有效手段但它不是“银弹”。它需要你深入理解整个软件栈——从引导加载程序、内核配置、设备树到应用层的调度策略。整个过程就像给一台高性能跑车更换了更精准的赛车级电控系统和悬挂但你需要一名专业的技师开发者来调校它才能让它在赛道上你的实时应用场景发挥出全部潜力。希望这份详细的记录能帮你少走弯路顺利踏上边缘实时计算的道路。如果在具体操作中遇到新的问题多查看串口日志、多分析内核配置社区的资源和NVIDIA的官方论坛也是很好的求助渠道。