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

简介:

本节书摘来自异步社区《Linux内核修炼之道》一书中的第2章,第2.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项目的网站http://www.bootsplash.org/上提供了针对很多内核版本的补丁供下载。

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”参数,这样就会去掉补丁文件中路径的第一个分量。比如:

# cd /usr/src/linux
# patch –p1 < ../patch-x.y.z

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

# patch –R –p1 < ../patch-x.y.z

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。

# patch –p1 < ../patch-2.6.24

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

# bzcat ../patch-2.6.24.bz2 | patch -p1 //bz2格式
# zcat ../patch-2.6.24.gz | patch -p1   //gz格式

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

# bzcat ../patch-2.6.23.8.bz2 | patch -p1 –R
# bzcat ../patch-2.6.24.bz2 | patch -p1
# bzcat ../patch-2.6.24.6.bz2 | patch -p1

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

# bzcat ../patch-2.6.23.6.bz2 | patch -p1 –R
# bzcat ../patch-2.6.23.8.bz2 | patch -p1

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

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

(1)下载源码并解压。

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

# cd /usr/src
# wget ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.23.tar.bz2 
# tar jxvf linux-2.6.23.tar.bz2

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

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

# cd /usr/src/linux-2.6.23

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

(5)配置内核。

# make menuconfig

(6)编译内核。

# make

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

# make modules_install

(8)安装内核。

# make install

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

复制生成的内核映像到/boot目录。在内核编译完成后,源码树目录arch/i386/boot/中会生成一个bzImage文件,该文件被复制到/boot目录并重命名为vmlinuz-2.6.23。
生成initrd-< kernel-version >.img文件。
配置引导程序(GRUB或LILO)。
(9)重启进入新内核。

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

# make htmldocs        //生成HTML文件
# make pdfdocs        //生成PDF文件
# make psdocs         //生成Postscript文件
# make mandocs        //为Kernel API生成man手册
# make installmandocs //将Kernel API手册页安装到man程序能够找到的目录中

执行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)屏蔽编译信息。

# make > /dev/null

(2)加速编译过程。

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

# make –j2 > /dev/null

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

# make V=1 > ~/bak.txt

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

CC      = ccache $(CROSS_COMPILE)gcc
HOSTCC  = ccache gcc
相关文章
|
15小时前
|
NoSQL Ubuntu Linux
【操作系统】实验三 编译 Linux 内核
【操作系统】实验三 编译 Linux 内核
3 1
|
2天前
|
Linux Windows 编译器
|
3天前
|
存储 算法 Linux
【Linux】线程的内核级理解&&详谈页表以及虚拟地址到物理地址之间的转化
【Linux】线程的内核级理解&&详谈页表以及虚拟地址到物理地址之间的转化
|
3天前
|
安全 Linux
【Linux】详解用户态和内核态&&内核中信号被处理的时机&&sigaction信号自定义处理方法
【Linux】详解用户态和内核态&&内核中信号被处理的时机&&sigaction信号自定义处理方法
|
3天前
|
存储 Linux
【Linux】对信号产生的内核级理解
【Linux】对信号产生的内核级理解
|
3天前
|
消息中间件 算法 Linux
【Linux】对system V本地通信的内核级理解
【Linux】对system V本地通信的内核级理解
|
4天前
|
Linux 编译器 调度
xenomai内核解析--双核系统调用(二)--应用如何区分xenomai/linux系统调用或服务
本文介绍了如何将POSIX应用程序编译为在Xenomai实时内核上运行的程序。
20 1
xenomai内核解析--双核系统调用(二)--应用如何区分xenomai/linux系统调用或服务
|
4天前
|
算法 Linux 调度
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
9 1
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
|
4天前
|
Linux 调度 数据库
|
4天前
|
存储 缓存 Linux
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(三)--实时与非实时数据交互
本文介绍了Xenomai中的XDDP(Xenomai Distributed Data Protocol)通信机制,XDDP用于实时和非实时进程之间的数据交换。XDDP在Xenomai内核中涉及的数据结构和管理方式,以及创建XDDP通道后的实时端和非实时端连接过程。
8 0
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(三)--实时与非实时数据交互