admin 管理员组

文章数量: 1087135


2024年12月24日发(作者:ug四轴编程教程)

有了gcc 编译器下面我们就可以来编译我们的内核了,内核的下载地址是

从这里来下载我们所想要的内核,我们现在用的是linux-2.6.18 内

核。

Linux2.6内核新特性

Linux内核吸收了一些新技术,在性能、可量测性、支持和可用性方面不断提高。这些

改进多数是添加支持更多的体系结构、处理器、总线、接口和设备;也有一些标准化内部接

口,简化扩展添加新设备和子系统的支持。

与Linux2.4版本相比,Linux 2.6 版本具有许多新特性,内核也有很大修改。其中一些

修改只跟内核或者驱动开者有关,另外一些修改则会影响到系统启动系统管理和应用程序开

发。

Linux 2.6 内核重要的新特性如下:

(1) 新的调度器: O(1)

(2) 内核抢占

(3) 新的线程模型

(4) 文件系统:access controls , ext2/ext3

(5) 声音:ALSA

(6) 总线:IDE,SCSI

(7) 电源管理

(8) 网络

(9) 用户界面层

(10) 统一的设备模型

在 Linux 上我们可以用以下命令来下载内核。

[root@binnary ~]#wget –c

/pub/linux/kernel/v2.6/2

[root@binnary ~]#wget –c

/pub/linux/kernel/v2.6/

新版本的内核分分两种:

一种是完整源码版本(我们直接解压就可以使用)

另一种是patch文件

[root@binnary ~]#cd linux-2.6.18

[root@binnary linux-2.6.18]#bzcat ../2 | patch –p1

patch命令可以根据补丁文件内容修订文件内容修改指定目录下的文件。

patch –p < diff_file

cat diff_file | patch –p

bzcat diff_2 | patch –p

zcat diff_ | patch –p

其中, 代表按照patch文件的路径忽略的目录级数,每个 ” / ” 代表一级。

p0 是完全按照补丁文件中的路径查找要修改的文件。

p1 则使用去掉 “ / “ 得到的相对路径,再基于当前目录,到相应的相对路径下查找要

修改的文件。

接下来我们就可以仔细阅读内核源代码了。Linux 内核源代码非常庞大,随着版本的发

展不断增加。它使用目录树结构,并且使用 Makefile 组织配置编译。

顶层目录的 Makefile 是整个内核配置编译的核心文件,负责组织目录树中子目录的编

译管理,还可以设置体系结构和版本号等。

[root@binnary ~]# tar -jxvf 2

内核的配置是一个非常大的工程,下面我们就对其进行分析。

[root@binnary ~]#cd linux-2.6.18

COPYING

README

Makefile

arch

有关FSF公共许可证制度GPL的具体说明

linux内核安装和使用的简要说明

重构linux内核可执行代码的make文件

arch是architecture一词的缩写,内核中与具体CPU和系统结构相关的代码分另

放在下一层的子目录中,而相关的.h文件则分别放在include/asm目录下。

alpha 原DEC开发的64位CPU

i386 包括X86系列中自80386以后的所有32位CPU,包括80486,Pentium,Pentium

II等等,也包括AMD K6等兼容系列。

m68k 由Motorola开发的6800系列

mips RISC CPU芯片

sparc RISC CPU芯片,主要用于Sun工作站等机型中

s390 IBM生产的一种大型计算机

ia64 Intel的IA-64结构64位CPU

说明:在每个CPU的子目录中,又进一步分解为boot,mm,kernel等子目录,分别

包含与系统引导、内存管理、系统调用的进入与返回、中断处理以及其他内核

中依赖于CPU和系统结构的底层代码。这些代码有些是汇编代码,但主要还是C

代码。

Documentation 有关linux内核的文件档

drivers

fs

include

设备驱动程序,包括各种设备和字符设备的驱动程序。

文件系统,每个子目录分别对应一个特定的文件系统,还有一些共同的源程序

则用于"虚拟文件系统"vfs

包含了所有的.h文件。如arch子树一样,在include中也是为各种CPU都专设一

个子目录,而通用的子目录asm则根据系统的配置而"符号连接"到具体CPU的专

用子目录,如asmi386、asm m68k等。除此之外,还有通用的子目录linux、net

等。

linux内核的main()及其初始化过程,包括:main.c,version.c等文件

linux内核的进程间通信,包括util.c,sem.c,.c等文件

进程管理和调度,包括:

sched.c,fork.c,exit.c,signal.c,sys.c,.c,softirq.c

,itimer.c等文件。

通用的工具性子程序,如对出错信息的处理等等

内存管理,即虚拟内存管理。包括:

swap.c,swapfile.c,page_io.c,page_alloc.c,swap_state.c,vmscan.c,kmall

oc.c,vmalloc.c,memory.c,mmap.c等文件。

包含了各种不同网卡和网络规程的驱动程序。

用于系统配置的命令文件。

linux内核核心代码(这部分很小)

声音驱动的的支持

init

ipc

kernel

lib

mm

net

scripts

kernel

sound

Linux 内核源代码支持二十多种体系结构的处理器,还有各种各样的驱动程序等选项。

因此,在编译之前必须根据特定平台配置内核源代码。 Linux 内核有上千个配置选项,配

置相当复杂。所以,Linux 内核源码组织了一个配置系统。

Linux 内核配置系统可以生成内核配置菜单,方便内核配置。配置系统主要包含

Makefile 、Kconfig和配置工具,可以生成配置界面。配置界面是通过工具来生成的,工具

通过 Makefile 编译执行,选项则是通过各级目录的 Kconfig 文件定义。

Linux 内核配置命令有:make config、make menuconfig和make xconfig。分别是字符界

面,ncurse光标菜单和X-window图形窗口的配置界面。例:

[root@binnary linux-2.6.18]#make config

[root@binnary linux-2.6.18]#make menuconfig

[root@binnary linux-2.6.18]#make xconfig

执行make menuconfig的主菜单如下图:

那么这个界面是如何产生的呢?下面我们来分析一下。

1、 Makefile

Linux 内核的配置编译都是由顶层目录的 Makefile 整体管理的。顶层的 Makefile 定

义了配置和编译的规则。关于 Makefile 的具体写法大家在前面已经学过,在此不再详解,

这里重点分析相关的变量和规则。

我们可以参考内核源码包中的Documentation/kbuild/,可以得到内核使用

Makefile 的详细说明。

在顶层的 Makefile 中,可以查找到如下几行定义的规则。

config %config: scripts_base outputmakefile FORCE

$(Q)mkdir –p include/linux

$(Q)$(MAKE) $(build)=scripts/kconfig %@

这就是生成内核配置界面的命令规则,它也定义了执行的目标和依赖的前提条件,还

有要执行的命令。

这条规则定义的目标为 config %config ,通配符%意味着可以包括config、xconfig、

gconfig、menuconfig、和oldconfig等。依赖的前提条件是script_basic outputmakefile,这些

在Makefile也是规则定义,主要用来编译生成配置工具。

根据配置工具的不同,内核也有不同的配置方式,我们可以用make help命令来查看。

这里是make help 显示出来的界面的配置信息。

这些内核配置方式是在scripts/Kconfig/Makefile中通过规则定义的。从这个Makefile中,

可以找到下面一些规则定义。如果变量或者通配符带进去,就可以明白要执行的操作。这里

的ARCH以arm为例来说明:

xconfig: $(obj)/qconf

$< arch>/$(ARCH)/Kconfig

执行命令:scripts/kconfig/qconf arch/arm/Kconfig

使用QT图形库,生成内核配置界面。arch/arm/Kconfig是菜单的主配置文件,每种配置

方式都需要。

gconfig: $(obj)/gconf

$< arch>/$(ARCH)/Kconfig

执行命令:scripts/kconfig/gconf arch/arm/Kconfig

使用GTK图形库,生成内核配置界面。

makemenuconfig: $(obj)/mconf

$(Q)$(MAKE) $(build)=scripts/lxdialog

$< arch/$(ARCH)/Kconfig

执行命令:scripts/kconfig/mconf arch/arm/Kconfig

使用lxdialog工具,生成光标配置菜单。因为mconf调用lxdialog工具,所以需要先编译

scripts/lxdialog目录。

config: $(obj)/conf

$< arch/$(ARCH)Kconfig

执行命令:scripts/kconfig/conf arch/arm/Kconfig

完全命令行的内核配置方式。

oldconfig: $(obj)/conf

$< -o arch/$(ARCH)/Kconfig

执行命令:scripts/Kconfig/conf –o arch/arm/Kconfig

完全命令行的内核配置方式。使用-o选项,直接读取已经存在的.config文件,要求

确认内核新的配置项。

silentoldconfig: $(obj)/conf

$< -s arch/$(ARCH)/Kconfig

执行命令:scripts/kconfig/conf –s arch/arm/Kconfig

完全命令行的内核配置方式。使用-s选项,直接读取已经存在的.config文件,提示

但不要求确认内核新的配置项。

$_defconfig: $(obj)/conf

$(Q) $< -D arch/$(ARCH)/configs/$@ arch/$(ARCH)/Kconfig

执行命令:scripts/kconf/conf –D arch/arm/configs/$_defconfig arch/arm/Kconfig

完全命令行的内核配置方式。读取缺省的配置文件arch/arm/configs/$_defconfig,另

存成.config文件。

通过上述各种都可以完成配置内核的工作,在顶层目录下生成.config文件。这

个.conifig文件保存大量的内核配置项,.config会自动转换成include/linux/autoconf.h头文

件。在include/linux/config.h文件中,将包含使用include/linux/autoconf.h头文件。

2、配置工具:

不同的内核配置方式,分别通过不同的配置工具来完成,scripts目录下提供了各种

内核配置工具,下表是这些工具的说明:

配置工具 Makefile相关目标

conf

mconf

qconf

defconfig

oldconfig …

menuconfig

xconf

依赖的程序和软件

conf.c .c

mconf.c .c调用

scripts/lxdialog/lxdialog

qconf.c kconfig_load.c

.c基于QT软件包实现图

形界面。

gconf.c kconfig_load.c

.c基于GTK软件包实现图

形界面

gconf gconfig

其中.c程序实现了解析Kconfig文件和内核配置主要函数。.c

程序还直接包含了下列一些C程序,这样各种配置功能都包含在.o目录

文件中了。

#include “.c”//lex语法解析

#include “util.c”//配置工具

#include “confdata.c”//.config等相关数据文件保存

#include “expr.c”//表达式函数

#include “symbol.c”//变量符号处理函数

#include “menu.c”//菜单控制函数

理解这些工具的使用,可以更加方便地配置内核。至于这些工具的源代码实

现,一般没有必要去详解分析。

3、Kconfig

Kconfig文件是Linux2.6内核引入的配置文件,是内核配置选项的源文件。内核源码

中的Documentation/kbuild/文件有详细说明。

前面已经提到了arch/$(ARCH)/Kconfig文件,这是主Kconfig文件,跟体系结构有关

系。主Kconfig主件调用其他目录的Kconfig文件,其他的Kconfig文件又调用各级子目录

的配置文件,成树状关系。

菜单按照树状结构组织,主菜单下有子菜单,子菜单还有子菜单或者配置选项。每

个选项可以有依赖关系,这些依赖关系用于确定它是否显示。只有被依赖项父项已经选

中,子项才会显示。

下面解释一下Kconfig的特点和语法。

(1)菜单项

多数选项定义一个配置选项,其他选项起辅助组织作用。举例说明单个的配置选项

的定义。

config MODVERSIONS

bool “Set version information on all module symbols”

depends MODULES

help

…….

每一行开头用关键字”config”,后面可以跟多行。后面的几行定义这个配置选项的属

性。

属性包括配置选项的类型、选项提示、依赖关系、帮助文档和缺省值。同名

的选项可以重复定义多次,但是每次定义只有一个选项提示并且类型不冲突。

(2)菜单属性

一个菜单选项可以有多种属性,不过这些属性也不是任意用的,受到语法

的限制。

每个配置选项必须有类型定义。类型定义包括:bool、tristate、string、hex、

init共五种。其中有2种基本的类型:tristate 和 string ,每种类型定义可以有一

个选择提示。下表说明了一个菜单的各种属性:

内核菜单属性说明:

属语法 说明

选“prompt” 每个菜单选项最多有一条提示,可以显示在菜单上。

项[“if” ] 某选择提示可选的依赖关系可以通过”if”语句添加

缺“default”

[“if”]

配置选项可以有几个缺省值。如果有多个缺省值可

选,只使用第一个缺省值。某选项缺省值还可以在

其他地方定义,并且被前面定义的缺省值覆盖。如

果用户没有设置其他值,缺省值就是配置符号的唯

一值。如果有选择提示出现,就可以显示缺省值并

且可以配置修改。某缺省值可选的依赖关系可以通

过”if”语句添加

“depends on 这个定义了菜单选项的依赖关系。如果定义多个依

“/”requires” 赖关系,那么要用”&&”符号连接。依赖关系对于本

菜中其他所有选项有效(也可以用”if”语句)

“select”

l>[“if”] 则是符号的下限。当前菜单符号的值用作符号可以

设置的最小值。如果符号值被选择了多次,这个限

制将被设成最大值。反向依赖只能用于布尔或者三

态符号

数“range”

字l>[“if”] 围内。用户输入的值必须等于第一个符号值或者小

范于等于第二个符号值

帮“help”

助或者

文”---help---“

这可以定义帮助文档。帮助文档的结束是通过缩进

层次判断的。当遇到一行缩进比帮助文档第一行小

的时候,就认为帮助文档已经结

束。”---help---“和”help”功能没有区别,主要给开发

者提供的不同”help”的帮助

(3)菜单依赖关系

依赖关系定义了菜单选项的显示,也能差事少三态符号的选择范围。表达式

的三态逻辑比布尔逻辑多一个状态,用来表示模块状态。下表是菜单依赖关系的

语法说明。

表达式

::=

菜单依赖关系语法说明

结果说明

把符号转换成表达式,布尔和三态符号可以转换成对应

的表达式值。其他类型符号的结果都是”n”

’=’ 如果两个符号的值相等,返回’y’,否则返回’n’

’!=’ 如果两个符号的值相等,返回’n’,否则返回’y’

‘(‘’)’ 返回表达式的值,括号内表达式优先计算

‘!’ 返回(2-/expr/)的计算结果

’&&’ 返回mini(/expr/,/expr/)的计算结果

’||’ 返回max(/expr/,/expr)的计算结果

一个表达式的值是”n”、”m”或者”y”(或者对应数值的0、1、2)。当表达式的

值为”m”或者”y”时,菜单选项变为显示状态。

符号类型分为两种:常量和非常量符号。

非常量符号最常见,可以通过config语句来定义。非常量符号完全由数字符

号或者下划线组成。

常量符号只是表达式的一部分。常量符号总是包含在引号范围内的。在引号

中,可以使用其他字符,引号要通过””号转义。

(4)菜单组织结构

菜单选项的树状结构有两种组织方式。

第一种是显式的声明为菜单。

menu “Network device support”

depends NET

config NETDEVICE

……

endmenu

“menu”与”endmenu”之间的部分成为”Network device support”的子菜单。所有

子选项继承这菜单的依赖关系。如,依赖关系“NET”就被添加到”NETDEVICE”

配置选项的依赖关系列表中。

第二种是通过依赖关系确定菜单的结构。

如果一个菜单选项依赖于前一下选项,它就是一个子菜单。这要求前一个选

项和子选项同步地显示或者不显示。

config MODULES

bool “Enable loadable module support”

config MODVERSIONS

bool “Set version information on all module symbols”

depends MODULES

comment “modules support disabled”

depends !MODULES

MODVERSOIONS依赖于MODULES,这样只有MODULES不是”n”的时候,

才显示。反之,MODULES是”n”的时候,总是显式注释”module support disabled”。

(5)Kconfig语法

Kconfig配置文件描述了一系列的菜单选项。每一行都用一个关键字开头

(help文字例外)菜单的关键字如下表所示。其中菜单开头的关键字有:config、

menuconfig、choice/endchoice、comment、menu/endmenu。它们也可以结束一

个菜单选项,另外还有if/endif、source也可以结束菜单选项。

表:Kconfig菜单关键字说明

关键字 语法 说明

Config “config” 这可以定义一个配置符号,并且可

以配置选项属性。

Menuconfig “menuconfig” 这类似于简单的配置选项,但是它暗示:所

有子选项应该作为独立的选项列表显示

Choices 这定义了一个选择组,并且可以配置选项属

性。每个选项只能是布尔或者三态类型。布

尔类型只允许选择单个配置选项,三态类型

可以允许把任意多个选项配置成”m”。如果

一个硬件设备有多个驱动程序,内核一次只

能静态链接或者加载一个驱动,从但是所有

的驱动程序都可以编译为模块。

选择还可以接受另一个选项”optional”,可以

把选择项设置成”n”,并且不需要选择什么选

项。

Menu “menu” 这定义了一个菜单项,在菜单组织结构中有

些描述。唯一可能的选项是依赖关系。

If “ if ” 这定义了一个if语句块。依赖关系表达式样

附加给所有封装好的菜单选项。 “ endif ”

Comment “comment” 这定义了一个注释,在配制过程中显示在菜

单上,也可以回显到输出文件中。唯一可能

的选项是依赖关系。

Source “source” 读取指定的配置文件。读取的文件也会解析

生成菜单。

“choice”

“endchoice”

1、Makefile 的组织与结构

Linux 内核源代码是通过 Makefile 组织编译的。下面我们来看一下 Makefile 文件。先前

我们学过 Makefile 方件的编写,在此不再多说,下面我们看一下 Makefile 包含的五个部

分:

Makefile

.config

scripts/Makefile*

kbuild Makefiles

顶层目录下的 Makefile

内核配置文件

所有kbuild Makefile 通用规则等定义

内核编译各级目录下的 Makefile ,在约有五百多个

arch/$(ARCH)/Makefile 对应体系结构的 Makefile

顶层目录的 Makefile 文件:

▲它会读取 .config 文件。

▲负责编译 vmlinuz 和 module 。并遍历内核源码树中所有子目录,编译所有的目标文件。

每个子目录都有一个子 Makefile 文件,这些 Makefile 执行从上层传递下来的命令。这些

Makefile 使用 .config 文件中的信息,构建各种文件列表,由 Kbuild 编译静态链接的或者

模块化的目标程序。

2、Makefile 语言

内核Makefile 是配合GNU make 使用的。除了GNU make 的文档中的特点,内核的

Makefile 还有一些GNU 扩展的功能。

GNU make 支持基本的链接表处理功能。内核 Makefile 使用新颖的编译列表格式,编

译过程几乎可以不用if 语句。

GNU make 有多种变量赋值操作符:“=”、“:=”、“?=”、“+=”。

第1 种是“=”操作符,在“=”左侧是变量,右侧是变量的值,右侧变量的值可以定义在文件的

任何一处,也就是说,右侧中的变量不一定非要是已定义好的值,其也可以使用后面定义的

值。

可以把变量的真实值推到后面来定义。但是这种形式也有不好的地方,那就是递归定义,

这会让make 陷入无限的变量展开过程中去,当然,make 是有能力检测这样的定义,并会

报错的。还有就是如果在变量中使用函数,那么,这种方式会让make 运行时非常慢,更糟

糕的是,会使得wildcard 和shell 两个函数发生不可预知的错误。因为不会知道这两个函数

会被调用多少次。

第2 种是“:=”操作符,这种方法,前面的变量不能使用后面的变量,只能使用前面已

定义好了的变量。如果是这样:

y := $(x) bar x := foo

那么,y 的值是“bar”,而不是“foo bar”。

第3 种是“?=”操作符,先看示例:

FOO ?= bar

其含义是:如果FOO没有被定义过,那么变量FOO的值就是“bar”;如果FOO先前被定义过,那么这条

语句将什么也不做。

第4种是“+=“操作符,将右边的变量值附加给左边的变量,例如:

FOO = string1

FOO += string2

这时,FOO的变量值为“string1 string2“。

3、 Kbuild变量

顶层 Makefile 输出下列变量:

(1) VERSION,PATCHLEVEL,SUBLEVEL,EXTRAVERSION定义了当前内核版本。

$(VERSION)、$(PATCHLEVEL)和$(SUBLEVEL)定义了基本的3个版本号,例如:2、6和14,

都是数字,对应内核版本号。

$(EXTRAVERSION)为预先或者附加的补丁定义了更细的子版本号。通常是非数字的字符串

或者空的。

(2) KERNELRELEASE定义了内核发布的版本,一般是单个的字符串。

(3) ARCH定义了目标板体系结构。如make ARCH=arm

(4) INSTALL_PATH为arch Makefile定义了安装驻留内存的内核映像和文件。使

用这个体系结构安装目标板。

(5) INSTALL_MOD_PATH和MODLIB。

$(INSTALL_MOD_PATH)在安装模块的时候作为$(MODLIB)的前缀。这个变量没有在

Makefile中定义,但是可以通过命令传递。

$(MODLIB)指定模块安装的路径。顶层 Makefile 的$(MODLIB)缺省定义如下。

$(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)

4、Kbuild Makefile 的定义

(1)目标定义

目标定义 kbuild Makefile 的核心。它们定义了要编译的文件、它们定义了要编译的文件、

特殊的编译选项和要递归地遍历的子目录。

最简单的 Kbuild makefile 包含一行,如:

obj-y += foo.o

如果要把 foo.o 编译成模块,就可以使用 obj-m 。因此经常使用下列方式:

obj-$(CONFIG_FOO) += foo.o

$(CONFIG_FOO)可以配置成y、m或者不选。

(2)静态链接目标文件 –obj-y

Kbuild Makefile 指定了 vmlinux 的目标文件,就在 $(obj-y) 列表中。这些列表依赖于内核

的配置。

Kbuild 编译所有的 $(obj-y) 文件,再用 $(LD) 命令把目标文件链接成一个 built-in.o 文

件,然后 built-in.o 将被链接到顶层目录的 vmlinux 中去。

$(obj-y)中的文件顺序很重要。因为列表中允许重复,第一个实例链接到built-in中以后,后

面的实例将被忽略。另外,某些函数(module_init()/__initcall)会在启动过程中按照排列顺序

调用。如果改变链接顺序,也可能改变设备的初始化顺序。例如:改变SCSI控制器的探测

顺序,就会导致磁盘重复编号。

例:obj-y

#drivers/isdn/i41/Makefile

obj-$(CONFIG_ISDN) += isdn.o

obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o

(3)可加载模块目标文件-obj-m

$(obj-m)用来指定要编译成可加载模块的目标文件。

一个模块可以由一个或者几个源文件编译生成。对于单个源文件的情况,kbuild Makefile 可

以简单地把文件添加到$(obj-m)中即可。

例:

#drivers/isdn/i41/Makefile

obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o

注:这里的$(CONFIG_ISDN_PPP_BSDCOMP)配置为

”m”。

如果内核模块由几个源文件编译生成,要指定要编译成一个模块。Kbuild 需要知道这个模

块包含哪些目标文件,那么必须设置一个$(-objs)变量。

例:

#drivers/isdn/i41/Makefile

obj-$(CONFIG_ISDN) += isdn.o

isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o

这个例子中,模块的名字是 isdn.o 。Kbuild 会编译 $(isdn-objs) 列表中的目标文件,然后

执行 “$(LD) -r” 命令,把这些目标文件链接成 isdn.o 。

Kbuild 可能通过 –objs 和 –y 后缀识别组成复合目标文件。这允许 Makefile 通过

CONFIG符号的值来确定一个目标文件是不是一个复合目标文件。

例:

#fs/ext2/Makefile

obj-$(CONFIG_EXT2_FS) += ext2.o

ext2-y := balloc.o bitmap.o

ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o

这个例子中,如果$(CONFIG_EXT2_XATTR)配置为 y ,xattr.o 就是复合目标 ext2.o 的一

部分。

注:当把这些目标文件编译链接到内核中去的时候,上面的语法仍然有效。如果配置

CONFIG_EXT2_FS=y ,kbuild 会单独编译 ext2.o 文件,再链接到 built-in.o 文件中。

(4)库目标文件lib-y

用obj- 列出的目标文件可以用于模块或者指定目标的 built-in.o 文件,也可以列出要包含到

一个库 lib.a 中的目标文件。用 lib-y 列出的目标文件可以组合到目录下的一个库中。在

obj-y 中列出并且在 lib-y 中列出的目标文件不会包含到这个库中,因为它们是内核可以访

问的。为了一致性,在 lib-m 中列出的目标文件会包含到 lib.a 中。

注:同一个 kbuild makefile 可以把文件列到 built-in 表中,同时还是一个库的列表的一部

分。因此,同一个目录可以包含一个 built-in.o 和一个 lib.a 文件。

例:

#arch/i386/lib/Makefile

lib-y := checksum.o delay.o

这会基于 checksum.o 和 delay.o 创建一个 lib.a 文件。为了让 kbuild 知道有一个 lib.a 要

编译,这个目录应该添加到 libs-y 列表中。

lib-y 一般仅限于 lib/ 和arch/*/lib/ 目录。

(6) 遍历子目录

一个 Makefile 只负责在自己的目录下编译目标文件。各子目录下的文件应该由各自的

Makefile 来管理。编译系统会自动在子目录中递归地调用 make 。

这项工作也要用到 obj-y 和obj-m 。比如

ext2 在一个单独的目录中,在 fs 目录下的

Makefile 使用下面的配置方法。

#fs/Makefile

obj-$(CONFIG_EXT2_FS) += ext2/

如果 CONFIG_EXT2_FS 设成 y 或者 m ,对应的 obj- 变量就会设置,并且 Kbuild 就会

下到 ext2 目录中编译。Kbuild 只使用这个信息决定是否需用要访问这个目录,编译的工作

是子目录中的 Makefile 负责。

对于 CONFIG_选项既不是 y 也不是 m 的目录,使用 CONFIG_ 变量可以让 Kbuild 忽略

掉。这是一个很好的办法。

(6)编译标志

编译标志包括:EXTRA_CFLAGS、EXTRA_AFLAGS、EXTRA_LDFLAGS、

EXTRA_ARFLAGS。

所有的 EXTRA_ 变量只适用于当前的 Kbuild makefile 。EXTRA_变量适用 Kbuild

makefile 中执行的所有的命令。

$(EXTRA_CFLAGS) 通过 $(CC) 指定编译 C 文件的选项。

例:

#drivers/sound/enu10k1/Makefile

EXTRA_CFLAGS += -I$(obj)

ifdef DEBUG

EXTRA_CFLAGS += -DEMU10K1_DEBUG

endif

因为顶层目录 Makefile 的变量 $(CFLAGS) 用于整个源码树的编译,所以这种变量定义是

必须的。

在编译汇编语言源码的时候,$(EXTRA_ARFLAGS) 分别是与每个目录$(LD)和$(AR)的选

项类似的字符串。

例:

#arch/m68k/fpsp040/Makefile

EXTRA_LDFLAGS := -x

CFLAGS_$@ ,AFLAGS_$@

CFLAGS_$@ 和 AFLAGS_$@ 仅适用于当前 kbuild makefile 的命令。

$(CFLAGS_$@) 为 $(CC) 指定每个文件的选项。$@ 代表指定的文件名。

例:

#drivers/scsi/Makefile

CFLAGS_aha152x.o = -DAHA152X_STAT –DAUTOCONF

CFLAGS_gdth.o = # -DDEBUG_GDTH=2 –D__SERIAL__ -d__COM2__

-DGDTH_STATISTICS

CFLAGS_seagate.o = -DARBITRATE –DPARITY –DSEAGATE_USE_ASM

这些行指定了 aha152x.o 、gdth.o 和 seagate.o 的编译标志。

$(AFLAGS_$@)对于汇编语言编译有类似的特点。

例:

#arch/arm/kernel/Makefile

AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR) –traditional

AFLAGS_head-armo.o := -DTEXTADDR=$(TEXTADDR) –traditional

(7) 依赖跟踪

Kbuild 按照下列步骤跟踪依赖关系。

·所有依赖的前提文件(*.c 和 *.h 文件)

·在依赖的前提文件中用到的CONFIG_选项

·编译目标文件用到的命令行

因此,如果修改$(CC)的一个选项,所有相关的文件都会重新编译。

(8)特殊的规则

当 Kbuild 结构不提供必须的支持的时候,要使用特殊规则。一个典型的例子是在编译过程

中生成头文件。另外一个例子是体系结构相关的 Makefile 需要特殊的规则准备映像等。

特殊的规则可以按照普通的规则来写。Kbuild 不在 Makefile 所在的目录执行,因此所有

特殊的规则应该为依赖的文件和目标文件提供相对路径。

定义特殊规则的时候,常用到两个变量:$(src) 和 $(obj)。

$(src) 是指向 Makefile 所在的目录的相对路径。当引用位于源码树的文件的时候,总是

使用$(src)。

$(obj) 是指向目标文件保存的相对路径。当引用生成文件的时候,总是使用$(obj)。

例:

#drivers/scsi/Makefile

$(obj)/53c8xx_d.h: $(src)/53c7, $(src)/script_

$(CPP) –DCHIP=810 - < $< | … $(src)/script_

这是一个特殊规则,遵守普通的 make 语法 uo$(src)前缀。

4、 体系结构相关的 Makefile 定义

顶层的 Makefile 在开始遍历各级子目录之前,要设置环境变量和做准备工作。

顶层目录 Makefile 包含通用的部分,arch/$(ARCH)/Makefile 则包含了设置 Kbuild 指定

的体系结构需要的东西。因此,arch/$(ARCH)/Makefile 设置一些变量并且定义一些目标规

则。

(1) 通过变量设置编译体系结构相关代码

LDFLAGS_vmlinx 是用来指定 vmlinux 额外的编译标志,在链接最终的 vmlinux 时传递给

链接器,通过 LDFLAGS_$@ 调用。

例:

#arch/arm/Makefile

LDFLAGS_vmlinux :=-p –no-undefined –X

CFLAGS 是 $(CC) 编译选项标志。缺省值在顶层 Makefile 中定义。对于不同的体系结构,

有附加选项。通常 CFLAGS 变量依赖于内核配置。

例:

arch-$(CONFIG_CPU_32v4) := -D__LINUX_ARM_ARCH__=4 –march=armv4

CFLAGS += $(CFLAGS_ABI) $(arch-y) $(tune-y)

许多体系结构相关的 Makefile 通过目标板 C 编译动态地探测支持选项。比如可以把相关

选项中的配置选项扩展为 y 。

CFLAGS_KERNEL 是$(CC)编译built-in 的专用选项。它包含了用于编译驻留内存内核代码的

额外 C 编译标志。

CFLAGS_MODULE 是 $(CC) 编译模块专用选项。它包含了用于编译可动态加载的内核块的 C

编译选项。

(2)添加 archprepare 规则的依赖条件

archprepare 规则用来列出编译依赖的前提条件,在开始进入各级子目录编译之前,先生成

依赖的文件。例:

#arch/arm/Makefile

archprepare: maketools

这个例子中,在进入子目录编译之前,要选处理 maketools 文件。许多头文件的生成也使

用 archprepare 规则。

(2) 列出要遍历的子目录

arch Makefile 配合顶层目录的 Makefile 定义如何编译 vmlinux 的变量。对于模块没有

对应体系结构的定义,所以模块编译方法是和体系结构无关的。

编译列表包括:head-y , init-y , core-y , libs-y , drivers-y , net-y 。

$(head-y) 列出链接到 vmlinux 的起始位置的目标文件。

$(libs-y) 列出 lib.a 的库文件所在的目录。

剩余列出的目录都是 built-in.o 文件所在的目录。

链接过程中,$(init-y) 列出的目标文件紧跟在 $(head-y) 后面。然后是 $(core-y)、

$(lib-y)、$(drivers-y)和 $(net-y)。

顶层目录 Makefile 的定义包含了所有普通目录,arch/$(ARCH)/Makefile 只添加体系结构

相关的目录。

例:

#ach/arm/Makefile

core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/

core-y += $(MACHINE)

lib-y += arch/arm/lib

drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/

(3) 体系结构相关的映像

arch Makefile 还定义 vmlinux 文件编译的规则,并且压缩打包到自引导代码中,在相关

的目录下生成 zImage 。这里包括各种不同的安装命令。不同的体系结构没有标准的规则。

通常是在 arch/$(ARCH)/boot 目录下做一些特殊处理。

Kbuild 不负责支持 arch/$(ARCH)/boot 目录的编译。因此,arch/$(ARCH)/Makefile 文件

应该自己定义编译目标。

推荐的方法是包含 arch/$(ARCH)/Makefile 中包含快捷方式,使用全路径调用子目录下的

Makefile 。

例:

#arch/arm/Makefile

boot := arch/arm/boot

zImage Image xipImage bootpImage uImage: vmlinux

$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@

“$(Q)$(MAKE) $(build)=

)”是在一个子目录中调用 make 的推荐方法。

在这里,不同体系结构的相关目标定义没有一致的规则,可以通过 “make help” 命令列

出相关的帮助。因此,还要定义帮助信息。

例:

#arch/arm/Makefile

define archhelp

echo ‘* zImage -Compressed kernel image (arch/$(ARCH)/boot/zImage)’

……

endif

当不带参数执行 make 的时候,首先会编译遇到的第一个目标。顶层目录 Makefile 中的第

一个目标是 all 。不同体系结构应该定义缺省的引导映像,在 “make help”中加注*kgn ,

而且加到目标 all 的前提条件中。

例:

#arch/arm/Makefile

ifeq ($(CONFIG_XIP_KERNEL),y)

all: xipImage

else

all: zImage

endif

当配置好了内核以后,执行 make 。如果没有把内核配置成 XIP 方式,就调用 zImage 的

规则。

(4) 编译非 kbuild 目标

除了使用 obj-* 列表指定编译的目标文件以外,还可以使用 extra-y 列表指定当前目录下

要创建的附加目标。

对于以下两种情况需要 extra-y 列表。

·使 kbuild 在命令行中检查文件修改变化。比如使用 $(call if_changed,xxx) 语句。

·告诉 kbuild 要编译或者删除哪些文件。

例:

#arch/arm/kernel/Makefile

extra-y := head.o init_task.o

在这个例子中,extra-y 用来列出应该编译的目标文件,但是不应该连接到 built-in.o 中。

(5) 编译自引导映像有用的命令

kbuild 提供了一些编译引导映像时很有用的宏。

· if_changed

if_changed 是下列命令的基本构成部分。

target: source(s) FORCE

$(call if_changed,ld/objcopy/gzip)

编译这个规则的时候,首先检查是否有文件需要更新或者命令行有没有改变。如果任何编译

选项改变,会强制重新编译。任何使用 if_changed 的目标必须列在 $(targets) 中,否则

命令行检查会出错,并且目标总是会编译。

注:一个常见的错误是忘记 FORCE 前提条件下。

另外一个问题是有无空格很重要。例如:下列语句的逗号后面有一个空格,这个空格会导致

语法错误。

target: source(s) FORCE

$(call if_changed, ld/objcopy/gzip) #WRONG!#

·ld

具有链接目标的功能。通常使用 LDFLAGS_$@ 设置 ld 的选项。

·objcopy

复制转换二进制程序。使用在 arch/$(ARCH)/Makefile 中的OBJCOPYFLAGS编译选项。

OBJCOPYFLAGS_$@可以用来添加附加的编译选项。

·gzip

压缩目标。使用最大压缩方式。

例:

$(obj)/: $(obj)/../Image FORCe

$(call if_changed,gzip)

(7)定制 kbuild 命令

当 kbuild 带KBUILD_VERBOSE=0 选项执行的时候,通常只会显示一个命令的简写。要在自

定义的 kbuild 命令中使用这种功能,必须设置以下两个变量。

·quiet_cmd_ 代表要显示的命令

·cmd_ 代表要执行的命令

例:

#arch/arm/boot/Makefile

quiet_cmd_uimage = UIMAGE $@

cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) –A arm –O linux –T kernel

-C none –a $(ZRELADDR) –e $(ZRELADDR)

-n ‘Linux-$(KERNELRELEASE)’ –d $< $@

$(obj)/uImage: $(obj)/zImage FORCE

$(call if_changed,uimage)

@echo ‘ Image $@ is ready’

当带 “KBUILD_VERBOSE=0”更新编译的时候,只显示下列一行,而不把编译信息都显示出

来。

UIMAGE arch/arm/boot/uImage

(8)预处理链接脚本

当编译 vmlinux 映像的时候,将用到链接脚本 arch/$(ARCH)/ 。这个脚本预

处理变体文件是相同目录下的 .S。

Kbuild 知道 .lds 文件并且包含 *.lds.S 到 *.lds 的转换规则。

例:

#arch/i386/kernel/Makefile

always :=

$(always) 列表告诉 kbuild 编译目标 。

#Makefile

export CPPFLAGS_ += -P –C –U$(ARCH)

$(CPPFLAGS_) 列表告诉 kbuild 在编译 的时候使用指定的选

项。

当编译 *.lds 目标文件的时候,kbuild 使用以下变量。

·CPPFLAGS: 在顶层目录 Makefile 中定义。

·EXTRA_CPPFLAGS: 可以在 kbuild Makefile 中定义。

·CPPPFLAGS_$(@F): 目标板特定选项。

Kbuild 的 *.lds 文件结构在多种体系结构的文件中使用。

(9)$(CC) 支持的函数

内核编译可能会使用不同版本的 $(CC) ,不能版本支持独立的一套选项和特点。Kbuild 提

供检查 $(CC) 有效选项的基本支持。$(CC) 一般就是 gcc 编译器,但是可能会在其他替代。

·cc-option

cc-option 选项用于检查$(CC)是否支持一个给定的选项或者第二个可选项。

例:

#arch/i386/Makefile

cflags-y += $(call cc-option,-march=Pentium-mmx,-march-i586)

在上面的例子中,如果 $(CC) 支持 –march=Pentium-mmx ,那么cflags-y 会被赋给这个

选项。否则,使用-march-i586 选项。如果没有后一个选项,在前一个选项不支持的情况下,

cflags-y 不会赋什么值。

· cc-option-yn

cc-option-yn 用于检查 gcc 是否支持给定的选项。如果支持,返回 y ,否则返回 n 。

例:

#arch/ppc/Makefile

biarch := $(call cc-option-yn,-m32)

aflags-$(biarch) += -a32

cflags-$(biarch) += -m32

在上面的例子中,如果 $(CC) 支持 –m32 选项, $(binarch) 就设成 y 。当 $(biarch) 等

于 y 时,扩展变量 $(aflags-y) 和 $(cflags-y) 会赋值 –a32 和 –m32 。

·cc-option-align

gcc 版本大于等于 3.0 时,使用选项的移位类型指定函数的对齐。用作选项前缀的

$(cc-option-align) 会选项合适的前缀。

cc-option-align 的伪语言描述如下。

if gcc < 3.00

cc-option-align = -malign

else if gcc >= 3.00

cc-option-align = -falign

例:

CFLAGS += $(cc-option-align)-functions = 4

上面的例子,对于 gcc >= 3.00 ,选项为 –falign-functions=4;对于 gcc<3.00 ,选项

为 –malign-functions=4 。

·cc-version

cc-version 返回$(CC)编译器的版本号数字。格式为代表主从版本号的2个十进制数。例如:

gcc-3.41 应该返回 0341。

当 $(CC) 版本在特定区域会导致错误的时候,cc-version 是很有用的。例如: -mregparm=3

选项的支持在一些 gcc 版本中不完整。

例:

#arch/i386/Makefile

GCC_VERSION := $(call cc-version)

cflags-y += $(shell

if [ $(CC_VERSION) –ge 0300 ] ; then echo “-mregparm=3”; fi;)

上面的例子中,-mregparm=3 只能用于版本大于等于3.0的gcc 。


本文标签: 文件 内核 选项 编译 配置