第2章从内核出发

   本章知识点

        介绍Linux内核的一些基本知识:从何处获取源代码,如何编译源代码,如何安装新内核。考察一下内核程序与用户空间程序的差异,以及内核中所使用的通用编程结构。内核在很多方面有其独特性,但从现在来看,内核和其他大型软件项目并没有多大差别。

2.1 获取内核源代码

        Linux内核官网http://www.kernel.org,可以获取当前版本的Linux源代码,可以是完整的压缩形式(使用tar命令创建的一个压缩文件),也可以是增量补丁形式。

1、使用Git

        建议使用Git来下载和管理Linux内核源代码。可以使用Git来获取最新提交到Linus版本树的一个副本:

         git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

        当下载代码后,可以更新你的分支到Linus的最新分支:

        git pull

2、安装内核源代码

        内核压缩以GNU zip(gzip)和bzip2两种形式发布。bzip2是默认和首选形式,因为它比gzip更有优势。以bzip2形式发布的Linux内核叫做linux-x.y.z.tar.bz2,这里x.y.z是内核源码的具体版本。下载源代码之后,对其解压。

        如果压缩形式是bzip2,则运行:

        tar xvjf linux-x.y.z.tar.bz2

        如果压缩形式是GNU的zip,则运行:

        tar xvzf linux-x.y.z.tar.gz

        解压后的源代码位于linux-x.y.z目录下。如果使用git获取和管理内核源代码,不需要下载压缩文件,只要运行git clone命令,git就会下载并且解压最新的源代码。

3、何处安装并触及源代码

        内核源码一般安装在/usr/src/linux目录下。注意:不要把这个源码树用于开发,因为编译你的C库所用的内核版本就链接到这颗树。不要以root身份对内核进行修改,而应当建立自己的主目录,仅以root身份安装新内核。即使在安装新内核时,/usr/src/linux目的都应当原封不动。

4、使用补丁

        在Linux内核社区中,补丁是通用语。可以以补丁的形式发布对代码的修改,也可以以补丁的形式接收其他人所做的修改。增量补丁可以作为版本转移的桥梁。不再需要下载内核源码的全部压缩,而只需给旧的版本打上一个增量补丁。这不仅节约带宽,还省时间。注意:要应用增量补丁,从内核源码树开始,只需运行:

patch -p1 < ../patch-x.y.z

一般来说,一个给定版本的内核补丁总是打在前一个版本上。

2.2 内核源码树

        内核源码树有很多目录组成,而大多数目录又包含更多的子目录。源码树的根目录及其子目录如表2-1所示:

表2-1内核源码树的根目录描述



备注:

        COPYING文件是内核许可证。CREDITS是开发了很多内核代码开发者列表。MAINTAINERS是维护者列表,负责维护内核子系统和驱动程序。Makefile是基本内核的Makefile。

2.3编译内核

        2.6内核提供了一套新工具,使得编译内核更加容易。

1、配置内核

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

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

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

        销售商提供的内核,其发布版中包含了预编译的内核,使得所需的功能得以充分地启用,并计划把所有的驱动程序都编译成模块。这就为大多数硬件作为独立的模块提供了坚实的内核支持。

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

make config

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

make menuconfig

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

make gconfig

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

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

make defconfig

尽管这些缺省值有点随意性,但是,如果从未配置过内核,那会提供一个开端。运行这个命令,然后看看,确保为你的硬件所配置的选项是启用的。

        这些配置项会被存放在内核代码树根目录下的.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、减少编译的垃圾信息

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

        make > ../detritus

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

        make > /dev/null

可把无用的输出信息重定向到/dev/null。

3、衍生多个编译作业

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

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

make -jn

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

make -j32 > /dev/null

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

4、安装新内核

        内核编译好之后,需要安装。怎么安装就和体系结构以及启动引导工具BootLoader有关-查阅启动引导工具的说明,按照其指导将内核镜像拷贝到合适的位置,并且按照启动要求来进行安装。一定要保证随时有一个或两个可以启动的内核,以防新编译的内核出现问题。

        例如,在使用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文件。这是一份符号对照表,用以将内核符号和它们的起始地址对应起来。调试时,如果需要把内存地址翻译成容易理解的函数名以及变量名,这会很有用。


  

猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/81031692