Linux Kernel in a Nutshell - 4
Configuring and Building
现在我们有了某个版本的内核源码。现在可以去编译代码了。第一步是是当地配置内核;之后可以对内核进行编译了。这两个任务都是使用标准的 make
工具实现。
Creating a Configuration
对内核的配置放置在源码树的顶层目录的 .config
文件中。如果你第一次打开内核源码,那么还没有 .config
文件,因此需要首先创建这个文件。它可以基于当前版本的 default configuration
从头开始创建,也可以从发行版本内核中创建。
Configuring from Scratch
最基本配置内核的方法是使用 make config
方法:
$ cd linux-2.6.17
$ make config
内核配置程序将会逐步进行每一个配置选项,并询问是否希望使能这个选项。通常情况下,选项为 [Y/M/n/?]
,大写字母是默认的选项,可以直接使用 Enter
按键选择默认选项。这四个选项为:
-
y
直接编译到内核中
-
n
编译到内核外
-
m
编译为一个模块,在需要时载入模块
-
?
打印描述性信息,并重复提示符
内核包含超过两千个不同的选项,因此逐个进行确定是十分耗费时间的。幸运地是,有一个配置内核的简便方法: 基于预编译的配置。
Default Configuration Options
每一个内核版本具有一个默认的内核配置。这个默认配置是代码维护者认为的针对这个版本最佳的配置选项。有时候,这个默认配置只是对他使用的个人设备的最佳配置。创建这个默认配置,使用下面的命令:
$ cd linux-2.6.17
$ make defconfig
执行这个命令之后,一系列配置选项会在屏幕上显示出来,一个 .config
文件将会写到内核目录中。这样就完成内核的配置了,但是需要客制化配置文件以在你自己的设备上可以完美地运行。
Modifying the Configuration
现在我们已经创建了一个基本的配置文件,它需要调整来支持自己系统中的硬件。对于具体需要修改哪一个配置选项来完成自己的目标,查看第七章。这里我们将想你展示如何选择要修改的选项。
有三个不同交互界面的配置工具: 一个基于终端的配置工具 menuconfig
,一个基于 GTK+
的图形化配置工具 gconfig
,一个基于 QT
的图形话配置工具 xconfig
。
Console Configuration Method
menuconfig
配置工具是基于终端的配置方法,使用键盘上的键盘进行控制。使用下面的命令启动这个配置工具:
$ make menuconfig
在这个配置方法下,程序的使用方式以及不同字符的含义展示在屏幕的顶。屏幕的其他部分包含不同的内核配置选项。
内核配置内容被分为不同的段。每一个段包含一个指定目标的相关选项。在这些段中有不同目标的子段。比如,所有的内核设备驱动可以在主菜单的 Device Drivers
选项下找到。希望进入到这个菜单中,移动箭头令 Device Drivers --->
高亮。之后按下 Enter
键,进入到 Device Drivers
子菜单中。在这个子目录中,可以继续向下进入更细分的子菜单中。比如进入 Generic Driver Options
子菜单。进入到这个子菜单中后,展示了三个选项,头两个具有 [*]
标识,这意味着这个选项被选中,这个选项是 yes-or-no
选项。第三个选项为 < >
标识,这个选项可以填入 Y
编译到内核中,M
编译为模块,或 N
单独编译。
如果选项为 Y
,尖括号中将展示一个 *
;若选项为 M
,尖括号中将展示为 M
;如果选项为 N
,尖括号中将展示为空白。
因此,如果你希望修改这三个选项,来实现选择那些编译时不需要外部固件的驱动,关闭选项来不构建固件,编译用户空间固件加载器作为一个模块,那么需要为第一个选项选择为 Y
,为第二个选项选择为 N
,为第三个选项选择为 M
。
在你完成这些工作之后,可以按下 ESC
键或右箭头之后按下 Enter
键来退出这个子菜单。其他的内核选项也可以使用这个方式进行修改。
当你结束对内核配置的修改工作之后,按下 ESC
键退出主菜单,此时程序会询问是否对修改内容进行保存。
按下 Enter
键来保存配置,或使用右箭头之后按下 Enter
按键选择 No
放弃修改内容。
Graphical Configuration Methods
gconfig
以及 xconfig
配置方法允许你使用图形化方法配置内核。这两个方法几乎是一模一样的,唯一的区别是写它们的工具箱不同。gconfig
使用 GTK
实现,xconfig
使用 QT
实现。
使用鼠标来在菜单中选择选项,比如,你可以选择 Device Drivers
菜单的 Generic Driver Options
子菜单。
Building the Kernel
现在,我们创建了我们希望的内核配置,可以编译内核了。可以使用下面的命令来进行编译:
$ make
运行 make
,会使用之前做的配置内容对内核进行编译。当内核编译过程中,make
打印正进行的文件名,并打印警告或错误信息。
如果内核编译完成后没有发生任何错误,我们就成功的创建了一个内核镜像。
一般情况下,对一个正常的发布版本的编译是不会存在错误的,如果发生了错误,那么请像内核开发者汇报你发现的问题。
Advanced Building Options
内核编译系统除了编译内核、模块,还允许你做一些其他的工作。第十章列出了内核编译系统提供的完整的功能列表。本小节,我们将会讨论部分的高级编译选项。若想要查看如何使用其他的高级编译选项,可以参考内核源码文档,可以在 Documentation/kbuild
目录下找到。
Building Faster on Multiprocessor Machines
内核编译系统工作任务可以被分割城小块任务,交给不同的处理器去处理。通过使用这一特性,你可以完全释放你使用的处理器性能,并显著降低内核编译时间。
使用 make
的 -j
选项,来以多线程方式编译内核。最好给 -j
选项一个数字参数,最好是系统中具有的处理器的双倍。比如,对于双核处理器:
$ make -j4
对于四核处理器:
$ make -j8
如果不对 -j
选项传递参数:
$ make -j
那么编译系统将会为内核源码树下的每一个目录创建一个任务,这会导致处理器失去响应,并将会消耗更长的时间完成编译工作。因此推荐给到 -j
选项一个参数。
Building Only a Portion of the Kernel
在进行内核开发时,有时候你只希望编译内核源码树的某个子目录或某个文件。内核编译系统允许你轻松地完成这项任务。为有选择性地编译一个特定的目录,在命令行中对它进行指定。比如,要编译 drivers/usb/serial
目录,只需键入:
$ make drivers/usb/serial
若使用上面的语句,并不会在目录中生成编译模块镜像,若希望生成模块,则需要键入:
$ make M=drivers/usb/serial
上面的语句,会编译所有对应目录中所有需要的文件,并最终链接生成一个模块镜像。
当你按照上面的方式单独编译某一个目录时,最终内核镜像并没有链接到一起。因此,对这个单独目录的子目录中的文件修改并不会影响内核镜像,这可能与预期不符,执行下面的命令:
$ make
这会检查所有修改的内容,并生成新的最终的内核镜像。
如果想要编译内核树中的某个单个文件,只需要将它作为参数传递给 make
。比如,你希望编译 drivers/usb/serial/visor.ko
内核模块,只需键入:
$ make drivers/usb/serial/visor.ko
编译系统会编译所有 visor.ko
文件需要的文件,并链接最终创建新的模块。
Source in One Place, Output in Another
有时候,我们会将内核源码树放到文件系统的只读位置,并将内核编译输出放置到其他位置。这样不会打乱源码树。内核编译系统可以轻松处理这一需求。只需要告知 make
将输出文件放置到哪里就可以了。比如,希望将输出放到家目录下创建的 linux/linux-2.6.17.11
目录下,只需键入:
$ make O=~/linux/linux-2.6.17.11
这样,所有编译生成的文件将会放到 ~/linux/linux-2.6.17.11
目录下了,需要注意的是,这个 O=
选项也需要在进行配置时使用,将配置文件放置到这个输出目录中,而不是将它生成到源码树中。
Different Architectures
一个十分有用的特性是使用交叉编译方式来允许一个强劲地机器去编译一个小型的嵌入式系统。内核编译系统允许你通过使用 ARCH
参数来完成这个工作。编译系统也允许使用 CC
选项来指定编译时使用的特定的编译器,使用 CROSS_COMPILE
参数指定一个交叉编译工具链。
比如,为对 x86_64
架构编译内核,只需键入:
$ make ARCH=x86_64 defconfig
再比如,使用 /usr/loocal/bin/
目录下的工具链编译 ARM
架构的内核:
$ make ARCH=arm CROSS_COMPILE=/usr/local/bin/arm-linux-
再比如,我们不用交叉编译系统,而只希望修改编译时使用的编译器:
$ make CC="ccache gcc"
即使用 distcc
又使用 ccache
:
$ make CC="ccache distcc"