ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

为NVIDIA AGX Xavier/Orin构建实时Linux内核:从PREEMPT_RT补丁到性能调优

为NVIDIA AGX Xavier/Orin构建实时Linux内核:从PREEMPT_RT补丁到性能调优 1. 项目概述为什么需要为NVIDIA AGX打实时补丁如果你正在使用NVIDIA Jetson AGX Xavier或AGX Orin这类边缘计算平台并且你的应用场景涉及到机器人控制、自动驾驶、工业自动化等对时序有苛刻要求的领域那么“实时性”这个词对你来说一定不陌生。标准Linux内核包括NVIDIA JetPack SDK默认提供的内核其调度策略并非为硬实时Hard Real-Time设计。这意味着一个高优先级的任务可能会被内核中断、调度器或其它系统活动延迟响应这种延迟对于要求毫秒甚至微秒级确定性的系统来说是不可接受的。“给NVIDIA AGX打实时补丁R35.3.1”这个项目核心目标就是将标准的、通用的Linux内核改造为一个具备实时能力的“实时内核”。这里的“R35.3.1”指的是NVIDIA为L4TLinux for TegraR35.3.1版本提供的实时内核补丁。L4T R35.3.1是一个特定的JetPack版本基础基于Ubuntu 20.04和特定的内核版本。为这个特定版本打补丁意味着我们需要从源码开始编译构建一个专属于你AGX设备的、带有实时抢占PREEMPT_RT特性的内核与模块。这不仅仅是运行一个安装脚本那么简单。整个过程涉及获取官方源码、应用补丁、配置内核、交叉编译或在设备上本地编译、以及最终的部署和验证。它考验的是你对嵌入式Linux系统构建流程的熟悉程度。完成之后你的AGX平台将能够运行像ROS 2 with Real-TimeCyclone DDS Real-Time Linux这样的中间件或者直接部署需要严格时序保障的控制算法让硬件潜力在关键任务中真正释放出来。2. 核心需求解析什么样的场景必须上实时补丁在深入动手之前我们必须先厘清一个关键问题我的项目真的需要实时内核吗盲目上马实时补丁会带来额外的复杂性和潜在的稳定性风险毕竟你修改的是系统最核心的部分。通常以下几类场景是实时补丁的强需求者2.1 高精度闭环控制例如机械臂的关节伺服控制、无人机飞控、CNC机床运动控制。这些系统需要以极高的频率通常1kHz以上读取传感器数据如编码器、IMU计算控制律如PID并输出控制信号PWM。任何不可预测的延迟都可能导致控制环路失稳、产生抖动甚至引发安全事故。实时内核能确保控制线程的调度延迟被限制在几十微秒的确定范围内。2.2 确定性通信与中间件在ROS 2机器人系统中默认的DDS实现如Fast DDS在标准内核上可能受到系统负载影响导致消息传递时间Latency和抖动Jitter增大。当你将通信中间件如Cyclone DDS或RTI Connext DDS与实时内核结合并配合线程优先级设置可以极大提升节点间通信的确定性和可靠性这对于多节点协同的机器人集群至关重要。2.3 硬件同步与数据采集在视觉处理流水线中你可能需要精确同步多个相机通过GPIO触发或与其他传感器如激光雷达、IMU进行硬件级同步。标准内核的中断响应延迟不确定性可能导致帧捕获时间戳漂移或触发信号错位。实时内核能提供更精确的中断响应保障数据采集的时序完整性。2.4 低延迟音视频处理虽然AGX强大的GPU擅长处理高吞吐量数据但对于需要极低处理延迟的交互式AR/VR、实时语音处理应用CPU侧的处理流水线若被非实时任务打断也会影响整体体验。实时内核可以保证关键的音视频处理线程优先获得CPU资源。如果你的应用属于以上类别或者你正在为未来的功能扩展预留确定性计算能力那么进行内核实时化改造就是一项必要的基础工作。反之如果你的应用是纯计算密集型如离线模型训练、大数据批处理或对延迟不敏感那么标准内核可能是更简单、更稳定的选择。3. 环境准备与源码获取为AGX构建实时内核我们有两种主要的构建环境选择在AGX设备本机上直接编译或者在一台x86_64的Linux主机上进行交叉编译。本地编译简单直接但耗时长且编译过程会严重占用开发板资源交叉编译速度快效率高但对主机环境有一定要求。这里我强烈推荐使用交叉编译这也是NVIDIA官方推荐的方式。3.1 主机开发环境搭建你的主机需要是一台运行Ubuntu 20.04或22.04的x86_64电脑虚拟机或物理机均可。首先安装必要的依赖包sudo apt-get update sudo apt-get install -y build-essential bc kmod cpio flex libncurses5-dev libelf-dev libssl-dev dwarves bison rsync git这些工具包括编译器、内核构建工具、库文件等是编译内核的基础。接下来你需要安装NVIDIA提供的特定交叉编译工具链。工具链的版本必须与L4T版本严格匹配。对于R35.3.1我们需要安装gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu。你可以从NVIDIA开发者网站或Linaro官网下载。假设你下载后解压到了/opt/linaro目录则需要将其加入PATH环境变量export CROSS_COMPILE/opt/linaro/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- export ARCHarm64为了方便建议将这两行写入你的~/.bashrc文件。3.2 获取L4T内核源码与实时补丁NVIDIA通过源代码发布Source Release的方式提供内核源码。你需要前往NVIDIA开发者网站的“下载”部分找到对应Jetson AGX Xavier或Orin以及L4T R35.3.1的“Kernel Sources”压缩包进行下载。通常文件名类似于public_sources.tbz2。同时你还需要下载对应版本的“实时补丁”文件。这个补丁文件通常独立于内核源码提供在下载页面的“BSP Sources”或额外组件列表中寻找文件名可能包含RT或real-time字样例如kernel-rt-patch-R35.3.1.tar.gz。请务必确认补丁版本与你的L4T版本R35.3.1完全一致版本不匹配会导致补丁应用失败甚至编译出无法启动的内核。下载完成后在主机构建目录中解压内核源码tar -xjf public_sources.tbz2 cd Linux_for_Tegra/source/public tar -xjf kernel_src.tbz2此时你会进入内核源码目录例如kernel-5.10。3.3 应用实时补丁进入内核源码根目录应用你下载的实时补丁。补丁通常是一个.patch文件或一系列补丁文件。使用patch命令应用# 假设补丁文件位于上级目录名为 kernel-rt-R35.3.1.patch patch -p1 ../kernel-rt-R35.3.1.patch应用过程中请仔细观察输出确保所有补丁都成功应用没有“FAILED”或“rejected”提示。如果出现大量失败首先检查补丁版本是否正确。应用成功后内核配置选项中将会出现实时相关的选项。注意这是一个关键且容易出错的步骤。我建议在应用补丁前先使用git init和git add .将纯净的源码初始化为一个本地git仓库然后git commit -m 原始内核。这样如果补丁应用失败你可以轻松地使用git reset --hard回滚到原始状态无需重新解压。4. 内核配置与实时选项详解内核配置是构建过程中的核心决策环节它决定了哪些功能被编译进内核、哪些作为模块、以及内核的行为特性。对于实时内核我们需要重点关注抢占模型Preemption Model和相关调试选项。4.1 获取默认配置并启动配置界面首先我们需要获取NVIDIA为你的AGX设备提供的默认配置文件。这些配置文件通常位于Linux_for_Tegra/kernel/目录下文件名如tegra_defconfig。更稳妥的方式是从一台已经运行R35.3.1系统的AGX设备上导出当前配置如果你有的话# 在AGX设备上执行 zcat /proc/config.gz .config # 然后将.config文件拷贝到主机的内核源码目录如果无法获取则使用NVIDIA提供的默认defconfig。在内核源码目录下执行# 对于Jetson AGX Xavier make ARCHarm64 CROSS_COMPILE$CROSS_COMPILE tegra_defconfig # 或者对于Jetson AGX Orin可能需要使用 tegra_android_defconfig 或其他请参考NVIDIA文档然后启动图形化配置界面需要主机安装libncurses5-devmake ARCHarm64 CROSS_COMPILE$CROSS_COMPILE menuconfig4.2 关键实时配置选项在menuconfig界面中使用方向键导航回车键进入子菜单空格键切换选择[*]表示编译进内核[M]表示编译为模块[ ]表示不编译。General setup - Preemption Model (完全可抢占内核实时这是最核心的选项。进入后你会看到几个选择No Forced Preemption (Server) 无强制抢占适用于服务器。Voluntary Kernel Preemption (Desktop) 自愿内核抢占适用于桌面。Preemptible Kernel (Low-Latency Desktop) 可抢占内核低延迟桌面。Fully Preemptible Kernel (Real-Time)完全可抢占内核实时。 你必须选择最后一项Fully Preemptible Kernel (Real-Time)。这会将内核转变为真正的实时抢占内核。Timer frequency进入Kernel Features - Timer frequency。更高的定时器频率HZ意味着更细粒度的时间片和更快的定时器中断响应这有助于降低调度延迟。对于实时系统建议设置为1000 HZ。这能提供1毫秒的定时器精度。注意更高的频率会增加一些系统开销。RCURead-Copy-Update配置RCU是一种同步机制在实时系统中其回调函数可能会引入延迟。为了减少这种延迟我们需要调整RCU的配置。进入Kernel Features - RCU Subsystem。找到RCU priority boosting启用它设置为[*]。这可以提升RCU回调线程的优先级防止其阻塞高优先级实时任务。将Kernel .config support和Enable RT to block on RCU等选项保持为默认或根据提示调整。通常实时补丁会为RCU提供一些优化选项请留意。中断线程化Threaded IRQs实时补丁的一个关键特性是将大部分硬件中断处理程序转化为内核线程threaded IRQs这使得它们可以被优先级更高的实时线程抢占。这个功能通常是默认启用的但建议确认在General setup - IRQ subsystem中确保Threaded IRQ core是启用的。在Device Drivers各个子模块下某些特定设备驱动可能有关闭中断线程化的选项除非有特殊原因否则保持默认。调试与跟踪选项可选但推荐为了后续排查实时性能问题可以启用一些调试功能Kernel hacking - Memory Debugging - Check for stack overflows 栈溢出检查。Kernel hacking - Tracers - Kernel Function Tracer和Interrupts-off Latency Tracer 函数跟踪器和中断关闭延迟跟踪器用于性能分析。Real-time group scheduling 在General setup - Control Group support中启用Real-time group scheduling便于用cgroups管理实时任务组。 注意调试功能会增加内核大小和运行时开销在生产环境中可酌情关闭。配置完成后选择Save使用默认文件名.config保存然后退出menuconfig。5. 内核编译与模块构建配置保存后我们就可以开始漫长的编译过程了。编译分为两个主要部分内核镜像文件Image和设备树二进制文件DTB以及所有可加载内核模块。5.1 编译内核与DTB在内核源码根目录下执行编译命令。-j参数指定并行编译的作业数通常设置为你的主机CPU核心数1以加快编译速度。make ARCHarm64 CROSS_COMPILE$CROSS_COMPILE -j$(nproc) Image编译成功后生成的内核镜像文件位于arch/arm64/boot/Image。接下来编译设备树Device Tree Blob。对于AGX平台通常有多个DTB文件对应不同的载板配置。你需要编译整个DTB集合make ARCHarm64 CROSS_COMPILE$CROSS_COMPILE -j$(nproc) dtbs编译成功后生成的DTB文件位于arch/arm64/boot/dts/nvidia/目录下例如tegra194-p2888-0001-p2822-0000.dtbAGX Xavier或tegra234-p3701-0000-p3737-0000.dtbAGX Orin。请根据你的具体设备型号确认正确的DTB文件名。5.2 编译内核模块内核模块是独立于内核镜像的可加载驱动。编译模块make ARCHarm64 CROSS_COMPILE$CROSS_COMPILE -j$(nproc) modules编译过程会生成大量的.ko内核对象文件。5.3 安装模块到临时目录我们不会直接将模块安装到主机系统而是安装到一个临时目录以便后续打包并传输到AGX设备。# 创建一个临时目录用于存放模块 mkdir -p ~/kernel_modules_output # 安装模块指定安装路径 make ARCHarm64 CROSS_COMPILE$CROSS_COMPILE INSTALL_MOD_PATH~/kernel_modules_output modules_install执行后所有编译好的模块及其依赖关系modules.dep都会被安装到~/kernel_modules_output/lib/modules/kernel-version-rt目录下。记下这个完整的内核版本号例如5.10.104-rt64-tegra后续会用到。实操心得编译过程中的常见问题内存不足编译内核尤其是并行编译多个作业时非常消耗内存。如果主机内存小于16GB可能会在编译某些大文件如vmlinux时因OOM内存溢出而失败。解决方法减少-j参数的值如-j4或者增加主机的交换空间swap。依赖缺失如果编译报错提示缺少某个头文件或库通常是因为开发依赖包没有装全。根据错误信息使用apt-get install安装对应的-dev包。配置冲突如果编译时报错某些选项未定义或冲突可能是.config文件有问题。最干净的方法是make mrproper清理然后重新从defconfig开始配置。mrproper会删除所有编译产物和配置文件请谨慎操作。6. 部署与安装到AGX设备编译产物准备就绪后下一步就是将它们安全地部署到AGX设备上。在进行任何操作前请务必备份AGX设备上原有的内核和模块一个错误的步骤可能导致设备无法启动。6.1 准备部署文件首先在主机构建目录下整理好需要传输的文件内核镜像arch/arm64/boot/Image设备树文件arch/arm64/boot/dts/nvidia/你的设备对应的dtb文件内核模块目录~/kernel_modules_output/lib/modules/kernel-version-rt/*你可以将这些文件打包也可以通过SCP直接传输到AGX设备。假设AGX设备的IP地址是192.168.1.100用户是nvidia。6.2 备份原系统通过SSH登录到AGX设备进行关键备份# 备份当前启动分区通常是/dev/mmcblk0p1但请用df -h /boot确认 sudo cp /boot/Image /boot/Image.backup sudo cp /boot/设备树文件.dtb /boot/设备树文件.dtb.backup # 备份当前内核模块目录 sudo cp -r /lib/modules/$(uname -r) /lib/modules/$(uname -r).backup6.3 安装新内核与模块将编译好的文件从主机传输到AGX并放置到正确位置。# 从主机传输文件到AGX在主机终端执行 scp arch/arm64/boot/Image nvidia192.168.1.100:/tmp/ scp arch/arm64/boot/dts/nvidia/你的dtb文件 nvidia192.168.1.100:/tmp/ scp -r ~/kernel_modules_output/lib/modules/kernel-version-rt nvidia192.168.1.100:/tmp/在AGX设备上执行安装# 1. 安装内核镜像和设备树 sudo cp /tmp/Image /boot/Image sudo cp /tmp/你的dtb文件 /boot/设备树文件.dtb # 覆盖原有的DTB文件 # 2. 安装内核模块 sudo cp -r /tmp/kernel-version-rt /lib/modules/ # 3. 更新initramfs初始内存磁盘镜像这一步非常重要 cd /lib/modules/kernel-version-rt sudo update-initramfs -c -k kernel-version-rt # 4. 更新引导加载程序配置extlinux.conf # 首先查看/boot/extlinux/extlinux.conf的内容 sudo cat /boot/extlinux/extlinux.conf你需要编辑/boot/extlinux/extlinux.conf文件。找到当前正在使用的引导条目由LABEL标识如primary修改其中的FDT行指向你新的DTB文件路径如果文件名有变化并确保INITRD行指向你刚刚生成的initrd文件通常位于/boot/initrd.img-kernel-version-rt。内核镜像/boot/Image的路径通常不变。一个修改后的条目示例LABEL primary MENU LABEL primary kernel LINUX /boot/Image FDT /boot/tegra234-p3701-0000-p3737-0000.dtb INITRD /boot/initrd.img-5.10.104-rt64-tegra APPEND ${cbootargs} root/dev/mmcblk1p1 rw rootwait consolettyTCU0,115200n86.4 更新引导并重启保存extlinux.conf后同步磁盘并重启sync sudo reboot注意事项部署环节的生死线双重备份除了备份文件我强烈建议在开始前使用NVIDIA SDK Manager或flash.sh脚本为你的AGX制作一个完整的系统镜像备份。这是系统变砖后最后的救命稻草。串口控制台在进行内核更换操作时务必连接AGX的串口调试口UART到你的主机。如果新内核无法启动你将无法通过SSH登录串口是唯一的诊断和恢复手段。模块版本匹配确保安装的模块目录名称/lib/modules/下的目录名与即将启动的内核版本uname -r的输出完全一致。否则系统在启动时可能无法正确加载驱动。initramfs不可或缺update-initramfs命令会创建一个包含必要驱动和模块的初始内存文件系统。如果跳过这一步新内核可能在挂载根文件系统时就失败因为缺少关键存储控制器如NVMe、eMMC的驱动。7. 验证与实时性能测试设备重启后如果一切顺利你将登录到全新的实时内核系统中。首先进行基础验证。7.1 基础系统验证# 1. 检查内核版本确认包含“rt”或“PREEMPT_RT”字样 uname -a # 输出应类似于Linux agx 5.10.104-rt64-tegra #1 SMP PREEMPT_RT ... # 注意 “PREEMPT_RT” 是关键标识。 # 2. 检查当前抢占模型 cat /sys/kernel/realtime # 输出应为 1表示这是一个实时内核。 # 3. 检查内核配置 zcat /proc/config.gz | grep PREEMPT_RT # 应该能看到 CONFIG_PREEMPT_RTy表示实时抢占已启用。7.2 实时性延迟测试Cyclictest安装并运行cyclictest这是最常用的实时延迟测量工具。# 安装 sudo apt-get update sudo apt-get install rt-tests # 运行一个简单的测试运行60秒优先级为80间隔为1000微秒 sudo cyclictest -t1 -p 80 -n -i 1000 -l 60000命令参数解释-t1 启动1个测试线程。-p 80 设置线程的实时优先级为80数字越大优先级越高范围1-99。-n 使用clock_nanosleep而非简单的sleep。-i 1000 目标间隔为1000微秒1毫秒。-l 60000 循环60000次。测试结束后观察输出的统计信息重点关注Max Latency最大延迟 整个测试期间观测到的最大延迟单位微秒。在标准内核上这个值可能高达几百甚至几千微秒。在良好的实时内核配置下对于AGX这类平台最大延迟应能稳定在几十到一百多微秒以内。Min Latency最小延迟 通常很小。Act Latency平均延迟 平均延迟。Histogram延迟分布直方图 更详细地展示了延迟的分布情况理想状态下绝大多数延迟都应集中在很低的区间。7.3 压力测试与延迟分析为了暴露最坏情况下的延迟需要在系统有负载时运行cyclictest。同时使用stress工具给系统施加压力。# 在一个终端运行压力测试 stress --cpu 4 --io 2 --vm 1 --vm-bytes 256M --timeout 60s # 在另一个终端或通过SSH新会话运行cyclictest sudo cyclictest -t1 -p 99 -n -i 1000 -l 60000 -m -h 1000 latency_report.txt参数-m锁定内存防止页面交换影响延迟。-h 1000生成详细的直方图上限为1000微秒。将结果重定向到文件便于分析。对比无负载和有负载情况下的Max Latency。如果实时补丁和配置生效良好即使在压力下最大延迟的增长也应该是可控的例如从50us增加到150us。如果压力下延迟激增到毫秒级说明系统存在“噪声源”需要进一步调优。8. 系统调优与“噪声”抑制即使成功安装了实时内核默认的系统配置可能仍包含许多会引入延迟的“噪声源”。要让实时性能达到最佳状态需要进行一系列系统调优。8.1 隔离CPU核心将特定的CPU核心专门分配给实时任务避免它们被操作系统守护进程、中断或其他非实时任务打扰。对于AGX Xavier8核Carmel或Orin12核Cortex-A78AE通常可以隔离出1-2个核心。# 编辑GRUB配置对于Jetson通常是extlinux.conf但内核参数通用 # 在APPEND行末尾添加以下隔离参数例如隔离CPU 7核心编号从0开始 # 修改 /boot/extlinux/extlinux.conf 中的 APPEND 行 APPEND ... isolcpus7 nohz_full7 rcu_nocbs7isolcpus7 将CPU 7从通用调度器中隔离出来。nohz_full7 在CPU 7上启用完全无滴答tickless模式减少定时器中断。rcu_nocbs7 将CPU 7从RCU回调处理中移除进一步减少干扰。 修改后需要重启生效。然后你可以使用taskset命令将实时进程绑定到被隔离的核心上运行。8.2 调整内核启动参数在extlinux.conf的APPEND行还可以添加其他有助于实时性的参数rcupdate.rcu_cpu_stall_suppress1 抑制RCU停滞警告避免其在实时关键路径中打印日志造成延迟。skew_tick1 错开各CPU的定时器滴答避免同时触发中断。nmi_watchdog0 禁用NMI看门狗非屏蔽中断看门狗它可能产生不可预测的中断。nowatchdog 禁用软锁检测看门狗。8.3 调整进程调度策略与优先级对于你的关键实时进程需要在程序内部或启动时设置正确的调度策略和优先级。使用Linux的sched_setscheduler系统调用。调度策略SCHED_FIFO 先进先出实时调度。更高优先级的进程会一直运行直到阻塞或主动让出CPU同优先级按FIFO顺序。SCHED_RR 轮转实时调度。与FIFO类似但同优先级进程会分配时间片轮流执行。SCHED_OTHER 标准的分时调度策略CFS。 对于硬实时任务通常选择SCHED_FIFO。优先级 实时优先级范围是1最低到99最高。数字越大优先级越高。示例C语言#include sched.h struct sched_param param; param.sched_priority 80; // 设置优先级为80 if (sched_setscheduler(0, SCHED_FIFO, param) -1) { perror(sched_setscheduler failed); }在命令行可以使用chrt工具来启动一个程序并设置其调度策略sudo chrt --fifo 80 ./my_real_time_app8.4 禁用可能引起中断的电源管理特性一些动态调频DVFS和节能功能如CPU idle states会在运行时改变CPU频率和状态引入不可预测的延迟。固定CPU频率 将CPU频率设置为一个固定的高性能值而不是ondemand或schedutil。# 查看可用频率 cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies # 设置调速器为performance sudo sh -c echo performance /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor # 对于所有CPU核心 for i in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do echo performance | sudo tee $i; done禁用CPU深度休眠状态C-states 在BIOS/UEFI中禁用或通过内核启动参数processor.max_cstate1或idlepoll来限制。在Jetson上可能需要修改设备树或使用jetson_clocks脚本该脚本会同时锁定CPU和GPU频率但注意其功耗和发热。8.5 使用tuna工具进行动态调优tuna是一个命令行工具可以方便地查看和调整线程的调度策略、优先级以及CPU亲和性绑定。# 安装 sudo apt-get install tuna # 查看所有线程 sudo tuna --show_threads # 将PID为1234的线程设置为SCHED_FIFO优先级90并绑定到CPU 7 sudo tuna --threads1234 --priorityFIFO:90 --cpus7 --move经过以上步骤的编译、部署、验证和调优你的NVIDIA AGX平台就已经从一个强大的通用计算平台转变为一个具备确定性的实时边缘计算节点。这个过程虽然繁琐但对于需要硬实时保障的应用而言是解锁平台全部潜力的必经之路。记住实时性能的调优是一个迭代和测试的过程需要根据你的具体工作负载反复测试和调整各项参数才能达到最优效果。
返回列表