【书摘】编译Linux 2.6内核(节选自《Linux内核设计与实现(原书第3版)》)

hzbook 2011-06-23

编译内核易如反掌。让人叹为观止的是,这实际上比编译和安装像glibc这样的系统级组伴还要简单。2.6内核提供了一套新工具,使编译内核更加容易,比早期发布的内核有了长足的进步。

2.3.1 配置内核

因为Linux源码随手可得,那就意味着在编译它之前可以配置和定制。的确,你可以把自己需要的特定功能和驱动程序编译进内核。在编译内核之前,首先你必须 配置它。由于内核提供了数不胜数的功能,支持了难以计数的硬件,因而有许多东西需要配置。可以配置的各种选项,以CONFIG_FEATURE形式表示, 其前缀为CONFIG。例如,对称多处理器(SMP)的配置选项为CONFIG_SMP。如果设置了该选项,则SMP启用,否则,SMP不起作用。配置选 项既可以用来决定哪些文件编译进内核,也可以通过预处理命令处理代码。

这些配置项要么是二选一,要么是三选一。二选一就是yes或no。 比如CONFIG_PREEMPT就是二选一,表示内核抢占功能是否开启。三选一可以是yes、no或module。module意味着该配置项被选定 了,但编译的时候这部分功能的实现代码是以模块(一种可以动态安装的独立代码段)的形式生成。在三选一的情况下,显然yes选项表示把代码编译进主内核映 像中,而不是作为一个模块。驱动程序一般都用三选一的配置项。

配置选项也可以是字符串或整数。这些选项并不控制编译过程,而只是指定内核源码可以访问的值,一般以预处理宏的形式表示。比如,配置选项可以指定静态分配数组的大小。

销 售商提供的内核,像Canonical的Ubuntu或者Red Hat的Fedora,他们的发布版中包含了预编译的内核,这样的内核使得所需的功能得以充分地启用,并几乎把所有的驱动程序都编译成模块。这就为大多数 硬件作为独立的模块提供了坚实的内核支持。但是,话又说回来,如果你是一个内核黑客,你应当编译自己的内核,并按自己的意愿决定包括或不包含哪一模块。

内核提供了各种不同的工具来简化内核配置。最简单的一种是一个字符界面下的命令行工具:

$ make config

该工具会逐一遍历所有配置项,要求用户选择yes、no或是module(如果是三选一的话)。由于这个过程往往要耗费掉很长时间,所以,除非你的工作是按小时计费的,否则应该多利用基于ncurse库编制的图形界面工具:

$ make menuconfig

或者,是用基于gtk+的图形工具:

$ make gconfig

这三种工具将所有配置项分门别类放置,比如按“处理器类型和特点”。你可以按类移动、浏览内核选项,当然也可以修改其值。

这条命令会基于默认的配置为你的体系结构创建一个配置:

$ make defconfig

尽管这些缺省值有点随意性(在i386上,据说那就是Linus的配置),但是,如果你从未配置过内核,那它们会提供一个良好的开端。赶快行动吧,运行这条命令,然后回头看看,确保为你的硬件所配置的选项是启用的。

这 些配置项会被存放在内核代码树根目录下的.config文件中。你很容易就能找到它(内核开发者差不多都能找到),并且可以直接修改它。在这里面查找和修 改内核选项也很容易。在你修改过配置文件之后,或者在用已有的配置文件配置新的代码树的时候,你应该验证和更新配置:

$ make oldconfig

事实上,在编译内核之前你都应该这么做。

配 置选项CONFIG_IKCONFIG_PROC把完整的压缩过的内核配置文件存放在/proc/config.gz下,这样当你编译一个新内核的时候就 可以方便地克隆当前的配置。如果你目前的内核已经启用了此选项,就可以从/proc下复制出配置文件并且使用它来编译一个新内核:

$ zcat /proc/config.gz > .config

$ make oldconfig

一旦内核配置好了(不论你是如何配置的),就可以使用一个简单的命令来编译它了:

$ make

这跟2.6以前的版本不同,你不用在每次编译内核之间都运行make dep了—代码之间的依赖关系会自动维护。你也无须再指定像老版本中bzImage这样的编译方式或独立地编译模块,默认的Makefile规则会打点这一切。

2.3.2 减少编译的垃圾信息

如果你想尽量少地看到垃圾信息,却又不希望错过错误报告与警告信息的话,你可以用以下命令来对输出进行重定向:

$ make > .. /detritus

一旦你需要查看编译的输出信息,你可以查看这个文件。不过,因为错误和警告都会在屏幕上显示,所以你需要看这个文件的可能性不大。事实上,我只不过输入如下命令:

$ make > /dev/null

就可把无用的输出信息重定向到永无返回值的黑洞/dev/null。

2.3.3 衍生多个编译作业

make程序能把编译过程拆分成多个并行的作业。其中的每个作业独立并发地运行,这有助于极大地加快多处理器系统上的编译过程,也有利于改善处理器的利用率,因为编译大型源代码树也包括I/O等待所花费的时间(也就是处理器空下来等待I/O请求完成所花费的时间)。

默 认情况下,make只衍生一个作业,因为Makefiles常会出现不正确的依赖信息。对于不正确的依赖,多个作业可能会互相踩踏,导致编译过程出错。当 然,内核的Makefiles没有这样的编码错误,因此衍生出的多个作业编译不会出现失败。为了以多个作业编译内核,使用以下命令:

$ make –jn

这里,n是要衍生出的作业数。在实际中,每个处理器上一般衍生出一个或者两个作业。例如,在一个16核处理器上,你可以输入如下命令:

$ make -j32 > /dev/null

利用出色的distcc或者 ccache工具,也可以动态地改善内核的编译时间。

2.3.4 安装新内核

在内核编译好之后,你还需要安装它。怎么安装就和体系结构以及启动引导工具(boot loader)息息相关了—查阅启动引导工具的说明,按照它的指导将内核映像拷贝到合适的位置,并且按照启动要求安装它。一定要保证随时有一个或两个可以 启动的内核,以防新编译的内核出现问题。

例如,在使用grub的x86系统上,可能需要把arch/i386/boot/bzImage 拷贝到/boot目录下,像vmlinuz-version这样命名它,并且编辑/etc/grub/grub.conf文件,为新内核建立一个新的启动 项 。使用LILO启动的系统应当编辑/etc/lilo.conf,然后运行lilo。

所幸,模块的安装是自动的,也是独立于体系结构的。以root身份,只要运行:

% make modules_install

就可以把所有已编译的模块安装到正确的主目录/lib/modules下。

编译时也会在内核代码树的根目录下创建一个System.map文件。这是一份符号对照表,用以将内核符号和它们的起始地址对应起来。调试的时候,如果需要把内存地址翻译成容易理解的函数名以及变量名,这就会很有用。

------------------------------

本文节选自《Linux内核设计与实现(原书第3版)》,作者:Robert Love(Linux核心黑客,Google公司资深软件工程师,Android移动平台内核开发团队成员;曾就职于Novell公司,任职Linux桌 面系统的首席架构师,以及MontaVista和Ximain公司内核开发工程师)。

Global site tag (gtag.js) - Google Analytics