内核配置和构建过程

作者:Greg Kroah-Hartman

构建内核的过程分为两个部分:配置内核选项和使用这些选项构建源代码。在 2.5 内核之前的版本中,配置由每个子目录中的 Config.in 文件和一个主要的帮助文件 Documentation/Configure.help 驱动。用于描述构建过程的语言松散地基于 shell 风格的语言,该语言将控制向用户呈现哪些配置选项,具体取决于当前呈现的选项。

这种方式运行得相当好,但随着时间的推移,内核中不同选项的多样性使该语言的功能超出了其合理处理范围。在 2.5.45 内核版本中,Roman Zippel 重写的配置语言和配置程序被放置在主内核树中。新的配置语言更加灵活和强大。它还将帮助文本与配置逻辑统一起来,使得应用单个驱动程序的补丁更容易,而无需担心单个 Configuration.help 文件中的冲突。

同样在 2.5 系列中,Kai Germaschewski 和其他 kbuild 开发人员缓慢地修改了内核中的 makefile 逻辑,使得基于所选选项构建内核更加容易。本文介绍了 2.5 内核中 makefile 和配置文件的格式,并展示了如何向构建过程添加新的驱动程序。

配置内核

要配置不同的内核选项,用户可以运行文本模式或图形内核配置器。文本模式配置器可以使用 make config 运行,并提示用户按顺序选择配置选项(图 1)。ncurses 文本版本更受欢迎,可以使用 make menuconfig 选项运行(图 2)。图形配置器可以使用 make xconfig 运行,并使用 Qt 作为窗口小部件集(图 3)。

The Kernel Configuration and Build Process

图 1. 使用 make config 配置内核

The Kernel Configuration and Build Process

图 2. make menuconfig 使备份和纠正错误更容易。

The Kernel Configuration and Build Process

图 3. 基于 Qt 的 make xconfig

当内核配置器运行时,它会读取主内核配置文件,对于 i386 平台,该文件位于 arch/i386/Kconfig 中。其他架构的主配置文件位于它们的主目录中。然后,此主配置文件包含来自内核目录树中不同子目录的其他配置文件。这些配置文件也可以根据需要包含其他配置文件。例如,arch/i386/Kconfig 文件包含以下行

source "sound/Kconfig"

这将从该文件读取信息。然后 sound/Kconfig 文件包含许多其他文件

source "sound/core/Kconfig"
source "sound/drivers/Kconfig"
source "sound/isa/Kconfig"
source "sound/pci/Kconfig"
source "sound/ppc/Kconfig"
source "sound/arm/Kconfig"
source "sound/usb/Kconfig"
sound/usb/Kconfig 文件描述了所有 ALSA USB 驱动程序选项,例如
# ALSA USB drivers
menu "ALSA USB devices"
    depends on SND!=n && USB!=n

config SND_USB_AUDIO
    tristate "USB Audio/MIDI driver"
    depends on SND && USB
    help
      Say 'Y' or 'M' to include support for
      USB audio and USB MIDI devices.
endmenu
# 字符可用于注释 Kconfig 文件。同一行上在其后写入的任何内容都不会被配置器读取,但它对于记录文件的用途和应该做什么很有用。

menu 和 endmenu 命令告诉配置器声明一个新的菜单级别或某些配置程序中的新屏幕。在 menu 行中,菜单的名称应在“字符内指定。对于此文件,菜单称为 “ALSA USB 设备”

可以控制菜单和配置选项是否显示。在本例中,仅当选择了 CONFIG_SND 和 CONFIG_USB 选项时才显示 USB 选项菜单,这由 depends on SND!=n && USB!=n 行控制。为了帮助减少输入的数量,所有配置选项都自动以 CONFIG 开头,这在配置语言中未使用。配置选项的有效状态为

  • y—选项已启用。

  • n—选项未启用。

  • m—选项设置为作为模块构建。

如果 CONFIG_SND 和 CONFIG_USB 选项都未设置为 n(意味着它们设置为内置到内核或作为模块构建),则会向用户呈现 CONFIG_SND_USB_AUDIO 选项。此选项可以设置为三个值之一,并且它被描述为“三态”值。应向用户显示的文本是 “USB 音频/MIDI 驱动程序”

tristate "USB Audio/MIDI driver"

用于描述配置变量的有效值为

  • bool—变量只能设置为 y 或 n。

  • tristate—变量可以设置为 y、n 或 m。

  • int—变量可以设置为任何数值。

此配置选项由 depends 逻辑行控制,该逻辑行遵循与菜单选项相同的逻辑。CONFIG_SND_USB_AUDIO 选项依赖于 CONFIG_SND 和 CONFIG_USB 选项,这意味着如果其中一个选项设置为模块,则 CONFIG_SND_USB_AUDIO 选项也应设置为模块。如果两个控制选项都未启用(意味着都设置为 n),则不会显示此选项。如果这两个选项都设置为 y,则可以将此选项选择为 n、y 或 m。所有这些都使用简单的行定义

depends on SND && USB

在内核代码中,将看到配置变量(在上面的示例中为 CONFIG_SND_USB_AUDIO),因此代码可以测试它或任何其他内核配置选项的存在。但是,在 .c 文件中使用 #ifdef 来测试不同的配置选项违反了内核风格的编程指南,我在我的文章“正确的 Linux 内核编码风格”[LJ,2002 年 7 月,www.linuxjournal.com/article/5780] 中介绍了这一点。相反,将 #ifdef 的使用限制在 .h 文件中,保持 .c 文件的清洁和易于阅读。

以前,配置选项的帮助文本放在一个大的 Configuration.help 文件中。现在,帮助文本放在 Kconfig 文件中 depends 行之后。它以包含 help---help--- 的行开头,后跟多行帮助文本,这些文本从 help 行缩进两个空格。

添加新的配置选项

要添加新的配置选项,只需在现有 Kconfig 文件中添加新行,位置与相关的配置选项相同。例如,如果为 ALSA 声音系统编写了新的 USB 声卡驱动程序,它将进入 sound/usb 目录,并将添加 sound/usb/Kconfig 文件。这个新的设备驱动程序控制着神话般的 FooBar USB 扬声器设备。除了 CONFIG_SND_USB_AUDIO 选项之外,它还依赖于启用 CONFIG_SND 和 CONFIG_USB 选项,因为新的驱动程序使用了该驱动程序中的一些函数。新的配置选项应放置在 SND_USB_AUDIO 选项之后,但在 closing endmenu 命令之前,它看起来像这样

config SND_USB_FOOBAR
    tristate "USB FooBar speaker device driver"
    depends SND_USB_AUDIO
    help
        Say Y here if you want to use FooBar USB
        speaker device.
        This code is also available as a module
        (= code which can be inserted in and
        removed from the running kernel whenever
        you want). The module will be called
        usbfoobar.o.

现在,当选择 SND_USB_AUDIO 选项时,将显示此选项(图 4)。

The Kernel Configuration and Build Process

图 4. 新启用的 FooBar USB 扬声器设备

构建内核

内核是使用一系列单独的 makefile 构建的,这些 makefile 在构建内核时链接在一起,形成一个大型 makefile。单独的 makefile 看起来不像任何标准的 makefile,而是遵循内核构建过程特有的特殊格式。makefile 需要仅构建必要的文件,具体取决于启用的配置选项,并以正确的格式(作为模块或内置到内核中)。例如,2.5.59 内核版本中的 drivers/usb/misc/Makefile 看起来像

#
# Makefile for the rest of the USB drivers
# (the ones that don't fit into any other
# categories)
#
obj-$(CONFIG_USB_AUERSWALD)  += auerswald.o
obj-$(CONFIG_USB_BRLVGER)    += brlvger.o
obj-$(CONFIG_USB_EMI26)      += emi26.o
obj-$(CONFIG_USB_LCD)        += usblcd.o
obj-$(CONFIG_USB_RIO500)     += rio500.o
obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
obj-$(CONFIG_USB_TEST)       += usbtest.o
obj-$(CONFIG_USB_TIGL)       += tiglusb.o
obj-$(CONFIG_USB_USS720)     += uss720.o
speedtch-objs := speedtouch.o atmsar.o

obj-$(CONFIG_USB_LCD)        += usblcd.o
如果 CONFIG_USB_LCD 配置选项设置为 m,则将 usblcd.c 文件构建为模块。否则,如果该配置选项设置为 y,则直接将其构建到内核中。如果模块仅由单个 .c 文件制成,则只需添加到内核 makefile 的就是此步骤。

如果驱动程序由多个 .c 文件组成,则需要将文件的名称与此驱动程序调用的模块名称一起在单独的行中列出。在前面的示例文件中,文件和驱动程序名称的列表如下所示

obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o

speedtch-objs := speedtouch.o atmsar.o
第一行控制是否构建 speedtch 模块。如果是,则该行指示它是编译到内核中还是作为模块存在。第二行解释说 speedtouch.c 和 atmsar.c 文件将被构建为 .o 文件,然后链接在一起形成 speedtch.o 模块。

在较旧的内核中,如果文件导出符号,则需要在内核 makefile 中显式提及。在 2.5 及更高版本的内核中,不再需要提及。

向构建过程添加新的驱动程序

要向内核构建过程添加新的驱动程序,如果驱动程序包含在单个文件中,则需要添加一行。基于先前 FooBar USB 扬声器设备的示例,行

obj-$(CONFIG_SND_USB_FOOBAR) += usbfoobar.o

已添加到 sound/usb/Makefile。

如果驱动程序包含在两个文件中,例如 foobar1.c 和 foobar2.c,则需要添加另一行

usbfoobar-objs := foobar1.o foobar2.o
结论

2.5 内核中的内核配置和构建过程比以前的内核版本简单得多,也灵活得多。感谢 Roman Zippel 和 Kai Germaschewski 所做的工作,使内核开发人员更容易专注于编写代码,而不必担心内核构建过程的复杂性。

有关 Kbuild 过程具体信息的良好资源可从 Sam Ravnborg 处获得,网址为 marc.theaimsgroup.com/?l=linux-kernel&m=104162417329638

The Kernel Configuration and Build Process
Greg Kroah-Hartman 目前是 Linux USB 和 PCI 热插拔内核维护者。他在 IBM 工作,从事各种与 Linux 内核相关的工作,可以通过 greg@kroah.com 与他联系。
加载 Disqus 评论