尧图精选

OpenEuler内核编译实战:从源码配置到引导替换全流程避坑指南

🕒 发布时间:2026/9/15 21:27:40 📁 来源:尧图网络
1. 环境准备与源码获取1.1 为什么需要自己编译OpenEuler内核很多刚接触OpenEuler的朋友会问系统自带的官方内核用得好好的为什么非要自己重新编译一遍我最早也有这个疑惑直到我自己在x86服务器上遇到一次硬件驱动无法识别的问题官方内核迟迟没有合入对应补丁最后只能自己动手编内核从那以后才意识到这项技能在生产环境中的分量。自己编译内核通常围绕下面几种场景需要开启某些官方默认关闭的配置项比如特定文件系统、调度器或网络功能模块。需要适配特定硬件平台像是ARM架构的RK3588开发板或者某款不常见的网卡芯片。需要为内核打上自己的补丁比如修改TCP拥塞控制算法、调整内存管理策略、加入自己的内核模块。需要排查线上问题通过开启内核调试选项拿到更多日志信息。出于学习目的搞懂内核从源码到二进制镜像的整个构建链路。这里要特别说明一个常见误区很多人以为编译内核就是把源码下载下来敲个make就完事了。实际生产中内核编译的结果不只是一个vmlinuz文件它还包括大量内核模块、头文件、签名信息等。而“替换”环节更是牵涉到grub引导、initramfs生成、模块依赖关系等多个方面任何一个环节掉链子系统都可能起不来。1.2 确认版本与获取源码的方式OpenEuler目前主流的发行版是22.03 LTS系列比如22.03 LTS SP3内核基线版本是5.10。除了LTS版本还有滚动版本比如25.09这种带更新的内核版本。编译内核的第一步就是明确你要在哪个版本的OpenEuler上做替换以及目标内核版本是什么。获取内核源码有两条路线从openEuler官方内核仓库获取带欧拉特性的源码适合要保留openEuler定制增强功能的情况。从kernel.org获取LTS主线源码再用openEuler的config文件做适配适合只需要纯净内核的场景。我个人日常用得最多的是第一种方式。openEuler的kernel仓库托管在gitee上分支命名一般对应发行版本。拉取方式很简单# 以openEuler 22.03 LTS SP3为例 yum install -y git git clone https://gitee.com/openeuler/kernel.git cd kernel git checkout openEuler-22.03-LTS-SP3如果你是在线环境建议直接把源码clone到/usr/src目录下避免后续编译时碰到路径或权限问题。离线环境则需要在有网的机器上先打包源码再拷到目标机器上解压。源码获取后做一件事记录当前运行内核的版本和配置。uname -r # 例如 5.10.0-136.12.0.91.oe2203sp3.x86_64 # 备份当前内核配置后面配置新内核时可以直接基于它修改 cp /boot/config-$(uname -r) /usr/src/kernel-config-backup先备份当前环境这一步虽然简单但真的能让你在后续踩坑时有路可退。1.3 安装编译工具链和依赖包内核编译依赖的工具链比较固定但没有经验的人容易漏装某些包导致编译到一半报错退出。以OpenEuler的dnf包管理器为例我一般会一次性装齐dnf install -y gcc gcc-c make flex bison bc openssl-devel \ elfutils-libelf-devel dwarves python3-devel git ncurses-devel \ diffutils patch binutils perl dracut逐个解释一下这些包的作用方便你按需剪裁gcc、gcc-c、make编译器的核心三件套不用多说。flex、bison用于处理内核源码中的词法分析和语法分析文件没有它们会报一堆parser错误。bc内核编译过程中某些配置脚本和计算逻辑要用到它缺少的话可能在生成头文件时报错。openssl-devel新版本内核在生成签名和某些密钥相关代码时需要OpenSSL头文件。elfutils-libelf-devel编译模块时需要处理ELF格式文件缺少这个包会报缺少libelf相关的头文件。dwarves生成BTF信息时依赖的工具如果开启CONFIG_DEBUG_INFO_BTF很多新内核默认开启就必须安装它。ncurses-develmake menuconfig图形化配置界面需要用到。dracut后面生成initramfs镜像时用的工具OpenEuler默认就是dracut体系。这里我特别想强调dwarves这个包很多教程都没提。新版本内核如果你开启了BTF选项但没有安装pahole工具编译时会在生成vmlinux阶段直接报错错误信息大概是“BTF: .tmp_vmlinux.btf: pahole (pahole) is not available”相当让人抓狂。安装完毕后用下面的命令确认工具链版本正常gcc --version make --version flex --version bison --version pahole --version2. 内核配置的核心思路与实操要点2.1 配置内核的基本逻辑不是从零开始而是继承演进拿到源码后第一件要决定的事情就是内核配置。很多新手喜欢用make defconfig从头开始配这是一个很大的坑。defconfig是针对通用x86平台生成的默认配置它和OpenEuler系统自带的发行版配置差别巨大。如果你直接基于defconfig编译编出来的内核可能缺少openEuler默认开启的很多驱动和功能替换后系统连根文件系统都挂不上。正确的做法是基于当前运行内核的配置做增量修改。前面我在/usr/src下备份的config-xxx文件就是为这一步准备的。cd /usr/src/kernel # 将备份的配置放到源码目录下 cp /usr/src/kernel-config-backup .config # 基于旧配置更新新版本的配置项 make olddefconfigmake olddefconfig的作用是读取现有的.config文件对新增的配置项自动选择默认值。这样既保住了原有配置又能让新版本内核的配置项有合理的初始值。如果后续想在文本模式下手工修改配置项可以执行make menuconfigmenuconfig依赖ncurses库界面操作起来和图形界面类似支持搜索功能。按/键输入关键字可以快速定位配置项这个功能非常实用尤其是配置项名字记不全的时候。2.2 需要重点关注的配置项内核配置项有几千个实际需要人工改动的通常只有几十个。下面这几个是我在OpenEuler环境上折腾时最常碰到的按重要性排序配置项作用建议值说明CONFIG_LOCALVERSION自定义内核版本后缀建议自定义不设置的话新内核和旧内核的版本号容易混淆CONFIG_MODULES支持动态加载模块必须开启关闭后所有驱动都得编进内核镜像会非常大CONFIG_IKCONFIG将配置文件嵌入内核建议开启方便后续在/boot下查看实际编译所用配置CONFIG_DEBUG_INFO编译调试信息生产环境关闭调试环境开启开启后编译产物体积会膨胀数倍CONFIG_DEBUG_INFO_BTFBTF调试信息建议保持默认部分追踪工具依赖BTF关闭前先确认需求CONFIG_SYSTEM_TRUSTED_KEYS内核签名密钥置空不置空的话用自签名证书的机器可能加载模块失败CONFIG_SYSTEM_TRUSTED_KEYS这个坑我踩过两次。openEuler默认配置里这个选项可能指定了发行版签名证书的路径但你的环境里不一定有这个证书文件编译时就会报错说找不到key文件。解决办法是在menuconfig里把对应的字符串清空或者直接在.config里搜索修改# 生成配置后手工替换为空字符串 sed -i s/CONFIG_SYSTEM_TRUSTED_KEYS.*/CONFIG_SYSTEM_TRUSTED_KEYS/ .config如果要在menuconfig里找路径是Cryptographic API - Certificates for signature checking - Additional X.509 keys for default system keyring把默认值删掉即可。2.3 模块与内置的选择策略内核驱动有两种编译方式一种是编译成模块m一种是直接编译进内核y。很多人不理解两者的区别我打一个生活化的比方模块模式像是家里备着的工具箱要用的时候拿出来不用就放着内置模式像是装修时候直接焊在墙上的柜子永远在那儿但占地方。生产环境上推荐的原则是根文件系统驱动、磁盘控制器驱动、网卡驱动尽量编进内核避免initramfs阶段模块加载失败导致系统无法启动其他无关紧要的驱动编译成模块即可。具体操作就是用menuconfig找到对应的驱动选项按Y或M切换。这里要提醒一个细节如果你修改了磁盘控制器相关的驱动编译方式替换新内核后第一次引导一定要确认initramfs里包含了对应模块。否则内核起来后找不到硬盘会直接掉进dracut的紧急shell。3. 编译流程与内核镜像生成3.1 并行编译与资源分配配置完成后正式进入编译环节。内核编译是个体力活耗时的长短主要取决于CPU核心数、内存大小和配置项多少。以一台16核32G内存的服务器为例编译一个常规配置的内核大概需要20到40分钟如果开满调试选项时间可能翻到两小时以上。编译命令很简单但参数有讲究# -j参数指定并行任务数一般设置为CPU物理核心数的1.5到2倍 make -j$(nproc)nproc命令返回的是逻辑CPU数量。如果你的是超线程机器逻辑核可能是物理核的两倍。我一般建议用物理核数加一两倍的数值因为内核编译期间会有大量I/O等待适当超配能压榨更多性能但也不建议盲目用两倍以上否则容易内存不足。编译过程中可以通过top命令观察CPU占用情况。如果发现内存吃紧可以把并行数降一档。内存不足时gcc进程会被OOM killer杀掉报错信息通常是“internal compiler error: Killed (program cc1)”非常误导人实际上是内存不够。编译完成后确认产物# 出现vmlinux和System.map即表示内核主体编译成功 ls -lh vmlinux ls -lh System.mapvmlinux是未压缩的内核映像几个GB大小很正常。真正引导用的是arch/x86/boot/bzImagex86平台编译完成之前你对vmlinux的操作都不要停。3.2 编译模块与安装模块内核主体编译完成后接着要编译并安装内核模块。模块数量少则几百个多则上千个同样需要一段时间。# 编译模块 make modules # 安装模块到/lib/modules/$(uname -r)对应目录 make modules_install执行make modules_install后模块会安装到/lib/modules/5.10.0-xxx.oe2203sp3.x86_64/这样的目录下。这个目录名和内核版本号严格对应后续depmod、dracut生成initramfs时都会依赖这个对应关系。模块安装完成后建议马上用depmod生成模块依赖关系depmod -adepmod的作用是读取/lib/modules下的所有模块生成modules.dep和modules.alias等文件。不执行depmod系统在modprobe加载模块时会提示找不到模块或依赖关系异常。3.3 安装内核镜像并生成initramfs这一步是整个“替换”流程中最关键的一环。# 安装内核镜像到/boot目录 make installmake install会做几件事将bzImage复制为/boot/vmlinuz-版本号将System.map复制为/boot/System.map-版本号将.config复制为/boot/config-版本号并且自动更新grub配置。但这里有个隐患make install在生成initramfs时用的是系统默认的mkinitrd或dracut逻辑。有时候因为配置差异或模块依赖问题生成的initramfs里缺少必要的驱动模块。为了确保万无一失我习惯手动再刷一遍initramfs# 先通过uname -r或安装日志确认新内核版本号 # 假设新内核版本为 5.10.0-xxx.oe2203sp3.x86_64 dracut -f /boot/initramfs-5.10.0-xxx.oe2203sp3.x86_64.img 5.10.0-xxx.oe2203sp3.x86_64dracut的-f参数表示强制覆盖同名文件。如果/boot分区空间紧张生成initramfs前先看看还剩下多少空间df -h /bootinitramfs默认生成大小通常在30M到80M之间如果开启了DEBUG_INFO相关选项可能膨胀到几百兆。空间不足时要么清理旧内核文件要么精简dracut配置。3.4 确认grub引导项并设定默认启动内核OpenEuler的引导方式默认是grub2。make install之后grub2一般会自动增加新内核的启动项。但为了稳妥需要手动重新生成grub配置并确认启动顺序。# 重新生成grub.cfg grub2-mkconfig -o /boot/grub2/grub.cfg # 查看当前所有可用的启动内核 grubby --infoALL | grep -E ^kernel|^index # 设置新内核为默认启动项 grubby --set-default/boot/vmlinuz-5.10.0-xxx.oe2203sp3.x86_64grubby是Red Hat系发行版自带的引导管理工具操作起来比直接改grub.cfg安全得多。设定完成后用grubby --default-kernel确认默认内核是不是你想替换的那个。这里有个容易忽略的细节UEFI引导和Legacy BIOS引导的grub.cfg路径不一样。UEFI模式下是/boot/efi/EFI/openEuler/grub.cfggrub2-mkconfig默认输出路径通常能自动识别但在双系统或特殊分区布局下可能要手动指定-o参数。执行完grub2-mkconfig后最好检查一下输出文件顶部的注释时间是不是最新。一切就绪后重启验证reboot重启后第一时间执行uname -r确认当前运行的内核确实是刚编译替换的新内核。4. 常见问题与排查技巧实录4.1 编译阶段的高频报错编译过程中的报错类型五花八门但归纳下来无外乎几类。我在OpenEuler上实际遇到的问题和解决思路如下报错现象根本原因解决办法BTF: .tmp_vmlinux.btf: pahole is not available缺少dwarves/pahole工具dnf install -y dwarvesopenssl/xxx.h: No such file or directory缺少OpenSSL开发头文件dnf install -y openssl-develflex: command not found缺少词法分析工具dnf install -y flexbison: command not found缺少语法分析工具dnf install -y bisoninternal compiler error: Killed编译内存不足降低-j并行数或增加交换分区No rule to make target xxx源码目录不干净或依赖缺失make mrproper后重新配置CERT: unsigned certificate配置中指定了不存在的证书清空CONFIG_SYSTEM_TRUSTED_KEYS这里面最阴间的其实是“No rule to make target”这类报错。有一次我在旧版本源码目录上直接切换分支没有清理之前的编译产物导致一堆残留的目标文件和新源码混合在一起编译报错完全找不到规律。后来一查是目录下的编译中间产物污染了。解决办法是执行make mrproper彻底清理再重新配置。4.2 替换内核后无法启动的急救方案内核替换最大的风险就是重启后系统起不来。我自己的经验是与其等到起不来再手忙脚乱不如在重启之前就做好最坏的打算。首先要保证grub菜单里还保留着旧内核的启动项。正常情况下grub2-mkconfig会自动扫描/boot下所有vmlinuz-*文件旧内核不会被删掉。万一旧启动项丢了启动时按e编辑grub启动参数手动指定旧内核的vmlinuz和initramfs路径也能引导进系统。如果系统直接卡在启动早期阶段连grub菜单都进不去我一般会做这么几步在grub菜单界面按e找到linux开头的那一行在末尾加上init/bin/bash或rd.break绕过systemd直接进shell。挂载根文件系统为可读写检查/boot目录下的文件是否完整。检查grub.cfg中指定的vmlinuz、initramfs路径是否存在。如果怀疑initramfs有问题用系统盘或旧内核引导后重新dracut。这里分享一个我个人的保留技巧编译新内核前把当前正在运行的内核镜像和initramfs先额外备份一份到/root目录cp /boot/vmlinuz-$(uname -r) /root/ cp /boot/initramfs-$(uname -r).img /root/这样即使/boot目录被意外清空或覆盖也能用备份文件手动恢复引导。4.3 模块加载失败与文件系统挂载异常新内核起来后有时候会遇到网络不通或某个设备不识别的情况。大部分原因都是模块没有正确加载。排查步骤我总结为三板斧# 第一板斧查看系统日志里和模块相关的报错 dmesg | grep -i module dmesg | grep -i fail # 第二板斧确认模块文件是否存在以及依赖关系是否正常 find /lib/modules/$(uname -r) -name *.ko* | grep 网卡驱动关键字 modinfo 网卡驱动名 # 第三板斧手动加载测试 modprobe 网卡驱动名还有一种情况是根文件系统挂载失败。比如你新配置里把ext4或xfs的驱动从built-in改成了module而initramfs里恰好没有包含对应的模块内核启动后就会因为找不到根文件系统而panic。解决方法是重新用dracut生成initramfs确保文件系统驱动被包含进去# 将文件系统驱动显式加入initramfs dracut --add-drivers ext4 xfs -f /boot/initramfs-xxx.img xxx4.4 内核模块开发与动态挂载file_operations挂钩的合法场景标题里的热词中有关于file_operations拦截read/write的内容我在这里顺带说一个相关的正规开发场景。很多做内核功能增强的朋友会写一个内核模块LKM通过替换file_operations结构体里的read或write函数指针来实现文件操作的审计、监控或者加密逻辑。基本套路是通过内核符号表找到目标文件系统或目标设备的file_operations结构体。保存原有的read/write函数指针。替换成自己实现的函数在处理完自定义逻辑后再调用原函数。这类做法的正统应用场景包括文件审计、数据防泄漏、透明加密、I/O行为监控等。内核模块开发的细节以后找机会单独展开。这里只提醒两点第一内核版本升级后很多内部接口会变化模块代码需要跟着适配第二新内核默认开启了内核锁定和模块签名校验自编译模块如果不签名可能会被拒绝加载需要在配置阶段把CONFIG_MODULE_SIG关闭或用自制密钥签名。针对OpenEuler环境如果你需要频繁加载自研模块做测试建议在编译配置时关闭模块签名校验或者开启CONFIG_MODULE_SIG_FORCE后统一用自签名证书签发模块。否则每次insmod都会报“Required key not available”的错非常耽误事。5. 我的一点实操体会内核编译这件事第一次做觉得高大上第二次觉得繁琐第三次开始理解门道。我在OpenEuler上编内核最有价值的一次经历是帮助同事在国产化平台上适配一款特殊加密卡驱动。官方内核没有包含对应的驱动源码我们基于硬件厂商提供的补丁重新编译内核后整机性能测试才顺利跑通。那次之后我真正意识到编译内核不是炫技而是一项兜底能力。当上游内核、发行版内核都满足不了需求时自己动手是最直接的路径。最后再分享两个实战小技巧。第一个编译内核前先用etckeeper或类似工具备份/etc目录尤其是grub配置相关文件。因为内核替换过程中可能会安装或更新一些软件包间接改动系统配置。第二个编译完成后不要急着删旧内核包保留最近两到三个版本的vmlinuz和initramfs一般生产环境建议在确认新内核稳定运行一周以上再做清理。有了这两个习惯绝大多数内核替换的坑你都能从容应对。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →