从源码到内核镜像:Linux内核编译全流程详解

发布时间:2026/10/18 23:54:24
从源码到内核镜像:Linux内核编译全流程详解 很多人学Linux都会碰到这一节编译Linux源码。听起来高高在上好像只有内核开发者才需要碰但实际走通一遍之后你会发现这个过程无非就是准备环境、配置选项、编译安装这三板斧而且它对你理解操作系统底层有巨大的帮助。这篇文章我尽量用实际操作经验来写把从源码到能跑起来的内核镜像的完整链路拆开讲清楚包括为什么要自己编译、每一步该怎么操作、哪些地方最容易翻车。适合刚学完系统管理、想往内核方向走一步的同学也适合需要定制内核功能、处理驱动兼容问题的运维或嵌入式开发者参考。1. 为什么需要编译Linux源码——不是装个系统那么简单1.1 内核源码是什么编译它到底在做什么先把这个概念拉回地面编译Linux源码不是让你从零开始写一个操作系统而是把官方发布的内核源代码通过编译器变成一台具体机器能够加载运行的内核镜像。内核源码本质上是一棵庞大的C语言工程树里面有arch、drivers、fs、kernel、mm、net这样一批核心目录分别对应体系结构相关代码、驱动、文件系统、进程调度与内存管理等模块。编译的过程就是让构建系统make去调用编译器把成千上万个源文件逐步转换成一堆目标文件最后链接生成一个可启动的内核二进制。这个二进制文件包含了操作系统最核心的能力进程管理、内存分配、文件系统、网络协议栈、各种设备驱动框架。而我们日常使用的大多数系统跑的是发行版厂商预先编译好的“通用内核”里面塞了非常多的模块和特性方便覆盖各种硬件环境但并不一定适合你的具体需求。自己编译其实就是按照你的选择重新打造一个最适合当前场景的内核。你可以把内核源码编译理解成“做菜”源码是食材编译器是刀具和锅具最终镜像就是端上桌的菜。配置阶段相当于决定放什么调料编译阶段就是开火翻炒。这道菜值不值得自己动手做取决于你是只想吃饱还是想掌控口味。1.2 什么人需要自己编译内核什么时候可以不用很多人会问“系统自带内核用得好好的编译它干嘛”如果你的服务器只是跑常规服务比如Web、数据库、容器那确实没必要直接用发行版维护的内核反而更省心因为安全补丁和兼容性测试都是现成的。但是有几种情况自己编译几乎是绕不开的第一种是硬件驱动需要。比如某款网卡、某款GPU厂商不提供预编译二进制驱动只提供源码包这时候你需要在内核源码的框架下把驱动编译成模块并加载否则硬件无法工作。第二种是性能定制。默认内核功能太多体积大、内存占用也不算低如果你追求极致的启动速度和更小的内存占用可以裁剪掉自己用不到的协议、驱动和特性。第三种是内核开发和学习。你想改一处调度算法、调试一个内核模块必须先编译出能跑的内核才能验证效果。当然如果你的目的只是“升级到新版本内核”那完全没有必要自己编译。用发行版仓库里的内核包或者维护良好的预编译内核源往往更可靠。自己编译的适用边界很清楚需要个性化定制、需要依赖某个特定内核版本的驱动或者你希望真正搞懂内核工作原理。2. 编译前的准备环境、工具链与源码获取2.1 磁盘空间、内存和CPU的要求编译内核是一场资源消耗不小的“重体力活”千万别忽略环境准备。先说磁盘空间内核源码压缩包解压后大概1GB出头编译过程中生成的中间文件、目标文件、模块文件加在一起轻松超过5GB。为了不把自己逼到墙角我建议在任何分区下预留至少10GB空闲磁盘。我习惯把源码放在独立的目录下不会跟系统其他文件抢空间。内存方面如果配置的选项多、并行编译任务数开得高内存很容易吃紧。4GB内存的机器跑完整编译可能会卡到怀疑人生8GB以上比较稳妥。如果你用的是云主机内存不够还容易触发OOM编译器进程被系统杀掉编译直接失败。CPU核心数决定了编译速度核心越多越快但内存也要跟得上。时间上做个参考在8核16GB的机器上用默认配置编译一个主流内核大约需要十到二十分钟如果是老机器半小时到一小时都很正常。所以建议编译前把其他重负载任务停一停给编译进程留出足够资源。2.2 安装必要的编译工具编译内核依赖一套完整的工具链缺一个都可能在某个阶段报错。核心工具包括编译器GCC或Clang、make构建工具、词法分析器flex、语法分析器bison、处理配置脚本的perl、生成文本配置界面的ncurses开发库以及一个经常被忽略的openssl开发包。openssl开发包主要用于处理部分内核特性比如内核模块签名、某些加密算法的头文件依赖缺少它会在配置阶段直接报出找不到头文件的错误。不同发行版的包管理器不一样安装命令也不一样。我这里不限定具体发行版只给包名参考你换成自己发行版对应的包名安装即可# 以通用包管理器安装示例包名随发行版略有差异 sudo 你的包管理器 install gcc make flex bison perl bc sudo 你的包管理器 install libncurses-dev # 有些发行版叫 ncurses-devel sudo 你的包管理器 install libssl-dev # 有些发行版叫 openssl-devel我实测踩过的坑是只装了gcc和make就开搞结果配置到一半提示找不到openssl/xxx.h回头补装后又发现ncurses没有来来回回折腾了好几轮。最稳妥的做法是先一次性装全再开始编译。2.3 获取内核源码的合理方式源码获取方式主要有两种从内核官方网站下载压缩包或者用git克隆源码仓库。对于绝大多数场景我推荐下载压缩包因为它体积小、速度稳定而且版本明确。git克隆完整仓库虽然能随时切换分支、看提交历史但会拉取非常多的历史记录费时费空间如果不是做内核开发没必要。下载完是一个tar.xz或tar.gz压缩包解压命令也很简单tar -xf kernel-xxx.tar.xz cd kernel-xxx解压时有一点要注意目录路径不要包含中文、空格也不要有奇怪的特殊字符否则部分构建脚本会处理出问题。我一般会把源码放到/usr/src/kernel-src这类干净路径下。另外源码目录的属主建议是当前用户而不是root后面编译可以用普通用户执行只有安装那几步才需要提权这样更安全。3. 内核配置整个编译过程最容易踩坑的环节3.1 配置方式怎么选默认配置、本地配置还是完全自定义源码拿到手之后千万别急着敲make第一件正事是生成配置文件.config。内核源码里有成千上万个配置项每个配置项决定某个功能是编译进内核、编译成模块还是彻底不编译。这个文件直接决定了最终内核长什么样。最省心、成功率最高的方式是复用当前系统内核的配置。多数发行版会把内核配置放在/boot目录下文件名类似config-xxxx。你可以复制一份到源码目录并命名为.config然后执行make oldconfig它会自动对比新旧版本的内核配置把新出现的选项以提问方式交给你决定。这种方式能确保你的新内核和旧系统兼容性最好我推荐新手第一次就这么干。如果你想要可视化地浏览和修改配置可以运行make menuconfig它会打开一个文本交互界面可以搜索、选择、切换各项参数。这种方式对新手非常友好因为依赖关系、帮助信息都展示得比较清楚。如果你完全清楚自己的需求也可以直接写配置文件但我不建议手动维护因为内核选项之间的依赖关系太复杂了漏一个可能就导致启动失败。3.2 常用配置项与模块化概念在menuconfig界面里你会看到大量选项前有*、M、 这样的标记。*代表编译进内核M代表编译成独立模块空白代表不编译。模块化是内核里一个非常重要的设计内核镜像本身保持小巧驱动和可选功能以ko文件形式放在/lib/modules/内核版本/目录下系统启动或需要时再动态加载。这样既节省内存又便于维护。配置时我有一条原则如果不确定一个功能是否需要优先选择编译成模块M而不是直接取消N。因为模块加载失败最多是某个功能不可用不会导致系统起不来但如果你把启动必需的驱动设置成N比如根文件系统所在的存储控制器驱动那系统开机直接卡死连错误提示都很难看到。在menuconfig里按/键可以搜索配置项比如输入CONFIG_MODULES就能快速定位到模块支持相关选项。模块支持本身是必须开启的通常选中为*否则后面make modules_install无意义。还需要注意一些常见的安全和调度配置不要随便关闭宁可多保留一些默认值也比你摸索着裁掉要稳。3.3 保存配置与增量修改配置完成后.config就是你的财富。我强烈建议在每次修改前都备份一份。你运行make menuconfig改来改去很有可能把某个关键依赖整没了。如果配置已经乱了可以直接恢复备份。此外make savedefconfig可以把当前的完整配置精简成一个最小化的defconfig文件很适合以后做模板。配置完如果立刻开始编译也不用做什么额外保存.config已经存在源码根目录了。但要注意如果你把源码目录清理了或者用make clean清理中间文件配置会一并被清掉吗其实make clean不会删除.config但make distclean会。所以命令要看清楚别手滑。升级到新版内核时直接拿旧版.config跑make oldconfig是最快的但我建议跑完之后再用menuconfig过一遍看看新增选项是否被默认值设置成意外项。尤其是跟安全相关的配置默认值不一定符合你的预期。4. 正式编译从make到安装4.1 编译参数的选择与多线程加速配置完成以后就可以进入真正的编译阶段。简单粗暴地敲make虽然也能跑但速度太慢了实际使用中我们都会用并行参数加速。最常用的做法是make -j$(nproc)其中$(nproc)返回CPU核心数。核心数决定并行度但内存也决定了能不能顶得住。如果你内存只有4GB即使CPU有8核我也不建议直接-j8。每个并行编译任务都会占用内存任务数超过内存承载能力编译进程会被系统杀掉出现类似Killed的提示。这种情况下老老实实make -j2或-j4反而更稳。我自己的习惯是8核16GB机器上用make -j8编译速度和稳定性都还可以。编译过程会输出大量信息看得人眼花缭乱。我一般会把输出重定向到日志文件make -j$(nproc) build.log 21这样出错后只需要查看日志尾部就能定位失败原因。中途如果因为异常中断不要急着从头再来内核构建系统支持增量编译只要源码没改动重新执行make会从上次失败点附近继续。但如果源码改动比较大或者配置变了建议先make clean再重新编译避免残留文件导致诡异问题。4.2 模块编译与安装编译完成后第一件要安装的是内核模块。命令是sudo make modules_install这个命令会把所有配置为M的模块安装到/lib/modules/内核版本/目录下。我看到很多人执行这条命令时忘了加sudo结果因为没有写权限只能报错。模块安装是写系统目录的操作必须用root权限。为什么模块必须安装因为如果你的内核里有很多功能是模块化的比如网卡驱动、文件系统驱动不装模块直接启动新内核很可能出现开机后网卡不识别、磁盘阵列无法挂载之类的问题。即使你觉得都是内置功能也建议把模块装上以备不时之需。安装完成后可以检查一下目录是否存在、大小是否正常。有一点经验如果你的机器之前装过其他内核模块modules_install不会覆盖旧内核目录会新建当前版本的新目录所以不同内核的模块可以共存不用太担心互相干扰。4.3 安装内核与更新引导模块装完之后安装内核镜像本身。一般执行sudo make install这个命令会把编译好的内核镜像复制到/boot目录下并自动尝试更新引导配置。但不同系统的引导程序不太一样make install之后引导配置未必能自动刷新所以还需要手动执行一下引导更新工具。不同发行版命令不一样常见的有update-grub或者grub2-mkconfig -o ...这类形式你根据自己的发行版来。这里有一条保命建议千万不要在安装完新内核后就立刻删除旧内核。新内核第一次启动时谁也没法保证百分百正常可能因为配置问题、驱动问题、initramfs问题导致进不了系统。这时候唯一的退路就是启动菜单里选择旧内核。等到新内核正常运行一段时间、确认无问题后再清理旧内核也不迟。另外如果你使用了LVM、加密分区、软件RAID这类复杂存储方案更新了内核后还需要重新生成initramfs这个文件负责在挂载根文件系统之前加载必要的驱动。不同发行版的生成工具不一样但本质都是基于当前内核模块目录重新打包。我记得第一次编译时忽略了这一步结果新内核启动后一直卡在挂载根文件系统的阶段教训很深刻。4.4 验证新内核是否生效安装完、重启后第一件事就是确认当前实际运行的内核是不是你编译的那个。命令很简单uname -r输出的版本号要和你编译的版本匹配。如果版本还是旧的说明引导程序没有选择新内核或者启动项没有正确更新。你可以回到引导菜单手动选择新内核项启动。验证模块是否齐全也有一个小技巧find /lib/modules/$(uname -r) -name *.ko | wc -l这个数字能帮你快速判断模块安装是否完整。如果数字很少说明配置阶段大部分模块都被裁掉了或者modules_install没有正确执行。还有一点如果你修改了内核配置后重新编译但忘了执行modules_install重启后系统会去加载旧版本的模块很可能出现版本不匹配导致加载失败。5. 常见问题与排查技巧实录5.1 编译中断、报错日志怎么看编译过程中最常见的错误就是缺少依赖包或头文件。比如报错fatal error: openssl/xxx.h: No such file or directory那基本就是缺openssl开发包补装对应包然后重新跑make即可。内核编译有增量机制错误修复之后从头跑也不会浪费太多时间。遇到错误信息很长的时候不要从开头一行行看直接看日志尾部。我通常是tail -n 100 build.log然后在输出里往上面找第一个error:出现的位置那个地方上下几行往往才是真正原因。有时候一个头文件缺失会引发连锁错误真正的根源就藏在最早的那个error里。5.2 磁盘空间不足的处理编译到一半提示No space left on device是最让人无奈的。这时候你需要清理临时文件。运行make clean可以清除大部分编译中间产物和对象文件但要注意它不会删除.config除非你用的是make distclean。清理完重新编译会从头开始时间成本比较高。如果你想保留配置建议先把.config复制到源码目录之外的地方存一份再执行make clean之后复制回来。当然最好的办法其实是提前留足空间。我在前面的准备章节已经强调过源码所在分区最好至少预留10GB。如果根分区紧张可以把源码放到空间充足的独立分区软链接过去也行。5.3 内核模块加载失败模块装好了但用modprobe xxx加载时报错Module xxx not found这种情况很常见。先确认当前运行的到底是哪个内核版本用uname -r看。模块是有严格的内核版本匹配要求的如果你在旧内核运行状态下却去找新内核的模块目录自然找不到。还有一种情况是模块编译出来了但安装的目录名和当前内核版本不一致。比如你改了内核版本字符串导致模块安装到了/lib/modules/新版本/而当前运行的还是旧内核。解决方案是重启到新内核或者先把模块复制到正确目录再depmod -a更新模块依赖关系。我通常习惯修改内核版本后第一时间在模块目录确认路径是否对。5.4 引导配置丢失后的修复思路如果你编译完新内核重启后卡在引导界面或者直接进入了一个带提示符的紧急模式那大概率是启动阶段出了问题。这时候还能用启动菜单中的旧内核进入系统先别慌。进入系统后第一件事检查新内核的根文件系统相关配置尤其是存储控制器的驱动。如果你的根文件系统所在的设备驱动被编译成了N那内核根本不认识你的硬盘自然无法挂载根分区。把对应驱动改成Y或者M并确保initramfs重新生成。另一个容易忽略的是用户态工具。如果你用了LVM、加密分区、RAID除了内核驱动还需要对应的用户态工具和initramfs中包含这些工具。运行一下对应的initramfs重建命令然后再更新引导。修好后先别急着删除旧内核至少要让新内核连续启动成功几次再考虑清理。6. 一些实测心得和值得尝试的扩展6.1 我第一次编译时踩过的坑我第一次编译内核时以为直接跑make就能成功结果磕磕绊绊折腾了一整天。第一个错误是直接用发行版默认配置等于把几百个驱动模块全编译了一遍编译时间长得离谱。后来我学会了先复制当前系统的config再走oldconfig速度和成功率都提升了一大截。第二个错误是编译完以后没更新引导配置重启一看还是旧内核当时一度怀疑自己哪一步做错了。现在回头看新手容易忽略的就是“配置”环节。不要追求一次就编译出最精简的内核而是先求“能成功”。先复现一个和自己当前系统几乎一样的内核然后再慢慢加选项、减选项。只要学会了完整链路后面再怎么改都有底气。6.2 从编译内核到定制内核的进阶方向当你把基础流程走通以后可以玩一些更有意思的东西。比如用make localmodconfig它只把当前系统已经在用的模块编译出来其他全部关掉这样生成的内核体积小、启动快非常适合做嵌入式系统或者实验环境。还有可以修改版本字符串给自己编译的内核打上独特标记方便之后区分。甚至可以尝试给内核打补丁比如某些第三方驱动补丁编译安装后就能在系统里用上新硬件的功能。如果再进一步还可以试试clang编译内核可能发现工具链不同带来的编译差异也可以用源码树自带的各种工具比如检查配置依赖、分析符号表加深对内核构建系统的理解。这些扩展玩法都建立在一个基本能力之上你能熟练完成配置、编译、安装、回退这一整套流程。我个人在实际操作中的体会是编译Linux源码最大的收获并不是得到一个多么精简、多么强力的内核而是打破了那种“操作系统是不可触碰的黑盒”的畏难感。当你明白内核本身只是一个可以替换的软件组件许多系统层面的“玄学问题”都变得可以解释、可以验证、可以解决。希望这篇文章能帮你把那根“隔阂”的线剪断顺利编译出属于自己的第一个内核。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询