ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux内核 defconfig 与 .config 配置原理及不生效排查

Linux内核 defconfig 与 .config 配置原理及不生效排查 1. 一次配置改动没生效的现场复盘前阵子帮人看一个嵌入式项目对方的问题描述很典型明明在.config里把某个触摸屏驱动改成了y重新编译、烧写板子起来驱动还是没加载。grep 了一遍.config确认是CONFIG_TOUCHSCREEN_XXXymake menuconfig里那一项也是打上勾的看起来毫无破绽。折腾了一下午最后问题出在一个最不起眼的地方——他们用的是自己维护的xxx_defconfig而每次编译脚本的第一步是make ARCHarm64 xxx_defconfig这个动作会把.config整个重新生成一遍之前手改的内容全部被覆盖回源码仓库里那份干净的 defconfig。这个坑几乎每个碰 Linux 内核的人都会踩一次区别只是踩得早晚。defconfig 和 .config 是内核构建配置体系里最容易混淆的一对东西很多人把它们当成同一个文件的不同叫法实际上它们的角色、生命周期、维护方式完全是两回事。如果你在做嵌入式 Linux、Android 内核、或者只是偶尔需要裁一个内核出来跑在自己板子上这一对概念搞不清楚后面所有关于驱动裁剪、功能开关、模块编译的调试都会变成瞎猜。这篇内容我打算把这件事从根上讲透defconfig 到底是什么定位、.config是怎么被生成出来的、Kconfig 在这中间扮演什么角色、改完配置为什么会出现看起来改了但没生效、以及多产品线怎么用 fragment 管理配置差异。适合已经会敲make menuconfig但没深究过背后机制的人也适合刚接手一个满是 defconfig 的内核仓库、被几十个配置文件搞晕的人。文中涉及命令和目录结构的部分基于主线内核的常见实践不同版本、不同厂商分支会有细节差异我会在关键处标出来。2. defconfig 的真实身份一份配置意图清单2.1 它住在 arch 目录下不是随便放的第一次找 defconfig 的人常常 grep 半天找不到文件。它的固定位置在架构目录下arch/arm64/configs/xxx_defconfig arch/arm/configs/xxx_defconfig arch/x86/configs/x86_64_defconfig arch/riscv/configs/defconfig注意后缀必须是_defconfig这不是命名习惯而是scripts/kconfig里模式规则的一部分。当你敲make ARCHarm64 xxx_defconfig时Makefile 会把目标名匹配到%_defconfig规则上去找arch/arm64/configs/xxx_defconfig这个文件。所以文件名少了后缀、或者放错目录命令会直接报找不到规则而不是文件不存在那种友好提示。目录结构本身也说明了它的定位defconfig 是架构相关的东西。同一份内核源码ARM64 和 x86 的 defconfig 完全不通用因为两者的可用符号集根本不一样。你在arch/arm64/configs/下加的配置项放到arch/x86/configs/下可能连对应 Kconfig 条目都不存在。2.2 内核为什么不愿意直接提交 .config一个很自然的疑问既然最终编译用的是.config直接把它提交进仓库不就完了为什么非要搞一个 defconfig 中间层原因是.config太长、太啰嗦、太不稳定。一份完整的内核.configARM64 上通常有三四千行里面绝大多数是默认值或者没被显式打开的空行。这种东西提交进仓库有几个致命问题第一它对版本极度敏感。内核每个版本都会新增、删除、重命名大量配置符号。你提交一份 5.10 的完整.config升到 5.15 之后里面可能有几百项是无效的编译时满屏 warning还得一项项对。第二它把默认值和我的意图混在一起了。完整.config里CONFIG_FOOy这一行你没法判断是作者主动开的还是因为别的项 select 进来然后跟着默认值走的。后来接手的人想改一个东西看到满屏配置根本不知道哪些是关键的。第三审查成本高。内核社区收 patch 的时候一份几千行的.configdiff 没人看得下去但一份 defconfig 的改动可能就十几行一眼能看出哦你开了这个新驱动。所以 defconfig 的设计思路是只记录与默认状态不同的、我明确关心的那部分配置让绝大部分符号走 Kconfig 里定义的默认值。这样文件短、易读、易审查版本升级时也不会被大量无效行淹没。2.3 没写进 defconfig 的项意味着什么这是最容易误解的一点。defconfig 里没有出现CONFIG_FOO不等于 FOO 是关闭的。真正的含义取决于三件事这个符号的 Kconfig 默认值是什么、它的依赖是否满足、有没有别的符号通过select把它拉起来。如果CONFIG_FOO的 Kconfig 定义里写着default y而且依赖满足那即使 defconfig 里一个字没提最终.config里它也是y。我见过一个很典型的例子某个项目想关掉一个默认开启的调试特性负责人翻遍 defconfig 发现里面根本没提这一项就以为没写就是关的结果一直没生效。正确做法是在 defconfig 里显式写# CONFIG_FOO is not set这个带注释格式的写法在内核配置语里是显式关闭的专用表达和完全不出现是两种不同的语义。提示判断一个符号当前状态不要靠看 defconfig 有没有写要在生成后的.config里 grep。defconfig 描述的是意图.config才是结果。3. 从 Kconfig 到 .config配置是怎么被算出来的3.1 Kconfig 树才是配置项的真正定义处defconfig 和.config都只是值真正定义这些值长什么样、叫什么名字、依赖什么、默认是什么的是遍布源码树的Kconfig文件。每个子目录下基本都有一份Kconfig顶层Kconfig通过source语句把它们串成一棵树。一个最小化的条目长这样config TOUCHSCREEN_XXX tristate XXX touchscreen driver depends on I2C INPUT default n help Say Y here to enable the XXX touchscreen driver.几个关键点类型bool只有开关两态tristate有 y/m/n 三态对应编进内核、编成模块、不编int/string/hex是数值或字符串。depends on依赖表达式不满足时这一项根本不会出现在配置界面里也会被强制成默认值。default默认值可以是常量也可以引用其他符号。select强行把另一个符号拉成 y注意它不检查对方依赖滥用会导致依赖不一致。理解 Kconfig 树是理解一切的起点。.config里的每一行都是这棵树在特定输入下求值的结果。3.2 make xxx_defconfig 到底做了哪几件事这条命令看着简单背后是一条完整的流水线读取arch/arm64/configs/xxx_defconfig解析里面的CONFIG_*赋值和# CONFIG_* is not set行。遍历整棵 Kconfig 树为每个符号计算默认值。把 defconfig 里给出的值覆盖到对应符号上但前提是该符号在当前环境架构、交叉编译器、依赖下可见且可配置。对无法应用的赋值输出 warning比如warning: override: reassigning to symbol FOO或依赖不满足被丢弃。把最终结果写入.config并生成include/config/auto.conf、include/generated/autoconf.h等派生文件。这里有个版本差异必须说清楚。在较早的内核上defconfig 是顺序应用的解析器按文件顺序逐行处理遇到某个符号的依赖还没被满足时这一行就被丢弃并打印 warning。这就导致手写 defconfig 时必须按依赖顺序自底向上排列先写被依赖的再写依赖它的否则就会出现我明明写了但没生效。较新的内核大约 5.8 之后改成了先建立完整默认配置、再整体套用 defconfig 值的做法顺序依赖问题基本消失了。如果你在维护一个老版本厂商内核很多手机、车机、路由器的 BSP 内核版本都偏老这个顺序问题一定要留意它和后面第 7 节的排查链路直接相关。3.3 oldconfig、olddefconfig、savedefconfig 三兄弟的区别这三个目标名字长得像作用完全不同混用会出大事目标读什么写什么遇到新符号怎么办make oldconfig当前.config.config逐项交互式提问make olddefconfig当前.config.config直接取默认值不问make savedefconfig当前.configdefconfig不涉及make defconfig架构默认.config全部走默认值make xxx_defconfig指定 defconfig.config全部走默认值加覆盖日常升级内核版本后的标准动作是make olddefconfig因为新符号用默认值通常是最省事的。但这里有个隐藏风险如果新增符号的默认值是 y而你的代码假设它是 n行为就悄悄变了编译不报错运行时出问题。所以跨大版本升级之后认真 diff 一遍新旧.config是个好习惯尤其是安全相关、内存管理相关的选项。make savedefconfig的作用方向相反它读你当前的.config把其中与默认值相同的项全部剔除只留下真正有差异的部分输出一份精简的 defconfig 文件。这是把手工调整固化回源码仓库的标准手段。4. 手改 .config 还是走 menuconfig4.1 menuconfig 的价值不只是界面友好make menuconfig最被低估的能力是搜索。按/键输入符号名它会告诉你这一项的当前值、依赖表达式、定义在哪个 Kconfig 文件的第几行、以及哪些符号在 select 它。这个功能在排查为什么这一项是灰的不能改时特别有用。大多数时候答案是依赖没满足——比如你想开一个网络驱动但它depends on PCI而你当前的架构配置里 PCI 是关的那这一项在界面里根本不会亮。4.2 手改 .config 的几种典型翻车直接拿编辑器改.config看起来最快但它有几个必须知道的坑第一改了不生效。如果你改的是一个被别的符号 select 的项手改的值会在下一次配置计算时被覆盖回去。Kconfig 里select的语义是强制的它会持续把目标符号钉在某
返回列表