Kbuild: the Linux Kernel Build System
2016-04-21 19:12
656 查看
Kbuild:Linux内核构建系统
Linux使用一样的基础代码却可以被上至超级计算机下至小型嵌入式设备的不同设备使用着实让我们惊讶。静下来想想其实Linux应该是唯一一款操作系统使用统一的代码基础的,相比之下微软的Windows NT和Windows CE和Apple的OSX和IOS在桌面和移动设备上使用了不同的内核。其中的原因我想大概是Linux内核有许多的抽象层和间接层次结构,并且Linux提供了允许高度定制的二进制内核文件。由于Linux的内核有庞大但统一的体系结构,全部的内核程序都会在内核空间中运行并享有相同的地址空间。因为这种特殊的系统结构,人们必须在内核编译前就确定好内核的功能。但是严格意义上来讲,Linux内核并非完全统一的内核,因为它仍然可以在运行时一定程度上扩展内核模块。为什么说是一定程度上呢?因为实际上在运行时扩展的新模块仍然需要调用已经编译好的内核模块的接口来实现。如果这些调用的接口没有一开始就写进内核中并被编译,那么实际上在加载新模块的时候就会出现找不到依赖的错误,所以后来加载的新模块只是内核功能的一种特殊实现方式。一旦内核模块被装载,那么这个模块就和原来编译好的内核共享一片相同的地址空间。由此看来,就算Linux支持添加新模块,我们仍需要在内核编译前就确定好内核所需要的大部分功能,这些功能将被写入内核镜像中用来在系统运行时加载新的内核模块。
因为上述原因,选择哪些代码在内核编译的时候被编译与否是非常重要的。为了实践这种思想,我们就需要可选择配置的编译,成吨的选项支持我们可以选择我们需要的功能,这些配置选项可以决定编译器在编译的时候是否将特定的C文件、代码段或者数据结构编译进内核镜像和内核模块中。
所以,需要一个简单高效的管理方法来配置这些选项。这个管理这些配置的基础程序就是Linux内核构建系统(kbuild)。
Kbuild的组成
Config symbols:一些可以决定哪些代码或数据结构应该被包含到内核映像或模块中的选项。Kconfig文件:定义每个配置选项和它的属性,比如说它的类型、描述和依赖。构建选项菜单树的程序(比如说
make menuconfig)会根据这些文件加载整个选项菜单。
.config文件:存储每个配置选项的选择。你可以手工编辑这些文件或者使用配置程序(比如说
menuconfig和
xonfig)更新这些文件。
Makefiles:标准的GNU
Makefile文件描述了各个源文件的关系和一些指向各个
make目标的命令,比如说内核映像和模块。
各个组成部分的详细说明
Compilation Options: Configuration Symbols
配置选项用来决定那些功能需要被包含到Linux内核映像中。两个主要的选项标志是:boolean选项和
tristate选项。这两种选项的区别仅仅是选择个数的区别,其中
boolean选项选项毫无疑问是只有
true和
false两个选项,而
tristate选项则有
yes、
no和
module三个选项。
并不是所有的在内核里面的东西都可以被编译成一个模块。许多功能具有干扰性了,你需要在配置时决定是否让你的内核支持这些功能。比如说,你不能添加
Symmetric Multi- Processing (SMP)或者
Kernel preemption support到一个正在运行的内核中。所以这个时候我们就需要用到
boolean选项来明确这些功能。但仍然有大部分的模块是可以在编译完后的内核中添加的,这就是
tristate选项存在的原因———来决定你是将这些可以后期添加的东西现在就编译到内核中
(y),作为模块编译在运行内核的时候加载
(m)还是根本就不编译
(n)。
除了上述两种形式,配置选择标志还有一些其他的形式比如说
strings选项和
hex选项。但因为这并不是用来条件编译的,所以我并不打算在这里多讲。如果需要了解,可以阅读Linux内核的官方文档。
Defining Configuration Symbols: Kconfig Files
定义配置选项的文件被称为Kconfig文件。每个
Kconfig文件可以描述一系列配置,也可以包括一些其他的
Kconfig文件的源码。构建选项菜单树的程序(比如说
make menuconfig)会根据这些文件加载整个选项菜单树。
内核文件中的每一个目录都有一个
Kconfig,里面包含了所有
Kconfig文件和它的子目录。在最外层内核源代码目录,有一个
Kconfig文件是整个树状结构的根。
menuconfig、
gconfig和其他编译目标组合程序从这个根
Kconfig文件开始搜索子目录来构建配置选项菜单树。选择哪个子目录去访问不仅决定于每个上层目录的
Kconfig文件,还会根据配置过程中用户的选项。
Storing Symbol Values: .config File
存储每个配置选项的选择。每次你想要改变内核编译的配置选项都要使用配置程序(比如说menuconfig和
xonfig)更新这些文件。当然,这些工具不仅可以更新你的配置选项,还可以建新的
.config文件如果文件不存在。
因为
.confi文件是一个纯文本文件,所以你也可以不用特殊的程序,手动改变这些文件。这也不失为一种方便的保存配置选项的方法。
Compiling the Kernel: Makefiles
最后一个kbuild系统的组件就是
Makefiles,它们用来编译内核映像和模块。就像
Kconfig文件一样,内核源码包的每一层子目录都有一个
Makefile用来编译这个文件夹下的文件。上一层
Makefile会向下找各个子目录的
Makefile的位置并且会编译它们。最后,这些被编译好的文件会成为Linux内核映像。
怎么将上述组件组合起来
想要在Linux内核中添加一个功能,需要做三件事:将源代码文件放在相应目录下,比如说把
WI-FI设备放在
drivers/net/wireless目录下。
在你放置源代码的目录下为每一个子目录更新每一个
Kconfig文件来让用户可以选择是否添加该功能。
在你放置源代码的目录下为每一个子目录更新
Makefile文件,确保你的构建系统可以条件编译你的代码。
用Scull设备驱动程序作为范例
因为这个设备是一个字符型设备,所以把源代码放置到drivers/char目录下。
接着就是让用户可以选择是否编译这个设备驱动,为了完成这一步我们需要为设备添加一个
Kconfig文件——
drivers/char/Kconfig file。
就像大部分的设备一样,
scull设备可以编译到内核映像里、模块里或者根本就不编译。所以这个配置选项应该叫
SCULL,是一个
tristate选项,具有
(y/n/m)三个选项。
Kconfig Entries for the SCULL Driver
# # Character device configuration # menu "Character devices" config SCULL tristate "Coin char device support" help Say Y here if you want to add support for the coin char device. If unsure, say N. To compile this driver as a module, choose M here: the module will be called coin.
那么怎么使用最新添加的这个配置呢?
就像前面说的一样,构建配置选项菜单树的程序会用到这个配置选项,所以你可以选择让什么编译到你的内核里面。比如说,当我们运行命令
make menuconfig时,命令行程序会开始读取所有
Kconfig文件来构建基础菜单接口。接着你就可以更新我们对
SCULL这个设备的配置选项了。将导航按照
Drivers→Character devices就可以看到我们对于
SCULL设备的选项了,再接着我们就可以根据我们的需要修改这个选项了。
一旦你完成了整个编译配置过程,退出菜单程序,如果配置过程中跟以前的配置发生了改变,那么程序会问你是否应用新的配置。这将会这些配置的选择到
.config文件。对于每一个配置选项都会添加一个
CONFIG_ prefix到
.config文件中。比如说,如果上述设备配置选项是
boolean形式而且我们选择了
yes的话,那么在相应的
.config文件中就会添加对应条目
CONFIG_SCULL=y。如果你并没有为这个配置选项选择,那么在
.config文件里将会对应一条
# CONFIG_SCULL is not set。
tristate形式的选项和
boolean形式的选项一样,都有
yes和
no两种情况,也有没有选择的情况,当然别忘记了这种形式下还有一个选项
module。如果上述设备使用的是这个形式的选项并且选择了
module,那么在
.config文件中就会添加一行
CONFIG_SCULL=m。
在这里我们可以让我们的设备驱动在编译内核的时候就编译到内核映像中或者选择在编译完内核之后在运行的时候再装载这个模块,对应的两种在
.config文件中的指令就是
CONFIG_SCULL=m和
CONFIG_SCULL=y。
一旦你已经生成好了
.config文件,就说明你已经准备好编译内核和内核模块。当你执行编译命令来编译内核以及模块的时候,计算机会先执行一个二进制程序来读取所有
Kconfig文件和
.config文件:
$ scripts/kconfig/conf Kconfig。
这个二进制程序更新(或者新建)了一个内有你对所有配置的选择的C语言头文件——
include/generated/autoconf.h,并且所有
GCC编译器都会包含这个头文件,所以这些配置选项对于所有内核文件的编译都生效。这个文件定义了数以千计用以描述配置选项的选择的
#define宏定义。
下面我们来看看这些宏定义是什么样的。
boolean型选项的
true和
tristate型选项的
yes是等价的,拿
SCULL设备做例子,对于这个选项,会生成三个宏定义如下:
#define __enabled_CONFIG_SCULL 1 #define __enabled_CONFIG_SCULL_MODULE 0 #define CONFIG_SCULL 1
同样的,对于
boolean型选项的
false和
tristate型选项的
no也是等价的,还是
SCULL设备做例子,对于这个选项,会生成宏定义如下:
#define __enabled_CONFIG_SCULL 0 #define __enabled_CONFIG_SCULL_MODULE 0
当然别忘了
tristate型选项还有一个选择
module,继续拿
SCULL设备做例子,对于这个选项,会生成宏定义如下:
#define __enabled_CONFIG_SCULL 0 #define __enabled_CONFIG_SCULL_MODULE 1 #define CONFIG_SCULL_MODULE 1 #define IS_ENABLED(option) \ (__enabled_ ## option || __enabled_ ## option ## _MODULE) #define IS_BUILTIN(option) __enabled_ ## option #define IS_MODULE(option) __enabled_ ## option ## _MODULE
更新Makefiles。
最后一步就是为我们这个设备的子目录更新
Makefiles
好让
kbuild可以为我们编译这个设备的驱动。但是我们怎么让
kbuild来条件编译我们的内核源文件呢?
内核编译系统
Kbuild有两个主要的任务:创造二进制的内核映像和内核模块。为了做到这两个任务,
kbuild将会分别生成一系列的两种文件:
obj-y和
obj-m。前者是一系列和构建内核映像有关的对象,而后者则是一系列将会被编译成内核模块的对象。
.config文件和
aotuconf.h中的配置选项及其选择被用来根据
GUN``make的语法完成以上一系列对象的创造。
Kbuild将会递归的进入到各个文件夹
Makefile文件中并构建列出的一系列对象。更多的信息关于
make语法和对象列表的描述可以读取源码包中的
Documentation/kbuild/makefiles.txt。
对于我们这个
SCULL设备,只需要添加一行
obj-$(CONFIG_SCULL) += scull.o到
drivers/char/Makefile中去就可以了。这句话的意思就是告诉
kbuild去根据
scull.c源文件创造一个对象并把它加入到构建对象的列表里面去。因为
CONFIG_SCULL的选择可能是
y或者
m,
scull.o对象文件将会根据选项被添加到
obj-y或
obj-m列表当中去,接着这个源文件就会被编译成
内核或者
模块。如果我们没有选择
CONFIG_SCULL的值,那么整个
scull.o对象文件将根本不会被编译。
现在你应该了解怎么让源文件有条件地包含到源码包中。最后一个难题就是怎么有条件地编译内核源文件,通过使用
autoconfig.h中的宏定义,这个难题将会被很轻松的解决。
作业
把我们自己编写的驱动程序加入Linux的源码树。翻译信息
原文档:<北京交通大学 杨武杰老师课件>原作者:杨武杰
译者:刘瀚文
相关文章推荐
- mysql开启慢查询日志和新增日志--linux
- centos7 下双网卡如何配置静态IP
- linux管道(pipe)
- 在Linux系统 CentOS 6 下安装KVM虚拟机
- Linux开机启动Jenkins
- 常用linux命令相关
- linux异步IO编程实例分析
- 使用kickstart脚本安装CentOS7
- Linux常用命令----基本文件系统常用命令
- NFS 安装记录
- linux 防火墙 iptables
- linux软连接和硬连接学习笔记
- centos 随意截屏
- 我眼中的Linux设备树(Device tree)
- linux下shapely的安装
- kali linux下开启ssh服务
- Linux date命令用法和使用技巧(获取今天、昨天、一分钟前等)
- Linux od命令
- Linux常见文件格式打包解压命令
- Centos 7 中 QT出现QSqlDatabase: MYSQL driver not loaded