1. 动手下载之前先把你要哪一份内核这件事定死第一次给一块嵌入式板子找内核源码时我直接抓了当时最新的 mainline tarball编译出来的内核在板子上跑是能跑但板载的那颗 WiFi 芯片死活不认。折腾了两天才发现厂商 BSP 里那份 4.19 的内核塞了几十个私有补丁跟主线完全是两套东西。从那以后我形成了一个固定习惯下载内核源码之前先花十分钟把版本谱系确认清楚比后面省两天时间划算得多。linux 内核源码的获取渠道看起来就是官网点一下这么简单实际上不同渠道给到你的东西差别巨大——有的是一份干净的主线快照有的是打了上百个补丁的发行版源码包有的是你根本编译不过去的半成品分支。选错了后面所有的编译、调试、裁剪都是在错误的地基上盖楼。1.1 内核版本号里藏着的发布节奏内核社区的发布流程是相当机械化的理解它你就能预判每个版本号的稳定性。一个完整周期是这样走的合并窗口打开两周期间 Linus 把各家子系统维护者的 pull request 合进来这时候代码变动最大、最容易炸两周后窗口关闭打出第一个候选版本-rc1之后每周一个-rc通常到-rc7或-rc8就发正式版。所以从-rc1到正式版大约是六到八周整个大版本周期在九到十周左右。版本号x.y.z.w的四个数字含义也不同。x.y是主版本比如 6.12z是稳定版的小版本号比如 6.12.5表示在 6.12 基础上合入了五个批次的稳定修复w一般是某些发行版自己加的主线不用。真正需要你关注的是-rc结尾的一定是开发版别拿它上生产.z大于 0 的是稳定分支只进 bug 修复不会再合新特性。还有一个容易忽略的点——补丁文件本身就是一种下载物。kernel.org 上除了完整 tarball还会放增量补丁patch-6.12.5.xz它只包含从 6.12.4 到 6.12.5 的差异。如果你已经有一份 6.12.4 的源码树用补丁更新比重新下一整个包省得多几百 KB 就能搞定本地patch -p1 ../patch-6.12.5一打就行。1.2 mainline、stable、longterm、next 怎么选这几个分支名字经常被混着说但它们的用途完全不同我整理成一张表你对着自己的场景挑就行分支类型仓库/目录位置更新频率适合场景mainlinetorvalds 树每个大版本周期追新特性、写驱动上游补丁stablestable 树每周多次生产环境、需要稳定修复longterm (LTS)longterm 目录长期维护通常 2-6 年产品固件、嵌入式量产linux-nextnext 树每个工作日提前适配、测试未来接口变动发行版内核各家源码包跟随发行版复现线上问题、做内核模块LTS 是大多数人的实际落点。选 LTS 的判断标准很简单你的设备生命周期有多长就选维护周期覆盖它的那个版本。如果产品要卖五年选一个只剩一年维护期的版本后面就得自己扛 CVE 修复成本高得多。我之前有个项目为了用某个新驱动选了当时的 mainline结果半年后接口改动驱动源码直接编译失败被迫整体升级那次的教训就是——量产项目别追 mainline。linux-next更特殊它是各家子系统维护者分支的每日聚合用来在合并窗口前发现冲突。除了给上游贡献代码的人普通开发者基本不需要碰它因为它的状态每天都在变今天能编过明天不一定。1.3 从需求倒推三种典型场景的下载目标场景一学习内核机制、读源码。选一个 LTS 的完整 tarball本地解压后配好 ctags/cscope就够了。不需要 git 历史因为你读的是代码逻辑本身。场景二给某个硬件写驱动并希望上游化。必须用 git 克隆 mainline 或相关子系统树因为你要基于最新代码出 patch还要用git log、git blame查历史tarball 给不了你这些。场景三复现客户报的线上问题。目标不是主线而是客户机器上那个内核版本对应的源码包这时应该走发行版的源码渠道而不是 kernel.org。版本号里带的那串后缀比如 5.10.0-136就是线索它明确告诉你这是哪家的哪个补丁集。把这三类分清楚后面的下载方式选择就是自然而然的事而不是哪个链接看起来顺眼就下哪个。2. kernel.org 官方 tarball入口最浅细节最多tarball 是最直接的获取方式解压完就是一个可直接make的源码树没有.git目录没有历史包袱磁盘占用也小。但恰恰因为它简单很多人在校验、压缩格式、断点续传这几个环节上偷懒最后拿着一个不完整的包编译半天报一堆莫名其妙的语法错误。2.1 目录结构和文件命名规则拆解官方发布目录的分层是按版本大号走的比如v6.x/下放 6 系列的所有东西testing/放候选版本longterm/单独给 LTS。文件名规律也很固定linux-6.12.tar.xz6.12 正式版完整源码linux-6.12.5.tar.xz6.12.5 稳定版完整源码patch-6.12.5.xz从 6.12.4 到 6.12.5 的增量补丁同时也有.gz版本incr/patch-6.12.4-5.xz只针对前一个小版本的极小增量补丁sha256sums.asc该目录下所有文件的 SHA256 清单带 GPG 签名每个 tarball 对应的.sign文件该文件本身的 GPG 分离签名这里有个实操细节值得单独说sha256sums.asc是清单的签名不是清单本身。你得先gpg --verify sha256sums.asc验证清单没被改过再用清单去核对你下载的 tarball。这两步顺序不能颠倒否则你等于用一份可能被篡改的清单去验证另一份文件安全性归零。2.2 下载工具的取舍wget、curl、aria2 与 rsync单线程下载一个两百兆左右的 tarball 在国内网络下体验差别很大选工具的逻辑我一般是这样的wget -c加断点续传是最稳的兜底方案。它的好处是重试逻辑成熟网络抖一下自动重连不会把已下载的部分丢掉。命令很短wget -c https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.5.tar.xzcurl -C -效果类似优势是参数更灵活脚本里集成方便。aria2c -x 16 -s 16走多线程分片峰值速度确实能快不少但我要提醒一句分片下载碰到 CDN 的限流或者中途某个连接僵死时很容易写出一个大小看起来对、内容其实掺了重复块的坏文件。所以我用 aria2 的时候一定会跟一次完整的 SHA256 校验校验不过就丢掉重下绝不抱侥幸心理。rsync适合另一个场景你已经下过一份想补一个更新的小版本或者要把整目录拉到内网。它支持--partial保留断点-P同时开启进度和续传rsync -avP rsync://rsync.kernel.org/pub/linux/kernel/v6.x/linux-6.12.5.tar.xz ./下载完顺手用ls -lh看一眼大小再跟官方目录页显示的数字对一下。差个几 MB 就说明文件被截断了这种情况xz解压到一半会报 unexpected end of file比编译报错好定位得多。2.3 校验不是走形式sha256 与 GPG 双保险很多人觉得校验是多余步骤直到真的遇到一次镜像同步到一半的文件。正确的做法分两层第一层是 GPG 验证清单签名。首次操作需要导入内核发布者的公钥公钥可以从官方 keyserver 或者直接抓官方维护的公钥文件gpg --locate-keys torvaldskernel.org gregkhkernel.org gpg --verify sha256sums.asc输出里出现Good signature才算过。如果提示Cant check signature: No public key说明公钥没导进来把密钥 ID 拿去 keyserver 上取一次即可。还有一种情况是This key is not certified with a trusted signature的警告这表示签名有效但你的信任链没建立不影响使用。第二层是用清单核对文件本身sha256sum -c sha256sums.asc --ignore-missing--ignore-missing这个参数很实用因为清单里列了几十个文件你只有其中一两个不加它 cpu 会疯狂报文件不存在。看到linux-6.12.5.tar.xz: OK这一行才说明你手里这份文件确实和官方发布的一模一样。注意如果只做了一层就往下走等于给了一个看起来专业的假保证。签名过的清单 清单核对的哈希两层缺一不可。2.4 解压、验目录、建软链的固定动作解压本身一行命令但我习惯加上几个小动作。tar -xf在较新的 GNU tar 里会自动识别压缩格式不用手动指定-J或-z除非你的环境里 tar 版本很老。解压完第一件事是确认顶层目录名因为它会变成linux-6.12.5这样的名字如果你同时装了多个版本编译脚本里写死路径就很容易出错。我的做法是建一个稳定的软链tar -xf linux-6.12.5.tar.xz ln -sfn linux-6.12.5 linux cd linux head -5 Makefile看一眼Makefile开头那几行的VERSION、PATCHLEVEL、SUBLEVEL确认和你想要的版本一致。这一步花三秒能避免明明下了 6.12.5编译出来是 6.12.4这种因为旧目录没删干净导致的错觉。顺带提一句源码树解压后大概占 1.5 到 2 GB加上编译产物轻松破 10 GB所以目录最好放在空间充裕的分区上别塞在只有十几 G 的系统盘里。3. Git 拉源码完整克隆、浅克隆与部分克隆的账怎么算要历史、要打补丁、要追某个函数的来龙去脉就必须上 git。但内核仓库是全互联网最大的代码库之一克隆策略选不对轻则等一晚上重则磁盘直接写满。3.1 先算空间账和带宽账完整克隆torvalds/linux的代价我实测下来的数量级是这样的.git目录压缩存储大约 5 GB 上下checkout 出工作区再加 1.5 到 2 GB也就是一次完整克隆接近 7 GB。如果你还要拉 stable 树、next 树每个再加几 GB。磁盘上的 inode 消耗也别忽略内核有几万个文件如果文件系统 inode 数量配得紧克隆到一半报No space left on device但df -h显示还有空间就是 inode 用完了df -i一看便知。带宽方面完整克隆要传输的历史 pack 通常在 3 到 5 GB 量级慢速网络下几小时起步中途断一次虽然可以git fetch续但断在Resolving deltas阶段特别耗时因为它要重新计算。3.2 --depth、--shallow-since、--filter 的适用边界浅克隆是省时间的首选但它有明确的能力边界选之前得知道自己会不会需要历史# 只要最新一个提交约 200-300 MB git clone --depth 1 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git # 只要某段时间之后的提交 git clone --shallow-since2024-01-01 --single-branch -b linux-6.12.y url # 部分克隆不要历史 blob按需拉取 git clone --filterblob:none --no-checkout url--depth 1的代价是你没有git log历史没法git blame也没法用git bisect定位回归问题。--single-branch只拉一个分支的引用能省掉大量无关分支的传输。--filterblob:none是相对新的能力它只拉提交和树对象文件内容在 checkout 时按需下载适合我要看代码但不需要离线全量的场景但它对网络稳定性要求更高因为随用随取。我个人给团队的默认建议是日常开发和调试--depth 1加上--single-branch够用只有做回归定位或者要给上游发补丁时才做完整克隆而且放在夜间跑。3.3 切 tag 与 stable 分支的正确姿势浅克隆出来之后想切到某个具体版本的 tag要用--branch指定因为浅克隆默认只带一个 taggit clone --depth 1 --branch v6.12.5 --single-branch url对于 stable 维护树分支名形如linux-6.12.y这个.y是社区约定表示该稳定系列。切到这个分支后git pull就能拿到滚动更新的小版本比每次都重下 tarball 方便得多而且你本地编译过一次之后重新编译只走增量速度差别很大。判断当前状态用几条命令就够git describe --tags # 看当前离最近 tag 多远 git log --oneline -5 # 看最近五个提交 git status -sb # 看分支和脏文件3.4 裸仓库加 worktree多版本并行的省空间玩法如果你需要同时保留几个版本的内核比如帮客户分别维护 5.10 和 6.1每个都完整克隆一遍是浪费。更聪明的做法是建一个裸仓库然后用git worktree挂出多个工作目录git clone --bare url linux-bare.git cd linux-bare.git git worktree add ../linux-5.10 linux-5.10.y git worktree add ../linux-6.1 linux-6.1.y这样所有版本共享同一份对象库磁盘占用大幅下降切换也快。缺点是心理上要清楚这些目录同源在其中一个里删分支或者gc会影响全局。团队里用它做内网分发也很顺手裸仓库拷一次别人git clone /path/to/linux-bare.git就是本地千兆速度。4. 镜像站与离线分发把拉不动变成拉得快官方源在高峰期速度不稳定是普遍现象镜像站是常规解法。但镜像站也分优劣用之前得知道它同步了什么、延迟多久。4.1 镜像站都同步了什么主流高校与云厂商的镜像一般会同步两类东西一类是 tarball 目录结构完全照搬官方另一类是 git 仓库的只读镜像。tarball 的同步通常延迟几小时到一天git 镜像有时候只同步某些分支。使用前建议先看一眼镜像站的同步状态页或者时间戳文件确认不是几天前的旧数据。具体的替换方式就是把 URL 的域名换掉路径保持不变。这样写脚本时很好处理定义一个MIRROR变量切源时只改一处。有一点要说明清楚镜像站只是搬运工它不能替你保证内容安全。所以哪怕从镜像下的包也要去官方目录取对应的sha256sums.asc来核对哈希值。哈希对了就说明文件字节级一致跟从哪下的没有关系。4.2 内网缓存的两种做法git bundle 与 rsync公司内网如果有十几个人都要内核源码每人各自去外网拉一遍是纯浪费。比较成熟的两种做法第一种是git bundle。它把仓库打包成一个单文件可以拷来拷去接收方直接clone或fetch# 在已有完整仓库的机器上打包指定分支 git bundle create linux-6.12.bundle linux-6.12.y # 接收方 git clone linux-6.12.bundle linux这个方式的好处是自带完整性校验且增量更新方便——后续只要把新的 bundle 拿过来git fetch一次就能只同步新增提交。第二种是 rsync 做目录镜像。适合 tarball 分发也适合把整个发布目录挂在内网。要注意的是 rsync 默认不做内容校验只比大小和时间戳如果想强制校验内容加-c参数代价是会慢很多因为每个文件都要算校验和。4.3 镜像拉来的包怎么自证干净流程其实和 2.3 节一样只是多了一层心理准备镜像拉的内容你没法 100% 信任来源所以哈希核对必须做。如果连官方哈希都取不到比如完全隔离的环境退而求其次的办法是至少拿两个不同来源的同一文件比对哈希两个独立镜像给出了相同哈希可信度就高很多。这不算严格的安全保证但在离线场景下是常见的折中。解压后还有一个低成本的自检动作make kernelversion会打印出源码树对应的版本号跟文件名对一下。再grep -r Linux version init/version.c之类的抽查一眼基本就能确认这份源码没被替换成别的东西。5. 发行版源码包与嵌入式 BSP厂商那一份往往才是你真正要的前面说的都是主线源码但实际工作中你更多时候面对的是一份被改过的内核。发行版和芯片厂商都会在主线基础上打大量补丁这类源码的获取方式和主线完全不同。5.1 Debian/Ubuntudeb-src 与 apt source这两个发行版的源码通过deb-src源分发。默认的镜像配置里通常没有启用源码源需要手动加上带deb-src的行然后apt-get update。之后apt-get source linux它会在当前目录下拉出源码包并自动解开得到的东西比一个 tar 包丰富得多源码树 debian/目录包含构建规则、config、全部补丁 打包元数据。想指定版本加版本号想跳过解压只下载用--download-only。Ubuntu 还有个额外渠道它的内核开发在 Launchpad 上有公开仓库按系列分了很多分支适合需要跟上游 Ubuntu 内核补丁的人。这条路径拿到的是Ubuntu 内核开发树比apt source更贴近开发过程。5.2 RHEL/CentOS/openEulersrc.rpm 的取法与拆包RPM 系走的是源码包路线需要先启用 source 类型的仓库然后用dnf download --source kernel # 或老一点的写法 yumdownloader --source kernel拿到的.src.rpm里同时包含原始 tarball 和一堆补丁文件。解开它不需要安装直接rpm2cpio kernel-*.src.rpm | cpio -idmv解开后你会看到.tar.xz上游原始代码、几十上百个.patch、.config以及一个.spec文件。要完整还原这个内核得按 spec 里的顺序把补丁一个个打上去不是解压 tarball 就完事。很多人在这里踩坑直接拿 tarball 编译行为跟线上内核完全不一样调试结论自然也不对。openEuler 等发行版思路类似有些也把内核源码直接放在公开代码托管平台上按分支拉更省事。5.3 嵌入式场景BSP、Yocto 与 Buildroot 的源码缓存嵌入式这块芯片厂商给的内核是唯一正确答案。你的 BSP SDK 里通常有一条获取源码的脚本跑完会在指定目录下生成内核树。别嫌它版本老那份代码里包含时钟、电源、引脚复用这些跟具体芯片强绑定的改动主线内核基本不可能直接跑起来。用 Yocto 构建的话内核源码由 recipe 管理首次构建时会下载到DL_DIR缓存目录而且能配置成从内网镜像取。想看某个 recipe 的实际源码位置用bitbake -e virtual/kernel | grep -i ^S bitbake -c devshell virtual/kernelBuildroot 更直白源码在output/build/linux-xxx/下make linux-source可以把源码准备到指定位置。这套缓存机制的价值在于多台机器共享一个DL_DIR就不会反复从外网拉几十个包。另外 Android 生态的内核源码走的是公开代码托管的 common 分支按android12-5.10这类分支名拉取。5.4 拿到源码先做三件事不管是 dex/rpm 还是 BSP拿到源码后的第一件事不是编译而是先看这三点第一看内核版本字符串。发行版内核的版本通常带后缀uname -r输出什么源码里的Makefile就应该是那个基线EXTRAVERSION里往往还藏着补丁级。第二看 config。发行版的 config 在源码包里是现成的拿它去对比make defconfig生成的结果差异条目往往就是你排查问题的关键——比如某个驱动是模块还是内建、某个调试选项开没开。第三看补丁集。debian/patches或者解开的 patch 列表里藏着大量跟你的问题直接相关的改动。客户报一个网卡随机断连主线代码里可能看不出端倪但在发行版的某个补丁里就有答案。6. 下载与解压环节的故障从磁盘写满到签名对不上这一节把我这些年真实遇到过的失败场景整理一遍都是能复现、能定位的。6.1 空间和 inode 要先看两眼克隆或解压中途报错第一反应别去看代码先看磁盘df -h . df -i . du -sh .gitdf -h看块空间df -i看 inode。内核源码文件数量极大如果分区 inode 总量是按小文件场景配的很容易出现空间还有大半但 inode 已满。这种情况在容器或者小容量云主机上特别常见。解决办法要么换分区要么重建文件系统时调大 inode 比例要么改用浅克隆减小规模。另一个隐性问题是/tmp被写满。git clone和tar在处理大文件时都可能用临时目录如果TMPDIR指向一个只有几 G 的 tmpfs大包解压到一半就炸。临时改一下TMPDIR环境变量到数据盘即可。6.2 GPG 校验失败的排查顺序签名校验报错有好几种处理方式不一样按这个顺序查先看错误信息是不是No public key。是的话导入公钥注意内核有多个签名者主版本由 Linus 签稳定版由稳定分支维护者签你需要哪一把导哪一把。再看是不是BAD signature。这基本意味着文件真的被改过或者下载不完整直接重下别尝试修复。如果报的是signature expired或者密钥已撤销说明发行方更新了密钥。这时候去官方目录重新取公钥文件再导入同时可以考虑清掉本地过期的旧密钥副本。还有一种很隐蔽的情况你的系统时间不对。GPG 会校验签名时间是否落在密钥有效期内如果机器时间被设成了错误的年份会出现签名有效期内的误判。date看一眼顺手校时。6.3 clone 中途断掉仓库怎么救网络断掉后git clone失败目录里会留一个半成品。如果只是浅克隆最简单的做法是删掉重来因为重新克隆的成本不高。但如果已经下了几个 G可以尝试在残留目录里续git fetch --depth 1 origin branch git checkout FETCH_HEAD如果 git 报对象损坏先git fsck --full看是哪一类问题多数情况下缺的是 pack 索引可以git index-pack重建或者直接删掉.git里不完整的 pack 再 fetch。经验上浅克隆的恢复成功率明显高于完整克隆因为涉及的中间状态少。还有一种情况是 clone 成功但 checkout 阶段报unable to create file这通常是文件名长度超出文件系统限制或者路径里撞上了保留字在把源码树放在某些网络挂载上时会出现。换个本地文件系统就正常了。6.4 解压报错与文件名乱码的成因解压时最常见的两类报错xz: (stdin): Unexpected end of input和tar: Unexpected EOF in archive。这两个都指向同一件事——文件不完整几乎百分百是下载被截断或者多线程下载写坏了。回去核对大小和哈希不用浪费时间调其他参数。另一类报错是xz: Cannot exec或者格式不认识多半是你的 xz 版本太老不支持新版本内核用的压缩等级或者 zstd 格式。内核近年的 tarball 有.tar.xz也有.tar.gz如果本地工具链老旧选.gz版本兼容性最好代价是文件大一倍多。至于文件名乱码内核源码主干基本都是 ASCII 文件名不太会遇到。但如果你在源码包里掺了中文命名的文档、脚本或者在 Windows 和解压工具之间来回倒腾就会出现乱码。根因是编码不一致文件系统、tar 记录、终端 locale 三者中有一方用了 GBK另一方按 UTF-8 解释。排查方法很简单locale看输出里LC_ALL、LANG是不是C或者空值。临时方案是LANGC.UTF-8再解压如果归档里的名字本身就是 GBKtar 支持转码tar --iconvGBK,UTF-8 -xf archive.tar处理 zip 归档时工具更杂unzip -O cp936是常见的指定编码做法但不是所有版本都支持这个参数遇到不支持的版本只能换工具或者改 locale。最后分享一个小习惯每次下载完内核源码我会在目录里放一个SOURCE_INFO.txt写清来源 URL、下载日期、校验结果和获取方式tarball / git / 发行版包。半年后回头查这份源码到底从哪来的这个文件比翻聊天记录快得多。