Jetson Orin Nano 内核编译实战:从依赖安装到驱动打包全流程
发布时间:2026/9/28 14:20:53 锦皓数字建站

1. 为什么要在 Jetson Orin Nano 上自己编译内核拿到 Jetson Orin Nano 的第一天大多数人都是插上电源、烧录官方镜像、跑通nvidia-smi或者jtop然后心满意足地开始跑推理。但只要你往深里走一步——想接一个官方镜像里没带的摄像头传感器、想启用某个默认关闭的硬件模块、想给实时性打补丁、想裁剪掉一堆用不上的驱动来省内存——你就会撞上同一堵墙官方预编译内核里没有你要的东西你必须自己编译内核。Jetson Orin Nano 的内核编译和普通 x86 服务器上编译 Linux 内核完全不是一回事。它用的是 NVIDIA 深度定制过的内核源码树交叉编译工具链、设备树、驱动模块、固件打包全都绑在一起任何一个环节没对齐最后得到的就是一块开机黑屏的砖。我自己第一次编译的时候前后折腾了整整两天踩的坑从依赖缺失到驱动打包路径错误几乎把能犯的错都犯了一遍。所以这篇东西不是一份照本宣科的官方文档翻译而是我把整个流程重新走了一遍之后把每一步的意图、参数选择的理由、以及那些文档里不会写的坑全部摊开来讲清楚。这篇文章适合谁看如果你手上有一块 Jetson Orin Nano已经能正常开机进系统现在需要修改内核配置、添加自定义驱动、或者重新打包内核与模块那这篇就是写给你的。如果你连系统都还没烧录建议先把官方镜像跑通再回来。全文会围绕四个核心环节展开依赖安装、源码与工具链准备、内核配置与编译、驱动打包与部署每个环节我都会告诉你为什么这么做以及不做会出什么问题。2. 编译前的整体思路与方案选型2.1 为什么不用官方预编译内核直接改很多人第一反应是官方不是给了内核镜像吗我能不能直接替换里面的某个.ko文件答案是理论上可以但实际几乎不可行。原因在于 Jetson 的内核模块和内核本体是强版本绑定的vermagic里包含了内核版本、编译器版本、配置选项的哈希。你手动编译一个模块塞进去只要编译器版本或者配置差一点点insmod就会直接报invalid module format。所以正确做法是拿到和当前系统完全对应的内核源码用匹配的工具链完整编译一遍内核和模块然后整体替换。2.2 本地编译还是交叉编译这是第一个要做的决策。Jetson Orin Nano 本身是 ARM64 架构你可以在它上面直接本地编译native build也可以在 x86 主机上交叉编译cross build。方案优点缺点适用场景本地编译环境简单不需要额外工具链路径不会错Orin Nano 性能有限全量编译内核可能要 1-2 小时内存吃紧时容易 OOM只改少量配置、临时验证交叉编译x86 主机编译速度快可并行度高需要正确安装交叉工具链路径和 sysroot 容易配错频繁编译、正式出包我个人的建议是如果你只是偶尔改一次本地编译足够如果你要反复迭代驱动强烈建议在 x86 主机上交叉编译。下面两条路线我都会讲但重点放在交叉编译上因为坑最多、也最值得讲。2.3 版本对齐是整件事的生命线在动手之前你必须先确认三件事的版本完全一致L4T 版本、内核源码版本、交叉编译工具链版本。查当前系统版本最直接的方法cat /etc/nv_tegra_release # 输出类似# R35 (release), REVISION: 4.1, GCID: 12345678, BOARD: t186ref, EABI: aarch64记下这个R35.4.1它决定了你要下载哪个版本的源码包和工具链。版本错一位后面全白干。我见过有人用 R35.3.1 的源码去编译 R35.4.1 的系统编译能过但刷进去直接黑屏因为设备树和内核 ABI 对不上。3. 依赖安装那些让你卡在第一步的坑3.1 主机端依赖清单与安装命令交叉编译的主机推荐 Ubuntu 20.04 或 22.04这是 NVIDIA 官方验证过的环境。依赖分两类编译工具类和库类。很多人只装了build-essential就开始编译结果在make dtbs阶段报一堆找不到libssl、flex、bison的错。完整的依赖安装命令如下sudo apt update sudo apt install -y build-essential bc flex bison libssl-dev \ libncurses5-dev libncursesw5-dev libelf-dev git wget \ device-tree-compiler python3 python3-pip cpio rsync这里逐个说清楚每个包的作用你就知道为什么不能省bc内核编译脚本里做算术运算用的缺了会在编译早期就报错。flex和bison词法和语法分析器内核的 Kconfig 和 dtc 解析都依赖它们。libssl-dev内核模块签名和部分加密代码需要。libncurses5-devmake menuconfig的图形界面依赖没有它你只能手改.config。device-tree-compiler编译设备树dtb必需Jetson 的设备树是核心。cpio打包 initramfs 用。注意Ubuntu 22.04 上libncurses5-dev可能已经被libncurses-dev取代如果安装报找不到包换成libncurses-dev即可。3.2 交叉编译工具链的获取与验证NVIDIA 官方推荐使用 Bootlin 提供的工具链版本要和 L4T 对应。以 R35.4.1 为例工具链是aarch64--glibc--stable-2022.08-1。下载后解压到一个固定路径比如/opt/toolchainwget https://developer.nvidia.com/downloads/embedded/l4t/r35_release_v4.1/toolchain/aarch64--glibc--stable-2022.08-1.tar.bz2 sudo mkdir -p /opt/toolchain sudo tar -xjf aarch64--glibc--stable-2022.08-1.tar.bz2 -C /opt/toolchain解压完一定要验证工具链能不能用export CROSS_COMPILE/opt/toolchain/aarch64--glibc--stable-2022.08-1/bin/aarch64-buildroot-linux-gnu- ${CROSS_COMPILE}gcc --version如果这条命令报No such file or directory八成是路径写错了或者你下载的工具链架构不对。这一步验证不过后面所有编译都是浪费时间。3.3 一个容易被忽略的依赖Python 环境内核编译过程中有些脚本用 Python 写比如scripts/dtc里的辅助工具。如果你的系统默认 Python 是 3.10 以上某些老脚本可能会因为语法不兼容报错。稳妥做法是确保python3可用并且python软链接指向python3sudo apt install -y python3 python3-pip sudo ln -sf /usr/bin/python3 /usr/bin/python这个坑我在 R35.3.1 上遇到过编译到一半报SyntaxError: invalid syntax查了半天才发现是某个脚本用了 Python 2 的 print 语法而系统里python指向了 Python 3。虽然新版源码已经修了但养成检查 Python 环境的习惯没坏处。4. 源码准备与目录结构梳理4.1 下载正确的源码包Jetson 的内核源码不在主线的 kernel.org 上而是 NVIDIA 在官方 L4T 页面提供的Linux_for_Tegra包里的source目录。你需要下载两个东西L4T BSP 包和对应的源码包。以 R35.4.1 为例wget https://developer.nvidia.com/downloads/embedded/l4t/r35_release_v4.1/sources/public_sources.tbz2 tar -xjf public_sources.tbz2 cd Linux_for_Tegra/source/public tar -xjf kernel_src.tbz2解压后会得到一个kernel目录这就是完整的内核源码树。不要从 GitHub 上随便 clone 一个 Linux 内核源码来用因为 Jetson 的驱动、设备树、配置全是 NVIDIA 定制的主线内核根本跑不起来。4.2 源码目录结构速览进入kernel目录后你会看到熟悉的内核源码结构但有几个 Jetson 特有的目录需要特别关注kernel/kernel-5.10/内核主体源码版本号可能随 L4T 变化。kernel/nvidia/NVIDIA 自己的驱动比如nvgpu、nvhost、摄像头驱动等。kernel/nvidia/drivers/你要添加自定义驱动时通常放在这里。kernel/hardware/nvidia/设备树源码tegra234就是 Orin 系列的平台代号。提示Orin Nano 属于tegra234平台设备树文件里看到tegra234开头的就是它。4.3 设置环境变量避免每次手敲编译前把关键路径写成环境变量后面所有命令都引用变量既不容易错也方便复现export L4T_ROOT$HOME/Linux_for_Tegra export KERNEL_SRC$L4T_ROOT/source/public/kernel/kernel-5.10 export CROSS_COMPILE/opt/toolchain/aarch64--glibc--stable-2022.08-1/bin/aarch64-buildroot-linux-gnu- export ARCHarm64 export TEGRA_KERNEL_OUT$KERNEL_SRC/../outTEGRA_KERNEL_OUT是编译输出目录强烈建议单独指定不要用源码树里的默认输出这样源码树保持干净出问题重新编译时直接删输出目录就行不用重新解压源码。5. 内核配置defconfig 的选择与裁剪5.1 找到正确的 defconfigJetson 的默认配置文件名是tegra_defconfig位于arch/arm64/configs/下。但注意Orin 系列有时候会用defconfig加上额外的 fragment。最稳妥的方式是参考官方编译脚本nvbuild.sh里的配置逻辑。你可以直接看这个脚本cat $KERNEL_SRC/nvbuild.sh里面会明确写出用哪个 defconfig、输出目录在哪、编译哪些目标。这个脚本是官方给的编译入口读懂它比看任何第三方教程都靠谱。5.2 生成基础配置用 defconfig 生成.configcd $KERNEL_SRC make ARCHarm64 O$TEGRA_KERNEL_OUT tegra_defconfig这条命令执行完$TEGRA_KERNEL_OUT/.config就是基础配置。接下来如果你要加自定义驱动有两种方式改 defconfig 源文件或者用 menuconfig 交互式添加。前者适合正式出包后者适合快速验证。5.3 menuconfig 的正确用法与常见误操作make ARCHarm64 O$TEGRA_KERNEL_OUT menuconfig进去之后你要添加的驱动如果是tristate类型建议先选M编译成模块这样调试阶段可以随时insmod/rmmod不用反复刷内核。等验证稳定了再改成Y编进内核。注意menuconfig 里改完一定要保存退出否则前面的操作全丢。另外如果你在 menuconfig 里改了配置但编译时又用了tegra_defconfig重新生成.config你的修改会被覆盖。正确顺序是先 defconfig再 menuconfig然后直接 make不要再跑 defconfig。5.4 配置验证确认你的驱动真的被选中了改完配置后别急着编译先验证grep YOUR_DRIVER_CONFIG $TEGRA_KERNEL_OUT/.config如果输出CONFIG_YOUR_DRIVERm或y说明配置生效。如果输出# CONFIG_YOUR_DRIVER is not set说明你的驱动依赖没满足menuconfig 里它可能是灰的。这时候要回去检查Kconfig里的depends on条件。6. 编译内核与模块参数选择与并行度调优6.1 编译命令与并行度cd $KERNEL_SRC make ARCHarm64 O$TEGRA_KERNEL_OUT -j$(nproc) Image modules dtbs这里三个目标分别对应内核镜像 Image、可加载模块 modules、设备树 dtbs。三个都要编缺一个后面部署都会出问题。-j$(nproc)是用满所有 CPU 核心。但如果你主机内存不够比如 8GB并行度太高会 OOM。经验值是每 GB 内存对应 1 个编译任务。16GB 内存可以用-j168GB 就老老实实-j4。6.2 编译时间预期与中断处理在 Ryzen 7 5800X、32GB 内存的主机上全量编译大约 15-25 分钟。如果中途因为报错中断修完问题后不需要重新全量编译直接再跑一次同样的命令make 会自动跳过已完成的部分。这也是为什么输出目录要单独指定——增量编译全靠它。6.3 编译产物清单编译成功后在$TEGRA_KERNEL_OUT下会生成arch/arm64/boot/Image内核镜像。arch/arm64/boot/dts/nvidia/*.dtb设备树二进制文件。各个驱动目录下的.ko文件内核模块。先别急着往板子上刷下一步的驱动打包才是真正容易翻车的地方。7. 驱动打包从 .ko 到可部署模块7.1 模块安装到临时目录make ARCHarm64 O$TEGRA_KERNEL_OUT modules_install INSTALL_MOD_PATH$TEGRA_KERNEL_OUT/modules这条命令会把所有.ko按内核目录结构安装到modules/lib/modules/kernel_version/下。kernel_version是你的内核版本号比如5.10.104-tegra。这个版本号必须和板子上uname -r的输出一致否则模块加载会失败。7.2 生成 modules.dep 和别名数据库cd $TEGRA_KERNEL_OUT/modules/lib/modules/kernel_version sudo depmod -a -b $TEGRA_KERNEL_OUT/modules kernel_versiondepmod会生成modules.dep、modules.alias等文件这些是modprobe自动加载模块的依据。漏了这一步你的模块即使放对位置也不会自动加载。7.3 打包成可部署的压缩包cd $TEGRA_KERNEL_OUT/modules tar -cjf ../kernel_modules.tar.bz2 lib/把这个压缩包传到板子上解压到根目录sudo tar -xjf kernel_modules.tar.bz2 -C / sudo depmod -a7.4 内核镜像与设备树的部署内核镜像Image和设备树dtb的部署方式和你的启动方式有关。如果是 eMMC 启动通常需要把Image复制到/boot/把 dtb 复制到/boot/dtb/然后更新extlinux.conf。这一步风险最高建议先备份原文件sudo cp /boot/Image /boot/Image.bak sudo cp /boot/dtb/*.dtb /boot/dtb/backup/注意Orin Nano 的启动链涉及多个阶段的固件单纯替换/boot/Image不一定生效具体要看你的 L4T 版本和启动配置。如果替换后黑屏用备份文件恢复即可。8. 常见问题与排查技巧实录8.1 编译阶段常见报错速查报错信息原因解决方法fatal error: openssl/ssl.h: No such file缺 libssl-devsudo apt install libssl-devflex: command not found缺 flexsudo apt install flex bisoninvalid module format模块与内核版本不匹配确认 vermagic 一致重新编译No rule to make target dtbs源码树不完整重新解压 kernel_src.tbz2编译到一半 OOM并行度太高降低-j数值8.2 部署后黑屏的排查思路黑屏是 Jetson 编译内核最吓人的问题但排查有章可循。首先确认是不是内核根本没起来接串口调试线看 UART 输出。如果 UART 有输出但停在某个阶段通常是设备树不匹配如果 UART 完全没输出可能是 Image 本身有问题。串口线是调试 Jetson 的必备工具强烈建议常备一根。8.3 模块加载失败的定位方法dmesg | tail -50 modinfo your_module.komodinfo会显示模块的vermagic和uname -r对比不一致就是版本问题。dmesg里如果有Unknown symbol说明模块依赖的其他符号没导出需要检查内核配置里相关选项是否开启。8.4 几个我踩过的独家坑第一个坑输出目录权限问题。如果你用sudo跑过一次编译输出目录里的文件属主变成 root后面用普通用户再编译就会报权限错误。解决办法是编译全程不用 sudo需要权限的地方单独处理。第二个坑设备树文件名混淆。Orin Nano 和 Orin NX 的设备树文件名很像刷错 dtb 直接黑屏。刷之前用fdtdump确认一下 dtb 里的model字段。第三个坑modules_install 路径写错。INSTALL_MOD_PATH如果指向了系统根目录而不是临时目录会直接覆盖你主机上的模块把主机搞崩。这个参数一定要指向输出目录下的临时路径。9. 一些让流程更顺手的经验整个流程走通之后我把它整理成了一个脚本每次编译只需要改几个变量。核心思路是环境变量集中定义、输出目录独立、编译和打包分步执行、每步都有验证。这样即使中间出错也能快速定位到是哪一步的问题不用从头再来。另外如果你要频繁添加驱动建议把自定义驱动的Kconfig和Makefile直接集成到kernel/nvidia/drivers/下这样每次编译都会自动带上不用手动insmod。集成方法是在对应目录的Kconfig里source你的驱动配置在Makefile里加一行obj-$(CONFIG_YOUR_DRIVER) your_driver/。最后分享一个判断编译是否真正成功的土办法编译完先别刷板子在主机上用file命令检查Imagefile $TEGRA_KERNEL_OUT/arch/arm64/boot/Image输出里应该包含ARM aarch64字样。如果显示的是 x86 或者别的架构说明ARCH或CROSS_COMPILE没生效刷上去必砖。这个检查只要两秒钟但能帮你省下几个小时的救砖时间。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。