一,kconfig在linux驱动不可或缺,
二,让我们一起来了解一下
- - - ,以下是官方文档翻译。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Kconfig 文档集合</title> <!-- 引入字体图标库 --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> <script src="A.插件_字体缩放.js"></script> <style> :root { --primary-color: #007bff; --secondary-color: #6c757d; --accent-color: #17a2b8; --text-color: #343a40; --text-secondary: #6c757d; --bg-color: #f8f9fa; --border-color: #dee2e6; --hover-bg: #e9ecef; --code-bg: #282c34; --code-color: #abb2bf; --tab-active-bg: #fff; --tab-active-border: var(--primary-color); } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif; font-size: 16px; /* Base font size */ line-height: 1.7; color: var(--text-color); background-color: var(--bg-color); display: flex; flex-direction: column; height: 100vh; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .container { flex: 1; display: flex; flex-direction: column; overflow: hidden; box-shadow: 0 4px 12px rgba(0,0,0,0.08); margin: 4px; border-radius: 8px; background-color: #fff; } header { background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); color: #fff; padding: 12px 16px; text-align: center; flex-shrink: 0; } header h1 { font-size: 1.5em; font-weight: 700; } .tab-controls { display: flex; background-color: #fff; border-bottom: 1px solid var(--border-color); flex-shrink: 0; } .tab-button { flex: 1; padding: 12px 8px; background: none; border: none; border-bottom: 3px solid transparent; color: var(--secondary-color); font-size: 1em; font-weight: 600; cursor: pointer; transition: all 0.3s ease; text-align: center; } .tab-button:hover { color: var(--primary-color); background-color: var(--hover-bg); } .tab-button.active { color: var(--primary-color); border-bottom-color: var(--tab-active-border); background-color: var(--tab-active-bg); } .content-area { flex: 1; overflow-y: auto; padding: 16px; background-color: #fff; } .page { display: none; } .page.active { display: block; animation: fadeIn 0.4s ease-in-out; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .page h2 { font-size: 1.8em; color: var(--primary-color); border-bottom: 2px solid var(--border-color); padding-bottom: 8px; margin-top: 24px; margin-bottom: 16px; } .page h3 { font-size: 1.4em; color: var(--text-color); margin-top: 28px; margin-bottom: 12px; padding-left: 8px; border-left: 4px solid var(--accent-color); } .page p { margin-bottom: 16px; text-align: justify; } .page ul { padding-left: 25px; margin-bottom: 16px; } .page li { margin-bottom: 8px; } .page pre { background-color: var(--code-bg); color: var(--code-color); padding: 16px; border-radius: 6px; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word; margin: 20px 0; font-family: 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace; } .page code { font-family: 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace; background-color: #e9ecef; padding: 0.2em 0.4em; margin: 0; font-size: 85%; border-radius: 3px; } .page pre code { background-color: transparent; padding: 0; font-size: 100%; } .page strong { color: var(--text-color); font-weight: 700; } footer { background: var(--secondary-color); color: #fff; padding: 8px; text-align: center; font-size: 0.9em; flex-shrink: 0; } /* 响应式调整 */ @media (max-width: 768px) { body { font-size: 15px; } header h1 { font-size: 1.3em; } .tab-button { font-size: 0.9em; padding: 10px 6px; } .page h2 { font-size: 1.6em; } .page h3 { font-size: 1.3em; } .content-area { padding: 12px; } } @media (max-width: 480px) { body { font-size: 14px; } header h1 { font-size: 1.2em; } .tab-button { font-size: 0.85em; padding: 8px 4px; } .page h2 { font-size: 1.5em; } .page h3 { font-size: 1.2em; } .content-area { padding: 10px; } .page pre { padding: 12px; } } </style> </head> <body> <div class="container"> <header> <h1>Kconfig 官方文档翻译集合</h1> </header> <div class="tab-controls"> <button class="tab-button active" data-target="syntax-page"><i class="fas fa-code"></i> 语法篇</button> <button class="tab-button" data-target="frontend-page"><i class="fas fa-tools"></i> 前端工具篇</button> <button class="tab-button" data-target="macro-page"><i class="fas fa-cogs"></i> 宏扩展篇</button> </div> <div class="content-area"> <!-- 语法篇 --> <div id="syntax-page" class="page active"></div> <!-- 前端工具篇 --> <div id="frontend-page" class="page"></div> <!-- 宏扩展篇 --> <div id="macro-page" class="page"></div> </div> <footer> <p>© 2025 Kconfig 文档集合 | Generated by 凉安</p> </footer> </div> <!-- 死数据:语法篇 --> <script> const syntaxData = { title: "Kconfig 语言", content: [ { type: 'h2', text: "Kconfig 语言" }, { type: 'p', text: "本文档介绍了 Linux 内核使用的配置系统(Kconfig)的语言。" }, { type: 'h3', text: "配置项定义" }, { type: 'p', text: "一个配置项(菜单项)在 Kconfig 文件中的定义以一行 config <symbol> 开始,后面可以跟着零个或多个属性。每个配置项都有一个类型(bool、tristate、string 等),并且可以有一个可选的提示。" }, { type: 'p', text: "配置项的属性可以在 config 语句之后的任何位置定义,无需遵循特定顺序。配置项定义在下一个 config 语句或文件末尾结束。(如果配置项没有定义提示,那么它对用户是不可见的,除非它是一个被其他配置项依赖的选择)。" }, { type: 'p', text: "以下是一些常用属性:" }, { type: 'ul', items: [ "<strong>bool / tristate / string / hex / int</strong>:定义配置项的类型。bool 表示布尔类型,tristate 表示三态类型,string 表示字符串,hex 表示十六进制数,int 表示整数。", "<strong>prompt <prompt></strong>:为该配置项显示一个提示文本。如果未定义 prompt,则该配置项的提示信息将不可见。", "<strong>default <value></strong>:为该配置项设置一个默认值。如果用户没有为该配置项设置值,则使用此默认值。一个配置项可以有多个默认值,只有第一个有效的默认值会被使用。", "<strong>depends on <expr></strong>:定义了该配置项的依赖关系。如果依赖的表达式 <expr> 为真,则该配置项才可见。如果依赖关系不满足,该配置项的值将被设置为 n。", "<strong>select <symbol> [if <expr>]</strong>:这是一个“反向依赖”。如果当前配置项被设置为 y 或 m,那么 <symbol> 也会被自动设置为 y(除非 <symbol> 本身有 depends on 条件不满足)。[if <expr>] 是可选的,用于在选择时附加一个条件。", "<strong>imply <symbol> [if <expr>]</strong>:类似于 select,但这是一个“软性”的反向依赖。如果当前配置项被设置为 y 或 m,那么 <symbol> 会被“建议”设置为 y。但如果 <symbol> 的依赖关系不满足,或者用户手动设置了它的值,则不会强制改变。", "<strong>range <symbol> <symbol> [if <expr>]</strong>:为 int 或 hex 类型的配置项设置一个有效值的范围。如果用户设置的值不在此范围内,配置系统会给出警告。", "<strong>option <symbol></strong>:定义一些特殊的选项,例如 option defconfig_list 用于指定默认配置文件的搜索路径。" ]}, { type: 'h3', text: "菜单结构" }, { type: 'p', text: "Kconfig 文件可以组织成菜单结构,以提高可读性。" }, { type: 'ul', items: [ "<strong>menu <prompt></strong>:开始一个菜单块,<prompt> 是菜单的标题。所有在 menu 和 endmenu 之间的配置项都属于这个菜单。", "<strong>endmenu</strong>:结束一个菜单块。", "<strong>if <expr></strong>:开始一个条件块。如果 <expr> 为真,则 if 和 endif 之间的配置项才会被处理。", "<strong>endif</strong>:结束一个条件块。", "<strong>comment <prompt></strong>:在菜单中显示一段注释文本,<prompt> 是注释内容。" ]}, { type: 'h3', text: "反向依赖(select 和 imply)" }, { type: 'p', text: "select 和 imply 用于创建反向依赖,即当前配置项影响其他配置项。" }, { type: 'ul', items: [ "<strong>select <symbol></strong>:当 A select B 时,如果 A 被设为 y 或 m,B 会被强制设为 y(除非 B 的 depends on 条件不满足)。这是一种强依赖关系,可能会覆盖用户的选择或其他依赖关系。", "<strong>imply <symbol></strong>:当 A imply B 时,如果 A 被设为 y 或 m,系统会“尝试”将 B 设为 y。但如果 B 的 depends on 条件不满足,或者用户已经明确设置了 B 的值,那么 B 的值不会被改变。这是一种更温和的依赖关系,避免了 select 可能带来的强制性问题。" ]}, { type: 'p', text: "<strong>译者注</strong>:简单来说,select 是“我选了你,你必须也选上”,而 imply 是“我选了你,建议你也选上,但听不听你的”。" }, { type: 'h3', text: "选择块" }, { type: 'p', text: "choice 块用于定义一组互斥的选项。用户只能从中选择一个。" }, { type: 'code', text: `choice\n prompt "Choice example"\n default CHOICE_A\n\nconfig CHOICE_A\n bool "Choice A"\n\nconfig CHOICE_B\n bool "Choice B"\n\nendchoice` }, { type: 'ul', items: [ "<strong>choice / endchoice</strong>:定义一个选择块的开始和结束。", "<strong>prompt <prompt></strong>:为整个选择块设置一个提示。", "<strong>default <symbol></strong>:设置选择块的默认选项。", "<strong>optional</strong>:如果一个 choice 块被标记为 optional,用户也可以不选择其中任何一个选项(即所有选项都为 n)。", "<strong>tristate</strong>:一个 choice 块也可以是 tristate 类型。在这种情况下,用户可以选择将其中一个选项编译为模块(m)。" ]} ] }; </script> <!-- 死数据:前端工具篇 --> <script> const frontendData = { title: "Kconfig", content: [ { type: 'h2', text: "Kconfig" }, { type: 'h3', text: "引言" }, { type: 'p', text: "Kconfig 系统为 Linux 内核提供了一个完整、可扩展且易于使用的配置框架。它允许用户从数千个选项中进行选择,以定制内核的功能和大小。" }, { type: 'p', text: "Kconfig 系统主要由两部分组成:" }, { type: 'ul', items: [ "<strong>Kconfig 语言</strong>:用于定义配置选项、菜单、依赖关系等的语言。这部分在 kconfig-language.txt 中有详细描述。", "<strong>前端工具</strong>:用于读取 Kconfig 文件并向用户呈现配置界面的工具。这些工具包括 make menuconfig、make xconfig 等。" ]}, { type: 'p', text: "本文档主要关注 Kconfig 系统的前端工具及其使用方法。" }, { type: 'h3', text: "make 目标" }, { type: 'p', text: "Kconfig 提供了多个 make 目标来启动不同的前端工具或执行配置相关的任务。" }, { type: 'h4', text: "make *config" }, { type: 'p', text: "这是最常用的目标,用于启动交互式配置界面。" }, { type: 'ul', items: [ "<strong>make config</strong>:最基础的、基于命令行的配置界面。它会逐个询问每个配置选项,非常繁琐,不推荐使用。", "<strong>make menuconfig</strong>:基于文本的菜单式配置界面。它使用 ncurses 库,可以在大多数终端中运行,是最常用的配置方式。", "<strong>make nconfig</strong>:另一个基于文本的菜单式配置界面,与 menuconfig 类似,但有一些额外的功能。", "<strong>make xconfig</strong>:基于 Qt 的图形化配置界面。需要安装 Qt 开发库。", "<strong>make gconfig</strong>:基于 GTK 的图形化配置界面。需要安装 GTK 开发库。" ]}, { type: 'h4', text: "make savedefconfig" }, { type: 'p', text: "此目标会将当前的 .config 文件“精简”并保存为 defconfig 文件。精简的过程是:移除所有值为默认值的配置项。这使得 defconfig 文件非常小,只包含了那些与默认配置不同的选项。这个文件非常适合作为特定平台或产品的默认配置模板。" }, { type: 'h4', text: "make oldconfig / make olddefconfig" }, { type: 'p', text: "当你有一个旧的 .config 文件,但内核源码已经更新(增加或删除了一些配置选项)时,可以使用这两个目标。" }, { type: 'ul', items: [ "<strong>make oldconfig</strong>:会读取旧的 .config 文件,并只对那些新增的或未被定义的配置项提示用户进行选择。对于旧的配置项,保持其值不变。", "<strong>make olddefconfig</strong>:与 oldconfig 类似,但它不会提示用户,而是自动为所有新增的配置项选择其默认值。这是快速更新配置的推荐方式。" ]}, { type: 'p', text: "<strong>译者注</strong>:oldconfig 需要手动干预,而 olddefconfig 是全自动的。在升级内核版本后,通常使用 make olddefconfig 来快速生成一个与新内核源码匹配的配置文件。" }, { type: 'h4', text: "make localmodconfig / make localyesconfig" }, { type: 'p', text: "这两个目标用于创建一个最小化的配置,只包含当前正在运行的系统所需要的功能。" }, { type: 'ul', items: [ "<strong>make localmodconfig</strong>:它会分析当前系统中已加载的模块(通过 lsmod 命令),然后生成一个 .config 文件,该文件只包含支持这些模块所需的配置选项。其他所有不必要的驱动和功能都被禁用(设为 n 或 m)。这非常适合为特定硬件定制一个精简的内核。", "<strong>make localyesconfig</strong>:与 localmodconfig 类似,但它会将当前系统中已加载的模块对应的配置选项设置为 y(编译进内核),而不是 m(编译为模块)。" ]}, { type: 'h3', text: "Kconfig vs. .config" }, { type: 'p', text: "这是一个非常重要的区别:" }, { type: 'ul', items: [ "<strong>Kconfig 文件</strong>:定义了“规则”和“菜单”。它描述了有哪些选项可供选择,这些选项之间有什么依赖关系,以及它们在菜单中如何显示。它就像是配置系统的“源代码”。", "<strong>.config 文件</strong>:是“结果”。它包含了用户在特定一次配置中所做的所有选择。它是一系列 CONFIG_XXX=y/m/n 的赋值语句。构建系统直接读取 .config 文件来决定如何编译内核。" ]}, { type: 'p', text: "简单来说,Kconfig 是“菜单和规则”,.config 是“用户的订单”。" } ] }; </script> <!-- 死数据:宏扩展篇 --> <script> const macroData = { title: "Kconfig 宏语言", content: [ { type: 'h2', text: "Kconfig 宏语言" }, { type: 'h3', text: "引言" }, { type: 'p', text: "Kconfig 宏语言是一种简单的字符串替换机制,用于减少 Kconfig 文件中的重复并提高其可维护性。它在 Kconfig 文件被解析之前进行处理。" }, { type: 'p', text: "宏的调用语法是 $(name)。对于带参数的宏,语法是 $(name,arg1,arg2)。" }, { type: 'h3', text: "用户定义的宏" }, { type: 'p', text: "用户定义的宏使用 macro 关键字来定义。宏定义必须以 endmacro 关键字结束。" }, { type: 'h4', text: "示例" }, { type: 'code', text: `# 定义一个简单的宏\nmacro FOO bar\nendmacro\n\n# 定义一个带参数的宏\nmacro A(x,y) x-y\nendmacro` }, { type: 'p', text: "在上面的例子中,$(FOO) 会被替换为 bar,$(A,1,2) 会被替换为 1-2。" }, { type: 'p', text: "宏是递归扩展的。这意味着宏的主体可以包含对其他宏的调用。例如:" }, { type: 'code', text: `macro BAR $(FOO)\nendmacro\n\nconfig BAZ\n string "Test"\n default $(BAR)` }, { type: 'p', text: "在这个例子中,$(BAR) 会被替换为 $(FOO),然后 $(FOO) 会被替换为 bar。因此,BAZ 的默认值是 bar。" }, { type: 'h3', text: "内置宏" }, { type: 'p', text: "Kconfig 提供了一些内置宏,这些宏在 Kconfig 文件中非常有用。" }, { type: 'ul', items: [ "<strong>$(cc-option, ...)</strong>:检查编译器是否支持给定的 CFLAGS 选项。如果支持,则返回该选项;否则,返回空字符串。", "<strong>$(cc-option-yn, ...)</strong>:与 cc-option 类似,但它返回 y 或 n。如果编译器支持给定的选项,则返回 y;否则,返回 n。", "<strong>$(cc-disable-warning, ...)</strong>:如果编译器支持禁用给定警告的选项,则返回该选项;否则,返回空字符串。", "<strong>$(strip, ...)</strong>:移除字符串开头和结尾的空白字符。", "<strong>$(shell, ...)</strong>:执行一个 shell 命令,并返回其标准输出。", "<strong>$(info, ...)</strong>:在标准输出上打印一条信息。这对于调试 Kconfig 文件非常有用。", "<strong>$(warning, ...)</strong>:在标准错误上打印一条警告信息。", "<strong>$(error, ...)</strong>:打印一条错误信息,并立即停止 Kconfig 的解析。" ]}, { type: 'h3', text: "示例" }, { type: 'p', text: "以下是一个更复杂的示例,展示了如何使用宏来简化 Kconfig 文件。" }, { type: 'code', text: `# 定义一个宏来检查编译器是否支持某个选项\nmacro cc_has_option(option) $(cc-option,$(option))\nendmacro\n\n# 使用宏来定义配置项\nconfig CC_HAS_STACKPROTECTOR\n bool\n default $(cc_has_option,-fstack-protector)\n\nconfig CC_HAS_STACKPROTECTOR_STRONG\n bool\n default $(cc_has_option,-fstack-protector-strong)` }, { type: 'p', text: "在这个例子中,我们定义了一个名为 cc_has_option 的宏,它接受一个参数 option,并使用内置的 cc-option 宏来检查编译器是否支持该选项。然后,我们使用这个宏来定义两个配置项 CC_HAS_STACKPROTECTOR 和 CC_HAS_STACKPROTECTOR_STRONG。这使得 Kconfig 文件更具可读性和可维护性。" } ] }; </script> <!-- 逻辑脚本 --> <script> document.addEventListener('DOMContentLoaded', function() { const tabButtons = document.querySelectorAll('.tab-button'); const pages = document.querySelectorAll('.page'); // 渲染页面内容的函数 function renderPage(pageElement, data) { pageElement.innerHTML = ''; // 清空现有内容 data.content.forEach(item => { let element; switch (item.type) { case 'h2': element = document.createElement('h2'); element.textContent = item.text; break; case 'h3': element = document.createElement('h3'); element.textContent = item.text; break; case 'p': element = document.createElement('p'); element.innerHTML = item.text; // 使用 innerHTML 以支持 <strong> 等标签 break; case 'ul': element = document.createElement('ul'); item.items.forEach(liText => { const li = document.createElement('li'); li.innerHTML = liText; element.appendChild(li); }); break; case 'code': element = document.createElement('pre'); const code = document.createElement('code'); code.textContent = item.text; element.appendChild(code); break; } if (element) { pageElement.appendChild(element); } }); } // 初始化:渲染所有页面内容 renderPage(document.getElementById('syntax-page'), syntaxData); renderPage(document.getElementById('frontend-page'), frontendData); renderPage(document.getElementById('macro-page'), macroData); // 标签切换逻辑 tabButtons.forEach(button => { button.addEventListener('click', () => { const targetId = button.getAttribute('data-target'); // 移除所有按钮的 active 类和所有页面的 active 类 tabButtons.forEach(btn => btn.classList.remove('active')); pages.forEach(page => page.classList.remove('active')); // 为当前点击的按钮和对应页面添加 active 类 button.classList.add('active'); document.getElementById(targetId).classList.add('active'); }); }); }); </script> </body> </html>