资讯动态

Jetson Orin Nano 内核编译与驱动打包实战指南

发布时间:2026/9/28 3:41:16 来源:尧图企业网站定制
1. 为什么值得在 Jetson Orin Nano 上折腾内核编译Jetson Orin Nano 这台小板子买回来刷个官方镜像就能跑推理、跑视觉、跑各种边缘计算任务很多人用得很开心。但只要你往深里用一步迟早会撞上那堵墙官方预编译内核里没有你要的驱动或者某个功能被裁掉了或者你需要打开一个默认关闭的内核选项。这时候摆在面前的路只有一条——自己编译内核。我最初动这个念头是因为要给板子接一个非官方的摄像头模组和一个自定义的串口扩展板官方内核里对应的驱动模块压根没编进去。网上搜了一圈中文资料要么是抄来抄去的片段要么是拿老版本 JetPack 的流程套在新板子上踩坑踩到怀疑人生。后来硬着头皮把整个流程跑通了好几遍从依赖安装到驱动打包把能踩的坑基本踩了个遍才有了这篇东西。这篇文章面向的是已经会用 Jetson Orin Nano 基本操作、想进一步定制内核的开发者。你不需要是内核专家但至少得会用apt、看得懂make menuconfig的界面、知道什么是设备树。我会把整个流程拆成依赖准备、源码获取、配置裁剪、编译、驱动打包、刷写验证几个阶段每个阶段讲清楚为什么这么做而不只是怎么做。全文基于 JetPack 6.x 系列L4T 36.x的常见实践不同小版本之间会有细微差异我会在关键位置标注出来。先说一个总的原则Jetson 的内核编译和普通 x86 Linux 内核编译最大的区别在于它和 NVIDIA 的 BSPBoard Support Package深度绑定。你不能随便从 kernel.org 拉一个 mainline 内核就往上怼那样编出来的东西大概率起不来。正确的做法是基于 NVIDIA 提供的 L4T 源码包来改这一点后面会反复强调。2. 编译前的整体思路与方案选型2.1 三种编译路线的取舍在 Jetson 上搞内核其实有三条路可以走我先把它们摆出来对比一下你再决定走哪条。路线做法优点缺点适用场景板端直接编译在 Orin Nano 上装工具链直接编不用交叉编译环境板子性能有限编译慢容易内存不足只改一两个模块交叉编译推荐在 x86 主机上编产物拷到板子速度快环境好管理需要配交叉工具链绝大多数场景用 NVIDIA 官方脚本走nvbuild.sh或 JetPack 自带流程省心兼容性好灵活性差定制受限只想加个模块我个人的建议是交叉编译。Orin Nano 虽然性能不弱但编译一个完整内核加上模块板端跑起来又慢又容易因为内存不够而中断尤其是你开了-j多线程之后。用一台 x86 的 Ubuntu 主机20.04 或 22.04 都行来交叉编译效率高出一个数量级。2.2 为什么必须基于 L4T 源码而不是 mainline这是新手最容易犯的错。有人觉得内核嘛去 kernel.org 下个最新的不就行了不行。原因有三第一Orin 系列的 SoC 支持Tegra234在 mainline 里并不完整很多外设驱动、电源管理、时钟控制都依赖 NVIDIA 的补丁。第二设备树Device Tree是 NVIDIA 定制的mainline 的设备树和你的板子对不上编出来直接黑屏。第三内核模块的版本必须和用户空间的库匹配L4T 的 CUDA、TensorRT 这些组件对内核版本有要求你换个内核版本驱动可能就加载不了。所以正确的姿势是从 NVIDIA 的公开源码仓库或者 JetPack 的源码包sources目录里拿 L4T 对应的内核源码在这个基础上改。2.3 版本对应关系必须先搞清楚动手之前先在板子上确认你的 L4T 版本cat /etc/nv_tegra_release # 输出类似# R36 (release), REVISION: 3.0, GCID: xxxxx这个R36.3.0就是你后续所有操作的基准。源码包、工具链、刷写工具都要和它对齐。我见过太多人拿着 R35 的源码去编 R36 的板子编出来的模块insmod时报version magic不匹配白忙活一天。提示L4T 版本和 JetPack 版本是绑定的比如 JetPack 6.0 对应 L4T 36.3JetPack 6.1 对应 L4T 36.4。查清楚再动手。3. 依赖安装那些让你卡在第一步的坑3.1 主机端依赖清单交叉编译主机上需要装的东西官方文档列了一堆但实际用起来有几个是必须的有几个是可选的。我整理了一份实测清单sudo apt update sudo apt install -y build-essential bc flex bison libssl-dev \ libncurses5-dev libncursesw5-dev libelf-dev \ device-tree-compiler python3 python3-pip \ git wget cpio rsync这里面几个关键项解释一下libssl-dev内核签名和模块签名相关缺了会在编译后期报错而且报错信息很隐晦不容易定位。device-tree-compiler编译设备树必须Orin 的设备树改动很频繁这个一定要装。libncurses5-devmake menuconfig的图形界面依赖不装的话你只能手改.config效率极低。bc内核编译脚本里用到了缺了会报bc: command not found。3.2 交叉工具链的坑这是重灾区。NVIDIA 推荐用他们提供的aarch64--glibc--stable工具链但很多人图省事直接用apt install gcc-aarch64-linux-gnu结果编出来的东西各种诡异问题。我的建议是用 NVIDIA 官方工具链。从 NVIDIA 开发者网站下载对应版本的aarch64--glibc--stable-xxxx包解压后把bin目录加到PATHexport CROSS_COMPILE/opt/toolchains/aarch64--glibc--stable/bin/aarch64-buildroot-linux-gnu- export PATH/opt/toolchains/aarch64--glibc--stable/bin:$PATH注意CROSS_COMPILE结尾那个横杠不能少这是 Makefile 的约定。注意如果你用的是 Ubuntu 自带的gcc-aarch64-linux-gnu编译时可能会遇到unrecognized command line option或者链接阶段找不到某些库的问题。这不是你代码的问题是工具链版本和内核源码不匹配。换官方工具链能省掉大量排查时间。3.3 依赖缺失的典型报错与排查我列几个实际遇到过的报错以及对应的解决办法报错信息原因解决fatal error: openssl/ssl.h: No such file缺 libssl-devapt install libssl-devbc: not found缺 bcapt install bcflex: command not found缺 flexapt install flex bisonscripts/dtc: dtc: not found缺 device-tree-compilerapt install device-tree-compilererror: unrecognized command line option -mgeneral-regs-only工具链版本太老换 NVIDIA 官方工具链No rule to make target debian/canonical-certs.pem内核签名配置问题关掉CONFIG_SYSTEM_TRUSTED_KEYS最后那个签名的问题特别常见尤其是你从 Ubuntu 主机上直接编的时候。解决办法是在menuconfig里把CONFIG_SYSTEM_TRUSTED_KEYS和CONFIG_SYSTEM_REVOCATION_KEYS都清空。4. 源码获取与配置裁剪的实操细节4.1 源码从哪来两条路一是从 NVIDIA 的公开 Git 仓库拉二是从 JetPack 的sources目录里找。我推荐后者因为 JetPack 的源码包和你板子上的 L4T 版本是严格对应的省去了对版本的麻烦。如果你装 JetPack 的时候勾选了下载源码在~/nvidia/nvidia_sdk/JetPack_xxx/Linux_for_Tegra/sources下就能找到。如果没有可以用 SDK Manager 单独下载或者从 NVIDIA 的公开仓库拉对应 tag。源码目录结构大概是这样sources/ ├── kernel/ │ └── kernel-5.10/ # 内核源码 ├── hardware/ │ └── nvidia/ # 设备树、驱动相关 ├── out/ # 编译输出目录 └── nvbuild.sh # NVIDIA 的编译脚本4.2 用 nvbuild.sh 还是手动 makeNVIDIA 提供了nvbuild.sh脚本封装了编译流程。对于只想加个模块的人来说用这个脚本最省事cd sources ./nvbuild.sh -o ./out但如果你想精细控制配置比如裁剪掉不需要的驱动来减小体积或者打开某个默认关闭的选项就得手动操作。我的做法是先用 nvbuild.sh 跑一遍拿到基础配置再手动改。4.3 配置裁剪的核心操作进入配置界面cd sources/kernel/kernel-5.10 make ARCHarm64 O../../out defconfig make ARCHarm64 O../../out menuconfigdefconfig是 NVIDIA 提供的默认配置基于它改最稳妥。menuconfig里几个关键区域Device Drivers→ 你要加的驱动在这里找比如 USB 串口、摄像头 sensor、网络驱动。Device Drivers→Network device support→ 网卡驱动Orin Nano 板载网卡和常见 USB 网卡都在这里。Kernel Features→ 内存和调度相关一般不动。Enable loadable module support→ 确保模块加载支持是开的否则你编出来的.ko没法insmod。改完之后保存配置会写到out/.config。实操心得改配置之前先备份一份.config。我有一次改着改着把某个关键选项关了编出来的内核起不来又得从头配。备份一份出问题直接覆盖回去省事。4.4 设备树改动的注意事项如果你加的驱动涉及硬件引脚、时钟、电源域光改内核配置不够还得改设备树。Orin 的设备树文件在hardware/nvidia/platform/t23x/下面具体路径根据你的板子型号Orin Nano 是p3768或p3767系列不同。设备树改动是最容易出问题的地方。改错一个引脚定义轻则驱动加载失败重则板子起不来。我的建议是改设备树之前先把原始的设备树反编译出来看一遍理解每个节点的含义再动手改。# 反编译板子上正在用的设备树 dtc -I fs -O dts /proc/device-tree -o current.dts这样你能看到板子实际用的设备树长什么样对照着改心里有底。5. 编译过程与驱动打包的完整流程5.1 编译命令与参数配置好之后开始编译。交叉编译的完整命令cd sources/kernel/kernel-5.10 make ARCHarm64 O../../out \ CROSS_COMPILE$CROSS_COMPILE \ -j$(nproc) Image modules dtbs三个目标分别对应Image是内核镜像modules是内核模块dtbs是设备树二进制。-j$(nproc)用满 CPU 核心数加速但如果你主机内存不够比如只有 8G建议降到-j4否则会 OOM。编译时间取决于主机性能一般 15 到 40 分钟。编完之后产物在out/arch/arm64/boot/下面。5.2 模块安装到临时目录编译完的模块不能直接拷到板子上得先install到一个临时目录这样会生成正确的目录结构和modules.dep等依赖文件make ARCHarm64 O../../out \ CROSS_COMPILE$CROSS_COMPILE \ INSTALL_MOD_PATH../../out/modules_install \ modules_install这一步会把你编的所有模块按lib/modules/内核版本/的结构放好。内核版本这个字符串很重要它必须和板子上uname -r的输出一致否则模块加载不了。5.3 驱动打包的两种方式打包驱动有两种常见做法看你的使用场景选方式一直接替换板子上的模块目录把modules_install/lib/modules/版本/整个目录打包拷到板子上覆盖/lib/modules/版本/。适合你编了全套模块的情况。cd out/modules_install/lib/modules tar czf modules.tar.gz 版本/ scp modules.tar.gz user板子IP:~/板子上解压覆盖然后sudo depmod -a重建依赖。方式二只打包你新增的模块如果你只加了一两个驱动没必要替换整个目录。找到对应的.ko文件单独拷过去# 比如你编了个 usbserial 驱动 find out -name usbserial.ko # 拷到板子 scp out/.../usbserial.ko user板子IP:~/ # 板子上加载 sudo insmod usbserial.ko注意单独拷.ko的时候一定要确认内核版本字符串匹配。用modinfo usbserial.ko | grep vermagic看一下和板子uname -r对不上就别费劲了加载必失败。5.4 内核镜像的刷写如果你改的是内核本身不只是模块那就得刷内核镜像。Orin Nano 刷内核有两种方式方式一用 flash.sh 刷整个系统重但稳cd Linux_for_Tegra sudo ./flash.sh jetson-orin-nano-devkit mmcblk0p1这会把你编的Image和设备树一起刷进去。前提是你得先把编好的产物放到Linux_for_Tegra/kernel/对应位置。方式二只更新内核分区轻但需要板子能进恢复模式sudo ./flash.sh -k A_kernel jetson-orin-nano-devkit mmcblk0p1只刷内核分区速度快很多。但注意 Orin 有 A/B 分区机制你得确认刷的是当前启动的那个分区。5.5 验证编译结果刷完重启板子上执行uname -r # 确认内核版本 lsmod | grep 你的模块 # 确认模块加载 dmesg | tail -50 # 看启动日志有没有报错如果模块没自动加载检查/etc/modules-load.d/或者用modprobe手动加载看看报什么错。6. 常见问题与排查技巧实录6.1 编译阶段的问题问题编译到一半报Killed这是内存不足被 OOM killer 杀了。解决办法是降低并行度-j2或-j1或者加 swap。我一般会在主机上开 8G swap 兜底。问题make menuconfig报Unable to find the ncurses libraries缺libncurses5-dev装上就行。如果装完还报试试libncursesw5-dev。问题设备树编译报dtc: command not found缺device-tree-compilerapt install一下。6.2 刷写与启动阶段的问题问题刷完黑屏串口也没输出大概率是设备树改错了或者内核配置裁掉了必要的启动选项。解决办法是回退到原始内核用串口调试看卡在哪一步。Orin Nano 的串口在板子上有标注接个 USB-TTL 就能看。问题能启动但模块加载失败报version magic不匹配内核版本字符串对不上。检查你编译时用的源码版本和板子当前内核版本是否一致。用modinfo看模块的 vermagic和uname -r对比。问题insmod报Unknown symbol模块依赖的其他模块没加载。用modprobe代替insmod它会自动处理依赖。或者手动insmod依赖的模块。6.3 独家避坑清单我把整个流程里最容易翻车的地方整理成一张速查表阶段坑点规避方法依赖安装工具链版本不对用 NVIDIA 官方工具链源码获取版本和板子不匹配从 JetPack sources 拿配置裁剪误关关键选项改前备份 .config设备树引脚定义改错先反编译现有设备树对照编译内存不足被 kill降并行度或加 swap打包内核版本字符串不一致用 modinfo 核对 vermagic刷写刷错分区确认 A/B 分区当前启动项验证模块没自动加载检查 modules-load.d 配置6.4 几个我踩过的真实坑第一个坑是工具链的CROSS_COMPILE变量结尾漏了横杠。这个错误不会立刻报而是在链接阶段报一堆找不到符号排查了半天才发现是变量名的问题。第二个坑是设备树改了但没重新编译 dtbs。我只编了Image和modules忘了dtbs结果刷进去设备树还是旧的驱动死活加载不了。后来养成习惯每次编译都把三个目标一起带上。第三个坑是模块目录权限问题。拷到板子上覆盖/lib/modules/的时候如果不用sudo文件权限会不对depmod跑不起来。这个坑很隐蔽因为文件确实拷过去了但就是加载不了。7. 一些让流程更顺手的经验整个流程跑通之后我把它脚本化了。一个脚本负责在主机上编译和打包一个脚本负责在板子上部署和验证。这样下次再改内核两条命令搞定不用每次翻笔记。主机端的编译脚本核心就是那几条make命令加上打包板子端的部署脚本就是解压、depmod、重启。脚本本身不复杂但省去了大量重复劳动。另外一个小技巧在板子上保留一份原始内核的备份。刷内核之前把/boot和/lib/modules/下的原始文件备份到别的地方万一新内核起不来还能通过恢复模式刷回去。我有一次改设备树把板子搞黑屏了就是靠备份救回来的。还有一点如果你只是加个简单的 USB 设备驱动其实不一定要重新编译整个内核。很多常见驱动在官方内核里是以模块形式存在的只是默认没加载。先modprobe试试或者find /lib/modules/$(uname -r) -name *.ko | grep 关键词找找看说不定你要的驱动已经在板子上了只是没启用。这个思路能帮你省掉一大半编译工作。最后说个关于版本管理的体会。Jetson 的 L4T 版本更新挺频繁的每次更新内核源码都会有变化。我的做法是给每个 L4T 版本单独建一个工作目录把源码、配置、编译产物都放在一起目录名带上版本号。这样切换版本的时候不会搞混也不会出现拿旧配置编新内核的情况。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑