天天看点

《Linux内核修炼之道》——2.2 编译内核

本节书摘来自异步社区《linux内核修炼之道》一书中的第2章,第2.2节,作者:华清远见嵌入式培训中心 任桥伟著,更多章节内容可以访问云栖社区“异步社区”公众号查看

linux内核修炼之道

2.2.1 准备工作

虽然与配置内核相比,编译内核所做的工作要少得多,但是在正式编译之前,我们仍需要做一些必要的准备。

1.需要了解的基础知识

首先我们需要了解系统中与编译过程有关的目录及文件。

/boot/vmlinuz-< version >:用于启动的压缩内核镜像。

/boot/system.map-< version >:存储内核符号表。

/boot/initrd.img-< version >:一个镜像文件,类似ramdisk(initrd的全称就是initial ramdisk),它将一些驱动程序和命令工具打包到img里,比如sisc_mod、ext3、sd_mod等模块和insmod、nash等命令,然后在开机的时候在内存里开辟一段区域,释放到那里运行。

它的作用是,在没有mount根分区(/)以前,系统要执行一些操作,比如挂载scsi驱动,此时就把initrd释放到内存里,作一个虚拟的/,然后执行其根目录下的脚本,运行insmod等命令加载模块。

/boot/grub/menu.lst:grub的配置文件(不同的发行版中它可能位于不同位置)。

/lib/modules/:该目录包含了内核模块(包括系统自带的和自己编译的)及其他文件,不同的子目录由内核版本号来区分。

/lib/modules/< kernel-version >/build/:存放编译新模块所需的文件,包括了makefile、.config、module.symvers(模块符号信息)以及内核头文件等。

/lib/modules/< kernel-version >/kernel/:存放模块的ko文件。

/lib/modules//modules.alias:模块别名定义,模块加载工具使用它来加载相应的模块。

/lib/modules/< kernel-version >/modules.dep:定义了模块间的依赖关系。

/lib/modules/< kernel-version >/modules.symbols:标识符号属于哪个模块。

2.下载内核源码压缩包

需要注意下载bz2格式的压缩包时,需要安装bzip2工具进行解压。

3.获取相关补丁

如果需要的某些特性并没有被现有内核支持,则需要去获取相关的补丁。比如,为了使内核支持图形化的启动界面,我们可能要用到bootsplash工具。bootsplash项目的网站<code>http://www.bootsplash.org/</code>上提供了针对很多内核版本的补丁供下载。

4.构建编译环境

编译内核需要用到一系列的工具,在编译之前,我们需要确保它们已经被安装。下面是一些debian和ubuntu发行版上用到的工具包。

modutils:模块工具。

kernel-package:包括了make-kpkg等工具。

patch:如果不需要为内核打补丁,可以不安装patch工具包。

build-essential:提供了c/c++的编译环境,包括了gcc、make等工具。

5.备份

当修改内核时,我们必须准备一个能够启动的备用内核。实现该目的的一种方式是通过配置linux引导程序(lilo或grub)以允许用户选择启动的内核映象,其中之一是从未修改过的内核的备份。

2.2.2 如何为内核打补丁

通过打补丁的方法升级内核版本,可以不用下载整个源代码。针对每个内核版本的补丁文件可以在ftp.kernel.org上面获得,我们的问题是应该选择哪个补丁文件,一个补丁又到底应该打在哪个版本的内核上。

下面的内容简单介绍了如何应用与卸载补丁,详细的内容也可以查看内核文档document/applying-patchs。

1.什么是补丁

一个补丁就是一个文本文档,由diff工具创建,它存放了两个不同版本的源代码之间的差异。为了正确地应用一个补丁,我们需要知道这个补丁文件是以哪个版本为基础产生出来的,以及它将把目前的源代码变化到什么新的版本,简单地说,就是需要清楚产生这个补丁文件的两个源码版本的情况。

2.如何打补丁和卸载补丁

patch工具可以用于打补丁和卸载补丁。内核的补丁是相对于保存内核源码的父目录而生成的,这就意味着,补丁文件中的文件路径包含了内核源码存放目录的名字(比如linux-2.6.23/,或者像是“a/”和“b/”之类的其他名字)。但是很可能我们本地系统上的内核源码存放目录和补丁中不匹配,为了解决这个问题,我们需要切换到自己的源码目录,并且在执行patch命令的时候加上“-p1”参数,这样就会去掉补丁文件中路径的第一个分量。比如:

为了卸载一个以前打上的补丁,需要使用“-r”参数。

3.如何利用补丁升级内核版本

考虑这样的几个场景:将内核从2.6.23升级到2.6.24;将内核从2.6.23.8升级到2.6.24.6;将内核从2.6.23.6升级到2.6.23.8。不管处于哪种场景,打补丁时要谨记的一点是:内核的补丁文件都是以2.6.x(基础稳定版basic stable,2.6.x.y是稳定版stable)为基础发布的。下面对这3种场景的打补丁过程分别进行介绍。

(1)将内核从2.6.23升级到2.6.24。这种情况,可直接使用补丁文件patch-2.6.24。

因为下载得到的补丁文件通常是使用gzip或bzip2压缩的格式,所以使用前还要将其解压生成patch-x.y.z文件。不过,我们也可以不用解压,使用下面的命令形式:

(2)将内核从2.6.23.8升级到2.6.24.6。这种情况下,我们需要将升级的过程分解为几个步骤,首先将2.6.23.8退回到2.6.23,然后再升级到2.6.24,最后升级到2.6.24.6。

(3)将内核从2.6.23.6升级到2.6.23.8。这种情况下,我们同样需要将升级过程分解,首先将2.6.23.6退回到2.6.23,然后再升级到2.6.23.8。

4.patch的替代工具

除了patch之外,也有其他的用来打补丁的工具,比如interdiff、ketchup等。

2.2.3 编译步骤

下面是针对2.6内核的通用的编译步骤。

(1)下载源码并解压。

虽然我们可以将内核源码存放在任何自己找得到的地方,但通常还是会将内核源码下载到/usr/src目录并解压(linus本人说不要解压到这个目录)。

(2)如果需要的话,下载补丁。

(3)进入刚刚解压的内核源码目录。

(4)如果需要的话,为内核打补丁。

(5)配置内核。

(6)编译内核。

(7)安装内核模块。将所有编译得到的内核模块复制到/lib/modules/&lt; kernel-version &gt;/目录下面。

(8)安装内核。

make install主要完成了3个工作。

复制生成的内核映像到/boot目录。在内核编译完成后,源码树目录arch/i386/boot/中会生成一个bzimage文件,该文件被复制到/boot目录并重命名为vmlinuz-2.6.23。

生成initrd-&lt; kernel-version &gt;.img文件。

配置引导程序(grub或lilo)。

(9)重启进入新内核。

2.2.4 文档的编译

内核源码树的documentation/目录下面有大量的文档,它们是内核最好的参考资料,对于我们学习内核有着重要的意义。我们可以使用下面的一些命令生成指定格式的文档。

执行make htmldocs/pdfdocs/psdocs之后,在documentation/docbook/目录下,会生成一些很重要的文档:

kernel-api:内核开发的api手册。

kernel-locking:内核加锁的howto文档。

kernel-hacking:内核开发的一些注意事项。

usb:usb host端的api手册。

gadget:usb device端的api手册。

2.2.5 编译小技巧

下面是一些内核编译过程中可以使用的小技巧。

(1)屏蔽编译信息。

(2)加速编译过程。

可以使用“-j&lt; n &gt;”参数,其中n = 2 * cpu的个数,对于一般的单cpu系统,通常用是使用“-j2”参数,为编译过程分配两个任务,这样在进行磁盘i/o操作时候,cpu就不会空闲了。

(3)使用verbose模式,将每一步执行的命令都打印出来,并重定向到一个文件中去,这样以后可以方便地查找模块之间的依赖关系。

(4)使用ccache提高编译速度。使用ccache时,需要更改源码树根目录下面的makefile文件,在cc和hostcc变量的定义前添加ccache。