贡献 Linux 内核—Linux 配置
Linux 内核一直是开源社区中最珍视的瑰宝之一。它基于通过模块化共享资源的理念,不知何故既编写良好,又由委员会编写。(或者,至少由许多个人和团队编写,他们在功能上争论/同意。)Linux 保持一切整洁和模块化的方法之一是内核配置系统,通常称为 config、menuconfig 和 xconfig。这些是源代码内核安装程序必须运行以设置内核选项的脚本,但如果您正在阅读本文,您可能已经知道这一点。从表面上看,这些看起来像是三个非常独立的程序,具有完全独立的界面。但实际上,这三者都来自相同的基本规则,Linux 内核的许多程序员必须了解这些规则,才能传播他们的工作,甚至向 Linus 提交他们的补丁。正是这个基本系统为 Linux 用户提供了根据他们的需求设计 Linux 系统所需的选项。
由于 Linux 内核是一个开源项目,它显然接受用户提交的新功能。然而,通常情况下,有添加 Linux 内核功能的愿望和技术的程序员会因为各种原因而选择不这样做。在本文中,我希望澄清围绕内核配置系统的一些谜团,这些谜团可能阻碍用户并阻止他们成为开发人员。在开源工作中,每一个大脑都很重要,每一个将他或她的更改添加到内核中的程序员都使内核对我们其他人来说更加健壮。
首先,您必须有一个理由首先在内核配置脚本中进行修改。也许您只是在探索系统,并等待着有一天您也将向内核提交补丁。或者,也许(更有可能)您向内核添加了一个特定的功能,您觉得它应该得到更广泛的使用,但您希望它准备好集成到 Linus 或 Alan 或其他内核开发大师中。为了本文的目的,我将使用一个假设的补丁来使随机设备驱动程序成为编译时选项,尽管我应该强调,实际上我与该驱动程序的创建绝对没有任何关系。(这是控制 /dev/random 和 /dev/urandom 设备的驱动程序。)此外,我不会在本文中深入讨论内核模块的创建——我将假设您可以从本文中推断出如何做到这一点,特别是如果您足够聪明,首先创建了一个模块化驱动程序。
模块化您的程序的第一步应该是显而易见的:您需要一些 C 预处理器可以识别的名称,以帮助它区分哪些更改是您的,哪些不是。内核通过使用预处理器指令来处理这种区分:内核中到处都是 #ifdef ... #else .. #endif 结构。
当您这样做时,首先要确保的是保持一致性。在像 Linux 内核这样复杂的系统中,一点点一致性可以在以后节省很多麻烦。您应该在 Documentation/Configure.help 中查找类似的选项,并检查它们是否有一个共同的前缀(在 CONFIG_ 之后,当然)。例如,所有块设备选项都以 CONFIG_BLK_DEV_ 开头。当然,这在以后相对容易更改。
选择名称后,您应该在补丁更改的代码部分周围放置 #ifdef...#endif 块(在您执行此操作时,拥有 Linus 树会很有帮助,因为您可以对其进行差异比较并轻松查看您更改的内容)。如果您删除了现有代码,则需要从真实树中集成 #else 块,除非您足够聪明,在编写补丁时将它们保留下来。(我通常在编程的早期阶段使用构造“#if 0”。)此时,编译内核应该可以工作,并且您的选项将被正确禁用,我们可以继续下一步。如果它不起作用或者您的补丁的某些部分仍然存在,那么您显然需要返回并仔细检查您的差异。
为了我的示例的目的,我将为该选项选择名称 CONFIG_RANDOM。随机设备是字符设备,并且在撰写本文时,没有通用的 CONFIG_CHAR_(或类似)前缀。
我们需要采取的下一步是将新的配置选项添加到配置系统中。幸运的是,这相当容易,只有几个警告。在您拥有大部分补丁的目录中(在我的示例中,是 drivers/char),将有一个名为“Config.in”的文件,其中包含该目录中代码的配置选项。可以将配置选项放在不同目录的配置文件中,但这会稍微模糊可读性,并且可能使以后难以找到您的代码。但是,如果它绝对属于其他地方而不是您放置它的位置(或者如果您的代码位置没有这些文件之一),您应该使用您自己的最佳判断,并准备好稍后移动它。浏览此文件,您会看到它包含看起来像是类似于 Bash 或其他 shell 脚本的粗略脚本语言。这种脚本语言很容易被称为“Config Language”,并且应该不难掌握。对于我们的非模块化示例,我们不需要使用该语言的完整词汇,而可以专注于几个关键字(如下定义)。有关该语言的更完整指南,更新的内核提供了完整的参考(Documentation/kbuild/config-language.txt)。但是,实际配置文件中提供了大量示例,并且该语言足够简单,正常维护不需要真正理解该语言和语法。通常,此文件中的行格式化为一个或多个关键字(称为动词),后跟一些参数。以下是动词及其含义的部分列表
comment: 未解析的注释,除非前面带有 mainmenu_option 命令,这将导致注释用作标题。
mainmenu_option: 一个动词,它将下一个注释变成标题。我无法充分解释这对我来说有多奇怪。
bool: 布尔值(是/否)配置选项。此动词始终后跟一个问题和一个配置变量,用于将结果(“y”或“n”)放入其中。
tristate: 一个值,类似于 bool,但具有额外的“制作成模块”可能性,表示为“m”。这仅适用于设备驱动程序。
if... fi: 一个条件块,仅当设置了某个配置变量时才进行评估。
bool 'Random driver support' CONFIG_RANDOM(请注意,这里使用 ' 作为引号字符。这很容易错过。)
如果您的配置选项依赖于另一个选项才能设置,则此模型会变得更加复杂。您需要将您的选项包围在测试先决条件选项的 if ... fi 块中。在各种配置文件中有很多示例可以帮助您完成此过程;如有疑问,请复制。最后一点:您应该意识到此文件不仅用于生成配置过程中的选择列表,还用于生成 include/linux/autoconf.h 文件。为了保持该文件的可读性,您应该注意您添加的选项不要在其他小标题之后出现,否则配置选项在生成该文件时不会出现在正确的位置。(当然,无论它在文件中的哪个位置,它仍然可以工作,但为了可读性,这是需要考虑的事情。)
此时,您的补丁被与配置选项对应的定义包围。您现在可以在配置器中切换补丁的使用。最难的部分已经完成,现在我们只需要填写一些零散的内容。
如果您在多个架构上使用过 Linux,您可能已经注意到,每个不同的平台都有不同的选项。到目前为止,您还没有关心做出太多区分,但现在是时候了。为了以正确的方式修改 defconfig 文件,您需要执行几个步骤。首先,您应该使用您的个人配置设置备份您的 .config 文件。将该文件替换为 arch/<whatever>/defconfig 的副本,然后重新运行配置器。选择您希望成为您的配置选项的默认值的任何选项,并保存该文件。用新的 .config 替换 defconfig,并替换旧的 .config。您现在拥有一个为您的架构构建的新默认配置文件。
如果您的补丁适用于多个架构(例如,它是一种通用的网络协议类型或通常适用的东西),您将需要为您支持的所有架构制作默认配置文件。这可以通过重复上述步骤最容易地完成,除了首先更改源 Makefile 中的 ARCH 行以反映您正在为其构建配置文件的架构。
补丁的最后一部分,也是经常被忽视的部分是必须随配置选项一起提供的帮助文件。有问题的文件是 Documentation/Configure.help。该文件的格式类似于这样(或参见附件示例)
<description> <variable name> <help file>此外,应注意,配置文件之间必须有一个额外的空行,并且较新的内核支持在帮助文本本身中包含空行,前提是空行实际上包含空格或其他空白字符。
强烈建议您尝试找到此文件中大致对应于您的选项用途的部分。通常,但不总是,配置菜单中相似位置的选项在帮助文件中也有相似的位置,这可以作为指示放置您的特定部分最佳位置的有用指示。如果您仍然不知道将所有内容放在哪里,我建议向当前的帮助维护者发送电子邮件,其中包含一个问题和对您的补丁作用的简要描述(甚至包括您希望添加的帮助文本)。
此时您应该完成了。在您可以发送补丁以进行提交之前,明智的做法是启用和禁用它进行编译,并确保两个内核都能正常工作。如果存在依赖关系,请确保它们工作正常,并且您拥有您期望拥有的所有选项,反之亦然。最后,您应该将您的补丁发送到内核列表并请求错误报告,然后再宣布它已完成给 Linus 和其他成员,或者准备好迎接严酷的现实,特别是如果您进行了影响您实际上没有的架构的更改。
CML2:一种新的、更简单的内核配置系统 作者:Eric S. Raymond
电子邮件:jpranevich@lycos.com
Joseph Pranevich (jpranevich@lycos.com) 是一位狂热的 Linux 爱好者,在不为 Lycos 工作时,喜欢写作(各种类型)并参与许多开源项目。