资讯动态

LWN内核补丁获取到编译安装:实战全流程指南

发布时间:2026/10/20 16:15:17 来源:尧图企业网站定制
1. 先搞清楚一件事LWN上翻到的patch哪一份才值得打1.1 LWN在补丁链路里的真实角色索引不是下载站我最初以为LWN是内核补丁的“官方发布渠道”后来才发现理解偏了。Linux内核的补丁日常流转主要靠邮件列表维护者每天会收到几十上百封补丁邮件这时候LWN起的作用更像一个补丁索引和讨论聚合平台。它把重要补丁按主题整理成页面提供补丁标题、版本号、所属子系统、维护者回复甚至能把整个补丁系列按时间顺序列出来。但LWN本身不托管内核源码也不会对补丁做编译验证它只是帮你“看到”补丁上下文真正要拿来打进入内核的patch文件还得从补丁邮件存档下载。这个区分很重要。我见过有人直接从LWN页面里的diff代码块复制粘贴再存成patch文件去打结果要么因为HTML转义把空格变成了不可见字符要么因为复制了行号补丁根本打不进去。正规做法是找到页面中指向邮件存档或mbox下载的链接把原始patch文本单独保存下来。LWN的价值在于它帮你节省筛选时间而不是替代原始补丁源。另外一点也要有预期LWN上会同时出现很多仍在讨论中的补丁不代表都已经合入主线。如果只是想修一个自己碰到的内核bug最好先确认这patch是否已经被维护者接受或者是否已经随某个稳定版发布。否则你打了一个还在反复修改的v1补丁后面维护者又改了v3、v4你反而要花更多时间追版本。1.2 用“标题、版本号、依赖信息”三招判断要不要下手打开任意一条LWN补丁页面信息密度很高但真正决定“你要不要动手”的只有三处。第一处是补丁标题里的[PATCH v2]这类标记它直接告诉你这是补丁的第几版。我通常会忽略v1除非某个修复只存在v1且没有后续版本否则优先看最新版。第二处是补丁对应的目标分支常见有mainline、linux-next、stable等。如果LWN页面或补丁邮件里写了“base-commit”你还能直接知道它在哪个git提交之上。第三处是依赖关系有些补丁是一个系列里的第3封前面还有第1、2封如果你只打这一封编译大概率失败。我自己总结了一张判断表打patch之前逐项对照一下检查项在哪里看需要判断什么补丁版本号邮件主题[PATCH vN]是否存在更新的版本目标内核分支邮件头或描述是适用于stable还是mainline当前系统内核版本uname -r与补丁基线是否接近所属子系统标题中模块名/驱动名是否涉及你正在用的硬件依赖的前置补丁补丁系列列表前置补丁是否也需要打维护者态度讨论回复是否有明显反对意见这里有一个容易踩的坑看到[PATCH]不带版本号就以为是“唯一版本”但维护者可能已经合并了只是这个帖子是很早之前的v1。LWN通常会在同一主题下列出该补丁系列的多次迭代如果不点进去看维护者的最新回复很容易被旧版本误导。我一般在确认patch版本后再去搜索一下这个补丁是否已经出现在某个已发布的内核tag里比如查stable版的变更日志。如果已经合并那就没有理由再手动打同一个补丁了。1.3 一次失败的选型让我不再“见PATCH就打”说个真实经历。之前一台测试机遇到网络模块丢包问题我在LWN上看到一个很对症的修复补丁标题写着[PATCH net]没多想就下载、解压源码、打补丁、编译。结果编译到一半报错函数签名对不上去邮件列表一看这个补丁后来被人重构过已经是v3而且v2修复了一个v1引入的编译警告v3又改写了部分逻辑。我手上的v1在原版本内核上能过但在自己源码版本上不行。那次我花了半小时下载依赖工具最后只得到一个编译错误。从那以后我养成两个习惯。第一个习惯是打patch前至少把该系列页面的最后两版diff都看完确认自己选的是最新版。第二个习惯是看补丁旁边的“discussion”链接如果维护者回复里指出了问题我就会谨慎一点哪怕标题看起来再对症。手动给内核打补丁不是不能用但要用得像个研究流程而不是下载软件包一样直接点安装。2. 从LWN定位patch文件下载、查看、dry-run三步走2.1 别从网页复制代码块要下载mbox或raw格式LWN文章里经常直接展示补丁diff的内容看起来很方便但我不建议直接从HTML页面复制。原因有两个一是网页为了排版会把、-开头的行保留成特殊空白复制时可能把制表符变成一串空格patch工具对空白很敏感二是部分页面会截断长行复制下来就不是完整补丁了。更稳妥的路径是找到补丁对应的邮件存档下载mbox格式或者直接下载单个补丁的raw文本。mbox格式适合一个补丁系列包含多封邮件的情况它会按顺序把所有补丁邮件打包在一起如果你只需要其中一个可以用命令行工具拆开或者干脆下载该邮件单独的raw补丁。实际操作中我在LWN页面里看到某个patch的编号后一般会把它指向的邮件存档markdown链接复制出来然后使用类似wget的命令下载mkdir -p ~/patches cd ~/patches wget -O net-fix.patch https://邮件存档的raw链接下载完先不要急着打先用file net-fix.patch或head -20 net-fix.patch看一眼文件头确认这是一个正常的Git diff补丁而不是一个HTML页面。我遇到过下载下来的是404页面存成.patch文件后完全无效的情况检查文件头是最快的拦截方式。2.2 打patch之前必须做的两个静态检查第一项检查是确定-p层级。内核补丁通常使用Git生成diff头会写成这样diff --git a/drivers/net/ethernet/xxx/xxx.c b/drivers/net/ethernet/xxx/xxx.c index 12345..67890 100644 --- a/drivers/net/ethernet/xxx/xxx.c b/drivers/net/ethernet/xxx/xxx.c这种格式说明补丁路径以a/和b/作为前级目录打patch时要用-p1意思是剥掉a/这一层。如果补丁是从某个非Git版本导出的路径可能是drivers/net/...这种不带前级的格式那就要用-p0。判断起来不复杂但很多人懒得看直接用-p1偶尔遇到-p0的补丁就会提示找不到文件。第二项检查是确认补丁基线和当前源码是否匹配。补丁邮件头部经常会有base-commit字段或者内文里写着“拜测于5.15.y”之类的信息。如果你用的是git仓库可以用git describe --always来确定当前提交如果你用的是tar包那就看顶层Makefile里的版本号head -5 Makefile对比一下patch里隐含的基线相差太远就不要硬打。曾经有人想在一个5.10内核上打一个针对5.15的stable补丁patch工具虽然会用“拒绝生成”的机制跳过不匹配的上下文但结果往往是源码里完全没有对应的函数最后编译失败。内核补丁不是给任意版本套用的基线这一关过不去后面全白做。2.3 dry-run是给你“反悔机会”的那一步下载好patch文件、确认源码基线之后真正打patch前一定要先跑一次dry-run。命令很简单cd linux-5.15.78 patch --dry-run -p1 ../patches/net-fix.patch如果输出类似checking file drivers/net/ethernet/xxx/xxx.c Hunk #1 succeeded at 128 (offset 3 lines).说明补丁能对上。注意后面括号里的offset表示行号有偏移只要偏移量不太大通常没问题。如果看到Reversed (or previously applied) patch detected! Assume -R? [n]说明这个补丁可能已经打过了问你要不要反向应用直接选n。如果看到cant find file或Hunk #1 FAILED那就别继续先去查源码版本或补丁版本。dry-run不会修改任何文件所以可以放心试。确认无误后再正式执行patch -p1 ../patches/net-fix.patch输出会变成patching file drivers/net/ethernet/xxx/xxx.c这时补丁才真正进入源码树。我自己现在只要打补丁一定把dry-run和正式打分成两步走避免一次性命令在失败时留下半个补丁的状态。如果正式打的时候进入失败状态也别想着手动修残缺片段干脆重新解压源码或者用git回滚干净重来一次比修补更快。2.4 如果你用的是git仓库git am是另一种选择不少内核开发者习惯直接clone一份git仓库而不是下载tar包。这种情况从LWN拿到的补丁如果本身就是Git格式头部有From:和Subject:我建议用git am而不是patch命令。好处是补丁会作为一个正式commit落进仓库后续要回退只要git reset --hard HEAD^非常干净还能保留作者信息和提交说明。命令是cd linux-source git am ../patches/net-fix.patch但git am更“挑剔”它要求补丁元数据完整某些只适合用patch工具直接应用的补丁反而过不了。我的习惯是临时测试某个补丁、以后不打算长期保留用patch -p1如果是在某个开发分支上做持续维护用git am。这没有对错只是看你想要什么样的追溯能力。3. 编译内核前最容易被忽略的准备工作源码基线和配置3.1 先让源码和patch“门当户对”我见过有人随便下载了一个linux主线源码然后尝试打一个stable补丁结果行号差了几十处patch虽然能打上但代码逻辑完全对不上。内核源码版本与补丁基线必须匹配这是整个流程里最不该省的一步。先跑一次uname -r假设返回5.15.78那需要准备的就是5.15.78对应的源码。如果LWN页面里的补丁明确说基于“5.15.78”或某个git提交那就直接以这个版本为基线。如果你用tar包去官方内核镜像站下载对应版本如果你用git可以浅克隆当前taggit clone --depth 1 --branch v5.15.78 https://内核源码镜像地址/linux.git cd linux深度为1的clone很快而且只保留v5.15.78这个点后续打patch时已经不需要历史记录。唯一要留意的是像git am可能需要更多历史才能处理某些冲突但这种场景很少。tar包的好处是不依赖git适合一次性测试坏处是回滚只能重新解压。我自己现在倾向用git浅克隆因为打patch后可以用git diff直观检查改动出了问题也能快速reset。3.2 从当前系统配置出发别从零开始搞menuconfig内核编译最折腾人的不是编译本身而是配置文件。如果你对内核选项不熟用make menuconfig一项一项勾选一个晚上都弄不完。对绝大多数场景正确的做法是把当前运行内核的配置拿过来复用。方式有两种# 方法A如果内核开启了CONFIG_IKCONFIG_PROC zcat /proc/config.gz .config # 方法B从/boot目录复制现有配置文件 cp /boot/config-$(uname -r) .config拿到.config之后执行make olddefconfig它的含义是保留.config里已有的配置项对当前源码里新增的选项采用默认值。这样不会因为某个配置项缺失而漏掉你需要的功能也不需要你对着几百个选项逐个确认。这一步做完再运行make menuconfig手动调整一下比如确认自己打补丁涉及的模块被编译成y还是m。这一条在编译驱动补丁时特别重要。很多内核补丁会新增一个CONFIG_FOO选项如果你直接用旧的.config这个选项根本不会出现在.config里导致补丁代码虽然是打进去了但编译器通过#ifdef把这段代码跳过你从结果上完全看不到它的存在。遇到这种情况可以在make menuconfig中按/搜索补丁提到的配置项把它设置为m或y再进行编译。3.3 工具链不齐编译到一半才报错是最浪费时间的事内核编译对工具链有硬性要求缺了某些包会在不同阶段给你颜色看。最常见的是缺少flex和bison它们负责生成内核解析器代码缺了直接报No rule to make target ...还有libssl-dev在做某些安全功能时会用到头文件libelf-dev则影响ELF相关的工具。另外如果以后要改菜单配置libncurses-dev也不能少。我一般先这样检查make -j$(nproc)第一次编译就开满线程机器内存小的话很容易把系统卡死。后来学乖了先用nproc看核心数然后留一个核心给系统比如make -j$(($(nproc)-1))。如果内存只有4G干脆用make -j2。另外磁盘空间也要留意一个完整内核编译加中间文件可能要20G以上如果磁盘紧张可以在编译配置里关掉调试信息比如CONFIG_DEBUG_INFOn能省掉不少临时空间。别小看这些准备准备工作做到位后面的编译安装一条龙才会顺。4. 从编译到安装手动选择内核启动的完整链路4.1 make、modules_install、install的顺序和理由打patch完成、配置确认无误后进入核心环节。命令序列是make -j$(nproc) sudo make modules_install sudo make install为什么顺序不能乱因为内核模块和内核镜像是一套整体。make modules_install会把编译好的.ko模块安装到/lib/modules/新版本号/目录而make install负责把内核镜像、System.map和initramfs生成的信息放到/boot并更新引导配置。如果先执行make install引导菜单里有了新内核但/lib/modules下还没有对应模块启动到挂载根文件系统时很容易失败因为文件系统驱动或磁盘控制器驱动找不到了。我在这上面栽过一次。当时为了省时间跑完make -j直接执行make install重启选择新内核结果卡在挂载根文件系统屏幕上一片报错。后来才反应过来模块还没装。解决方法是重启进旧内核老老实实补跑sudo make modules_install然后重新生成initramfs再重启才正常。所以我现在把这三条命令当成一个不可拆分的顺序一步都不跳。另外一点如果补丁改动的驱动被编译成模块m这一步尤其重要。模块没装内核即使能起来你需要的驱动也不会加载效果等于没打补丁。4.2 安装完成后怎么在启动时手动挑中那颗新内核安装完成并不代表系统会默认进入新内核很多发行版的GRUB默认启动项还是旧内核。手动选择内核是验证补丁最稳妥的方式因为它只影响本次启动不会导致系统不可逆转。先看启动画面。BIOS引导模式下开机时按住Shift键GRUB菜单通常就会显示出来UEFI模式则可能是开机后连续点按Esc或按制造商定义的引导快捷键。如果你的系统把GRUB菜单遮住了也可以修改配置让菜单停留时间变长。常见做法是编辑/etc/default/grubGRUB_TIMEOUT5 GRUB_TIMEOUT_STYLEmenu然后重新生成GRUB配置。这样每次开机都能看到菜单不需要手速抢拍。菜单显示后找到类似“Advanced options for Linux”的入口再选择带新版本号的内核项回车。这里要注意部分发行版第一项是旧内核新内核放在“高级选项”子菜单里别选错。如果GRUB配置重新生成后新内核项依然没有出现多半是make install阶段没有正确触发生成initramfs。此时先别急着重启手动检查一下ls /boot/initrd.img-新版本号如果这个文件不存在或为空就需要手动生成initramfs再更新GRUB配置。生成的命令因发行版不同略有差异但思路一致让initramfs工具读取当前内核的模块目录生成对应版本的ramdisk然后重新扫描引导菜单。这个过程不复杂但在手动选内核的场景里很容易被漏掉。4.3 启动后验证、排查和紧急回滚顺利进入新内核后第一件事是确认你确实跑在新版本上uname -r如果输出的是新内核版本号再检查补丁涉及的模块是否加载成功lsmod | grep 模块名 dmesg | grep 模块名如果补丁是修改已有驱动的重点看dmesg里有没有异常报错。如果新内核出现明显问题比如网卡不通、文件系统挂载失败、显卡驱动异常最简单的处理不是现场调试而是重启后从GRUB菜单里重新选择旧内核。旧内核项不会因为你安装了新内核就被清除除非你手动清理。这种“双内核并存”的状态非常适合验证性补丁测试。等确认新内核稳定、补丁生效后再考虑要不要把默认启动项换成新内核。需要改的时候可以用grub-set-default指定新内核菜单项或者在GRUB配置文件里直接修改默认值。但注意手动选择是测试阶段的低成本方案改默认值则是长期使用前的最后一步。我建议至少让新内核连续跑几天没问题再动默认项避免因为一个补丁导致的隐性Bug影响日常使用。4.4 补丁打完、内核装完源码树的清理也值得一提整个流程结束后源码目录里会有构建产生的海量中间文件占用空间很大。如果不再需要继续开发可以保留源码但至少要记住patch文件应该放在源码树之外避免下次重新编译时误以为源码是干净的。我习惯在~/patches/目录统一存放所有补丁文件名带日期和内核版本比如20240528-net-fix-5.15.78.patch。这样哪份补丁对应哪个基线时间一长也不会搞混。还有一个小技巧如果一次打了好几个补丁可以在正式打patch前用diff -u把当前源码树保存成一个自己的patch将来想完全恢复原始状态直接反向应用即可。或者用git仓库的话打完补丁后立刻git diff my-combined.patch相当于做了一个逻辑备份。我在实际项目中经常这样操作尤其当补丁来自不同邮件讨论时合并后的patch比分散文件好管理得多。

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

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

免费获取报价 →
↑