文章目录
概述
Kbuild是Linux系统提供的专门用于编译内核源码的工具,它的底层仍然是基于Makefile机制,但引入了一些扩展功能,例如用于配置内核特性的Kconfig,以及针对于内核功能模块的目标定义语法等,所有这些功能一起组成了完整的Kbuild机制。
Kconfig
Kconfig用于管理内核配置,我们编译内核前通常会使用make menuconfig来配置所需要的内核特性以及进行功能裁剪,其底层就是利用Kconfig进行管理。
Kconfig的结构元素
执行make menuconfig之后会进入如下界面,从中可以看到Kconfig管理的基本层次结构。

上图展示的界面中展示了Kconfig的几个基本元素:
- 主目录:顶层的配置目录,全局唯一,通过关键字mainmenu进行配置;
- 目录:用于组织内核的特性或功能,通过关键字menu进行配置;
- 目录配置选项:可进行配置的目录,通过关键字menuconfig进行配置;
- 配置选项:对应具体的内核特性或功能,通过关键字config进行配置。
Kconfig语法
Kconfig语法支持的主要关键字如下:
- mainmenu
- menu/endmenu
- config
- menuconfig
- choice/endchoice
- if/endif
- comment
- source
mainmenu
mainmenu,只能出现在配置层次结构的顶部(且只能出现一次),用于为整个层次结构指定一个标题。因而该项只用于arch/arch/Kconfig中,因为这些文件表示配置层次结构的起始点。
menu
目录使用以下命令指定:
menu "string"
<attributes>
<configuration options>
endmenu
其中string是菜单的名称。menu和endmenu之间所有项都解释为该菜单的菜单项,自动地从菜单继承了依赖关系。
config
配置选项由关键字config开头,必须后接一个配置符号。
config <symbol>
<type-name> "Description"
<attributes>
类型名()表示选项的类型。如前所述,
- tristate类型的值为以下一种状态:y、n或m。其他的选项类型如下所示:
- bool用于返回y或者n的布尔查询,即是否选中该项。
- string查询一个字符串。
可以使用下列语法:
config <symbol>
<type-name>
prompt "Description"
menuconfig
关键字menuconfig用于定义一个配置符号和一个子菜单。
comment
comment在配置选项列表中创建一个注释。注释的文本会显示,但用户不能进行选择。
source
通过source,可以将更多的配置文件关联进来。这些配置文件的文本内容,将直接包含到嵌入的配置文件中进行解释。
Kernel Makefile
Kernel Makefile结构
完整的Linux内核Makefile包含以下5个部分:
- .config:内核配置文件;
- 顶层Makefile:位于内核源码顶层目录的Makefile;
- arch/$(SRCARCH)/Makefile:体系结构相关的Makefile;
- scripts/Makefile.* :所有kbuild Makefile都使用的公共规则;
- kbuild Makefiles:位于各个子目录下的Makefile。
顶层Makefile
Makefile,通过根据配置递归地编译子目录,并将编译结果合并到最终产品中,来生成内核本身和模块。
体系结构相关Makefile
体系结构相关的Makefile,在arch/arch/Makefile中,负责在编译期间必须遵守的与处理器相关的微妙之处,如特别的编译优化选项。该文件还实现了所有体系结构相关的make目标,此前在讨论help时提到过这些目标。
scripts/Makefile
scripts/Makefile.*包含了与一般编译、模块生成、各种实用程序的编译、从内核树删除目标文件和临时文件等任务相关的make规则。
驱动程序和子系统Makefile
内核源代码的各个子目录都包含了与特定驱动程序或子系统相关的Makefile(也采用了标准的语法)。
驱动程序和子系统目录中的Makefile用于根据.config中的配置来编译正确的文件,并将编译的流程导向到所要求的子目录中。
编译目标定义
obj-$(CONFIG_FOO) += foo.o
通常CONFIG_FOO有三种取值:y/m/n。
- obj-y:内置对象目标,这种情况下,目标文件会编译到内核镜像中;
- obj-m:可加载模块目标,最后会编译成内核模块;
- obj-n:目标不参与编译。
如果一个内核模块是从多个源文件构建的,则必须通过设置<mod_name>-y变量来告知kbuild模块构建依赖的源文件列表。
obj-$(CONFIG_FOO) += foo.o
foo-y := foo_comm.o foo_lib.o foo_main.o
编译外部模块
Linux编译外部模块的命令语法如下:
make -C <path_kernel_src> M=<path_external_module> [target]
- -C <kernel_path>:指定Linux内核源码路径。make执行时会实际切换到内核源码目录进行编译,并在完成时切换回来;
- M=<mod_path>:指定外部模块所在路径。通常都是在外部模块所在目录执行编译,因此这里基本都是使用当前目录;
- target:指定编译目标,默认为modules。
目前Kbuild支持的target如下:
- modules:编译外部模块,为默认的编译目标,可以不进行指定;
- modules_install:安装外部模块,默认安装路径为
/lib/modules/<kernel_release>/extra/;; - clean:清理外部编译的编译生成文件;
- help:列出外部模块编译可以使用的target。
导出内核头文件
Linux内核导出的头文件用于向用户空间程序描述内核提供的服务接口,这些头文件会被系统C程序库(例如glibc)包含来定义系统调用以及相关的数据结构。对于系统C程序库提供的头文件通常会放置在/usr/include目录下,而内核头文件则习惯性放在/usr/include的linux或asm子目录下。
Linux系统通过make headers_install安装内核头文件到合适的用户目录中,如下:
make headers_install ARCH=i386 INSTALL_HDR_PATH=/usr
- ARCH:指定目标体系架构;
- INSTALL_HDR_PATH:指定头文件安装路径。
相关参考
- Kernel Build System
- Kconfig语法简介
- 《深入理解Linux内核架构》