Ubuntu下编译安装PREEMPT_RT实时内核及NVIDIA驱动修复指南
发布时间:2026/10/5 8:21:25 作者:尧图编辑部 阅读量:1,286

如果你正准备在Ubuntu上跑EtherCAT主站、机器人实时控制或者高精度数据采集那么“给内核打上PREEMPT_RT实时补丁”几乎是绕不开的一步。但我要先给你打个预防针补丁本身不难打真正让人血压飙升的是打完之后显卡驱动那一堆烂摊子。上个月我在一块工控板上装RT内核内核编译一次通过结果进系统后NVIDIA驱动直接罢工桌面起不来nvidia-smi报错折腾到凌晨两点才把问题理清楚。这篇文章就是把整条路线完整捋一遍从要不要打补丁、怎么选内核和补丁版本、如何编译安装RT内核到补丁装完后显卡驱动挂掉的排查与修复全程在Ubuntu 22.04上验证过内核采用6.6.119配合对应实时补丁这个组合对EtherCAT常用的igc网卡驱动支持也很到位。适合搞工业控制、机器人实时通信、机器视觉集成的朋友参考也建议那些想在带NVIDIA显卡的机器上跑实时Linux的人先看完再动手。1. 为什么说Ubuntu默认内核撑不起“实时”这件事1.1 PREEMPT_RT改了什么中断线程化、可抢占锁与优先级继承Ubuntu发行版自带的generic内核日常跑Web服务、写代码、看视频完全没问题但它解决不了“确定性”问题。普通内核里一个高优先级任务想抢CPU时可能撞上中断处理、自旋锁临界区或者其他不可抢占的内核路径调度延迟的波动范围可以到几十毫秒甚至上百毫秒。对于人机交互这种场景几百毫秒延迟你根本感知不到可对EtherCAT这种要求周期性抖动在微秒级的协议来说这简直是灾难。PREEMPT_RT补丁做的事情本质上是把内核变得“几乎处处可抢占”中断线程化以前一个硬中断来了CPU立刻跳进去处理处理完之前谁也别想抢。打补丁后中断变成内核线程有优先级可以被调度器管理高优先级实时任务可以压过中断线程先跑。可抢占的自旋锁普通自旋锁持有期间禁止抢占RT补丁把大多数自旋锁改成可睡眠的互斥锁锁等待不会让实时任务傻等。优先级继承当低优先级任务占着锁、高优先级任务在等锁时低优先级任务会临时提升优先级避免“优先级反转”导致实时任务被无限期卡住。用生活化的方式理解普通内核像一个特别忙的餐厅厨师炒菜时手上的活不干完绝不接新单所以VIP客人来了也得等。RT内核相当于给餐厅装了传菜铃任何时刻有VIP订单进来厨师都能立刻放下手里的活去响应而且其他服务员还得给VIP让路。实时补丁不会让系统“变快”它让系统对关键任务的响应时间变得可预测、波动极小这才是“实时”的意义。1.2 用EtherCAT和igc网卡来理解“实时抖动”EtherCAT主站软件比如IgH、Acontis、TwinCAT依赖网卡以固定周期发送帧周期通常是1ms甚至250us。主站每发一帧从站处理完再传回主站等到回帧后立刻开始下一周期。如果内核调度抖动大主站线程错过发帧窗口轻则周期漂移重则总线断连、从站进入Safe-OP失败。散热器上这类主站最常用的网卡是Intel I210/I225/I226等型号对应内核里的igc或igb驱动。6.6.119这个内核版本值得特别提一下它对igc系列网卡的支持比较成熟很多EtherCAT主站厂商的兼容性列表里也包含这个驱动。配合RT补丁后实时主站线程的周期抖动可以从几十微秒降到个位数微秒。所以你在选内核版本时不能只看“最新”还得看两个匹配一个是RT补丁是否发布了对应版本另一个是网卡驱动在你的内核里是否稳定。6.6.119正好同时满足这两个条件这也是为什么它成了当前不少工控方案的默认选择。1.3 先分清你是真需要RT还是看着厉害动手之前先泼一盆冷水不是所有项目都需要RT补丁。如果你只是普通的数据采集或者控制周期在10ms以上Ubuntu默认内核配好nice优先级可能就够了。打了RT补丁的内核因为增加了大量可抢占点整体吞吐量会有一定下降某些批量运算场景反而变慢。我的判断标准很简单控制周期在1ms或更短且对抖动有硬性要求的必须RT。控制周期在几毫秒以上容忍几十微秒抖动的先用普通内核实时线程优先级试试通常能撑住。纯粹跑深度学习训练、科学计算完全不用碰RT把GPU驱动伺候好就完了。想清楚了再往下走免得白折腾半天最后发现瓶颈根本不在内核。2. 动手前的版本匹配与工具链准备2.1 为什么6.6.119配patch-6.6.119-rt是当前最稳的选择实时补丁的发布节奏和主线内核严格一一对应。拿到的补丁文件是patch-6.6.119-rt.patch它只能打在linux-6.6.119.tar.xz这个源码包上版本差一点都不行。你没法拿一个6.6.120的内核去硬打6.6.119的补丁这种错位会引发大量patch冲突修起来非常痛苦。选版本时要考虑三点长期维护分支6.6是LTS长期支持版本RT补丁更新持续且稳定社区和厂商都会持续跟进。与周边模块的兼容性EtherCAT主站、NVIDIA驱动、第三方内核模块它们对新内核的适配总是滞后的。选一个已经发布一段时间、被大量人验证过的版本比追新稳妥得多。补丁发布时间去内核官网的RT分支页面看patch-6.6.119-rt已经正式发布说明该版本没被社区发现明显问题。你的Ubuntu版本只要是20.04以上编译这个内核都没问题。20.04用老一点工具链也能编22.04是当前体验最好的24.04注意一下gcc版本较新个别配置项可能有小变化但整体不影响。2.2 编译依赖清单与磁盘规划编译内核需要装一堆工具直接用apt一把梭sudo apt update sudo apt install build-essential libncurses-dev libssl-dev bc bison flex libelf-dev dwarves逐个说下这些是干嘛的build-essential提供gcc、make等基础编译工具链。libncurses-devmake menuconfig图形化配置界面需要它。libssl-dev内核里部分模块比如内核模块签名需要OpenSSL头文件。bc内核编译脚本里大量用到这个计算器工具少它会在配置阶段直接报错。bison和flex解析内核配置文件和设备树文件的语法分析器。libelf-dev编译某些ELF相关工具需要。dwarves如果开启BTFBPF Type Format功能pahole工具需要这个包Ubuntu 22.04以后的内核配置经常默认带BTF。磁盘空间也要提前看。完整编译一次内核源码加编译产物大概需要15~20GB空间其中.o文件占大头。我吃过一次亏只给/分了30GB编译到一半磁盘满了整个构建失败。建议编译前用df -h检查给/usr/src所在的挂载点留够空间如果不够就提前清理APT缓存或者调整挂载。下载源码时注意别用Git仓库自己切tag再等RT补丁最省心的方式是直接下载两个文件cd /usr/src sudo wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.119.tar.xz sudo wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/6.6/older/patch-6.6.119-rt.patch.xz如果你在的网络环境访问kernel.org比较慢可以用镜像站或者把文件下载后传到/usr/src。文件不大源码压缩包大概130MB左右补丁压缩包只有几MB传起来不难。3. 打补丁与内核配置紧盯着抢占模型就对了3.1 解压、打补丁、校验把源码包解压然后进入目录打补丁cd /usr/src sudo xz -dk linux-6.6.119.tar.xz sudo tar xf linux-6.6.119.tar sudo xz -dk patch-6.6.119-rt.patch.xz cd linux-6.6.119 sudo patch -p1 ../patch-6.6.119-rt.patchpatch -p1的意思是去掉路径里的第一层目录因为补丁文件内的路径格式是a/kernel/sched/core.c b/kernel/sched/core.c需要从linux-6.6.119目录内执行才能正确命中。打完补丁后建议看一眼是否有FAILED字样。正常情况下输出会是一长串patching file ...如果你看到任何.rej文件生成说明补丁没打干净。用find . -name *.rej搜一下有结果就说明源码和补丁版本不匹配需要重新核对。3.2 基于现有Ubuntu配置生成.config的三种做法内核编译前必须先生成.config配置文件。最省事的不是从零配置而是基于当前Ubuntu内核的配置修改这样能保证默认配置和你现有硬件兼容。做法一复制当前内核配置最推荐cp /boot/config-$(uname -r) .config这里有个坑当前如果你原本跑的就是generic内核它的配置里开启了很多Ubuntu定制项这些项在RT补丁下大多数没问题少数会有Kconfig依赖冲突。别慌先跑一下make olddefconfig让它自动把失效项清理掉make olddefconfig做法二用scripts/config脚本精确打开RT模式scripts/config --enable PREEMPT_RT make olddefconfig这种做法比进menuconfig里翻菜单高效得多尤其适合脚本化操作。打开PREEMPT_RT后内核会自动关闭其他抢占模式比如PREEMPT、PREEMPT_DYNAMIC因为实时抢占模型是互斥的不可能同时存在。做法三make menuconfig手动确认想直观确认配置状态的话跑make menuconfig进入Kernel Features - Preemption Model选中Fully Preemptible Kernel (Real-Time)。如果你看不到这一项十有八九是.config里还有别的抢占模型冲突项回到做法一重新来。3.3 关键配置项详解与常见配置冲突打开RT模式后有几个配置项值得顺手检查CONFIG_HZ建议保持在1000Hz也就是CONFIG_HZ_1000y。实时任务调度粒度更细EtherCAT周期精度更高。CONFIG_NO_HZ_FULL可以开启让用户态的实时任务所在CPU核不再接收周期性时钟中断减少干扰。但注意这个配置需要配合内核启动参数nohz_full使用否则不会生效。CONFIG_DEBUG_INFO如果你的磁盘空间紧张可以考虑关掉能省不少编译时间和空间。不过没了它后续排查内核问题时不方便看符号建议空间够就保留。CONFIG_SCHED_DEBUG调试用生产环境关掉减少调度路径上的额外开销。CONFIG_FTRACE保留做实时性排查时ftrace几乎是必备工具后续想看中断延迟、函数调用耗时都靠它。最常见的配置冲突是你复制了Ubuntu的config后里面可能打开了CONFIG_PREEMPT普通抢占当你通过menuconfig切到RT时Kconfig会自动把CONFIG_PREEMPT关闭。但如果你在旧配置上同时开了某些依赖具体抢占模型的调试选项会报出类似“warning: ... selects PREEMPT_NONE which has unmet direct dependencies”这样的错误。这时候不用手工去改那一堆依赖项直接跑make olddefconfig让它自动按Kconfig规则调整。有个小技巧改完配置后把.config备份一份。万一后面编译失败不用从头再来。cp .config /usr/src/config-6.6.119-rt.backup4. 编译、安装与启动后的实时性验证4.1 用bindeb-pkg生成deb包而不是直接make install我强烈建议你用deb包方式安装而不是传统的make install make modules_install。原因很简单deb包能让内核纳入dpkg管理卸载、升级都有章可循不至于在系统里留下一堆不知道是谁的内核文件。在linux-6.6.119目录下执行make -j$(nproc) bindeb-pkg-j$(nproc)是让编译器用满所有CPU核心我8核16线程的机器编译这个内核大概花了25分钟。如果你的是4核老机器做好等1小时以上的准备。编译期间可以干点别的但别开make clean或者删源码目录。编译完成后会在/usr/src生成几个deb文件cd /usr/src ls -lh linux-*.deb重点装这两个sudo dpkg -i linux-image-6.6.119-rt*.deb linux-headers-6.6.119-rt*.deblinux-headers包千万别省后续DKMS编译NVIDIA、EtherCAT等第三方内核模块全靠它。很多人在这一环省了结果显卡驱动编译时找不到头文件白折腾半天。4.2 安装、更新GRUB、启动选内核装完deb包后更新GRUB菜单sudo update-grub重启后在GRUB菜单的Advanced options for Ubuntu下能看到6.6.119-rt这个内核。第一次建议手动选它启动别直接改默认项先确认能正常进系统再做调整。如果想默认进RT内核编辑/etc/default/grubGRUB_DEFAULTAdvanced options for UbuntuUbuntu, with Linux 6.6.119-rt然后sudo update-grub。注意这个写法是两级子菜单的索引方式每台机器实际的菜单名可能有差异用之前先看grep menuentry /boot/grub/grub.cfg确认一下条目名。4.3 cyclictest实测怎么看实时性达标没达标启动RT内核后先做基础验证uname -r # 输出应包含 6.6.119-rt cat /sys/kernel/realtime # 输出 1 表示当前内核启用了RT如果/sys/kernel/realtime输出是0说明内核虽然带着rt后缀但抢占模型实际没生效基本可以确定是配置阶段没把CONFIG_PREEMPT_RT打开。进一步做实时性定量测试需要安装rt-testssudo apt install rt-tests然后跑一个典型的cyclictest测试sudo cyclictest -t 1 -p 99 -i 1000 -l 100000参数含义-t 1只跑一个实时线程-p 99设为SCHED_FIFO策略最高优先级-i 1000周期设为1000微秒-l 100000跑10万个周期总共100秒。看输出里的max列T: 0 ( 1234) P:99 I:1000 C: 100000 Min: 2 Act: 3 Avg: 3 Max: 18Max: 18表示最大延迟18微秒。在RT内核上这个数值通常能压在20微秒以内如果跑出上百微秒甚至毫秒级说明系统里有干扰源可能是NVIDIA驱动占用大量中断、BIOS电源管理设置不合理、或者CPUFreq调节器不合适。这时候先别急着优化继续看下面显卡驱动的问题很多时候显卡驱动修好了实时性数据也跟着变好了。5. 显卡驱动在RT内核下崩了的完整排查链路5.1 先认清崩掉的三种典型症状RT内核装好、重启、选新内核进入结果发现桌面起不来GDM/ LightDM 登录界面一直转圈或者黑屏只有一个鼠标光标。进到桌面但分辨率不对识别不出显示器图形卡在低分辨率或者显示nouveau相关的报错。终端里nvidia-smi直接报错比如NVIDIA-SMI has failed because it couldnt communicate with the NVIDIA driver。这三种症状本质是同一个根源RT内核启动时NVIDIA内核模块没有被正确加载。要么是模块编译失败要么是模块签名无效被拒绝加载要么是nouveau驱动抢先占用了GPU。5.2 按顺序排查dmesg、DKMS状态、头文件、Secure Boot别一上来就重装驱动先按链路排查每步都有明确结论。第一步在GRUB菜单按e在linux行末尾加nomodeset然后按CtrlX启动。nomodeset会让内核不做模式设置用基本显示输出至少能进终端排查问题。如果你用Ubuntu Server版没桌面可以跳过这步。第二步进入系统后看日志确认NVIDIA模块加载失败的现场dmesg | grep -i nvidia | tail -n 30 journalctl -b -g nvidia常见的报错信息有“module verification failed: signature not found”和“Unknown symbol”前者指向Secure Boot签名问题后者指向DKMS编译失败或者头文件不匹配。第三步查DKMS模块状态dkms status正常输出应该是类似nvidia/535.xx.x, 6.6.119-rt, x86_64: installed如果显示build failed说明DKMS尝试在新内核上重新编译NVIDIA模块时失败了。失败的详细日志在/var/lib/dkms/nvidia/535.xxx/build/make.log直接打开看最后几十行能定位到具体编译错误。第四步确认内核头文件是否装全ls /usr/src/linux-headers-6.6.119-rt*如果你用bindeb-pkg方式安装头文件是单独的deb包必须确认已经装好。没有头文件DKMS根本无从构建。第五步查Secure Boot状态mokutil --sb-state如果输出SecureBoot enabled而你的NVIDIA模块没有通过MOK签名内核会拒绝加载它。这就是开头那个“signature not found”报错的来源。5.3 修复方案重装DKMS、runfile安装与核显兜底方案A重装已安装的NVIDIA驱动最简单但请先处理Secure Boot如果排查确认是DKMS编译失败最直接的办法是重新触发DKMS构建sudo apt install --reinstall nvidia-driver-535 sudo dkms autoinstallUbuntu 20.04、22.04、24.04上NVIDIA驱动版本号不同可以根据自己机器执行ubuntu-drivers devices查看推荐版本。如果Secure Boot处于启用状态重装前我建议先去BIOS把它关掉或者用mokutil --import导入DKMS生成的公钥流程有点繁琐工控机上没有特殊安全需求的话直接BIOS关闭最快。方案Brunfile手动安装适合apt源里没有的驱动版本如果你需要特定版本NVIDIA驱动比如CUDA版本配套的用官方runfile更灵活sudo telinit 3 sudo ./NVIDIA-Linux-x86_64-550.xx.run --dkmstelinit 3会切到多用户文本模式把图形界面完全停掉否则安装会提示“You appear to be running an X server”。安装时选--dkms参数让驱动注册到DKMS这样以后换内核时它能自动重新编译。runfile安装完nvidia-smi能出来就说明模块加载成功。方案C核显兜底如果是纯工控场景控制任务和图形界面可以分离。主板有核显输出的话把显示器插到主板接口上BIOS里设核显为首选显示NVIDIA GPU只留给CUDA计算。这样就算NVIDIA驱动在RT内核下没好系统也能正常显示实时控制不受影响。这个方法看起来有点“逃避”但在生产环境里特别实用因为RT内核下NVIDIA驱动的长期稳定性说句实在话没有官方保证能分离就别硬绑。5.4 教训驱动与内核的先后顺序到底怎么安排我这次踩坑的根因就是我先在内核源码目录里折腾编译又在旧内核上把NVIDIA驱动卸载了结果RT内核装好后DKMS找不到可用的模块源又因为Secure Boot开着新模块签名失败整个链路彻底崩掉。复盘出来的合理顺序应该是在旧内核系统上先把NVIDIA驱动装好确认nvidia-smi正常。再编译RT内核并安装让DKMS用已安装的驱动源自动适配新内核。重启进RT内核如果nvidia-smi报错再按上面的排查链路走。换句话说驱动安装最好在打内核补丁之前就完成。驱动本身不依赖RT补丁但RT内核依赖驱动源和头文件。如果驱动已经注册到DKMS每次换内核时它会自动尝试编译少很多手动操作的环节。6. RT内核与显卡驱动共存的生产级建议6.1 实时任务与GPU驱动各占各的CPU核RT内核和NVIDIA驱动能共存但不意味着它们应该互相干扰。NVIDIA驱动的中断、DMA、内存分配会引入不可忽略的延迟实时任务如果和GPU中断撞在同一个CPU核上cyclictest的max值会明显变差。我实际用的方案是在GRUB里隔离出专用实时核GRUB_CMDLINE_LINUX_DEFAULTquiet splash isolcpus2,3 nohz_full2,3 rcu_nocbs2,3这段参数的意思是CPU 2、3号核心从Linux调度器中隔离出来不跑普通进程nohz_full让这两个核减少周期性时钟中断rcu_nocbs让RCU回调不在这些核上执行。然后在应用层把EtherCAT主站线程用sched_setaffinity绑到2、3号核上NVIDIA驱动中断和桌面进程就留在0、1号核上跑。这样一来cyclictest的抖动可以稳定在10微秒以内GPU并行训练也不影响实时链路。6.2 内核多引导与回滚策略生产环境最怕的不是出问题而是出问题后回不去。装了RT内核后旧内核的GRUB菜单项依然保留这是天然的回滚通道。但需要注意如果在新内核里系统起不来GRUB菜单还是能进的选择旧的generic内核启动后可以卸载RT内核也可以继续排查。我建议保留至少一个已确认能用的旧内核别为了省磁盘空间把旧内核删干净。工控设备上一个可启动的备用内核就是救命的。另外装RT内核后如果做过update-grub小心有些云镜像或定制系统会自动清理旧内核要在/etc/apt/apt.conf.d/里检查有没有开自动清理开了的话改成保留旧内核。6.3 一套我验证过的“重来一次”安装顺序最后一次总结我实测过的全过程顺序跟着走最省事在原来正常工作的Ubuntu系统上先装好NVIDIA驱动确认nvidia-smi正常并确认dkms status里能看到对应模块。安装编译依赖下载6.6.119源码和RT补丁。解压源码、打补丁确认没有.rej文件。复制/boot/config-$(uname -r)为.config执行scripts/config --enable PREEMPT_RT再执行make olddefconfig。编译并生成deb包make -j$(nproc) bindeb-pkg。安装linux-image-6.6.119-rt*.deb和linux-headers-6.6.119-rt*.deb。sudo update-grub重启手动选择RT内核。验证uname -r和/sys/kernel/realtime再跑cyclictest确认实时性。如果nvidia-smi报错按第五章的链路排查如果正常下一步配置isolcpus和nohz_full做实时核隔离。我实际把机器完全按这个顺序重做了一遍全程没再出现模块签名错误和DKMS构建失败的问题。RT内核下NVIDIA驱动的确能跑但别指望它像generic内核下那样“百毒不侵”偶尔升级驱动或内核后还会冒出新问题。做好隔离、留好回滚通道、别删旧内核这三件事比任何配置技巧都管用。