1.Kbuild本质:一个可扩展、可配置的Makefile框架,递归式Makefile,菜单式配置。
2.Kbuild构成:
- Makefile:顶层目录下的Makefile
- .config:内核的配置文件
- arch/S(ARCH)/Makefile:跟平台架构相关的Makefile
- scripts/Makefile.*:通用编译规则
- Kbuild Makefile:分布在各个子目录下
- Kconfig:配置菜单,定义每个configsymbol的属性 (类型、描述、依赖等)
3.Kconfig作用:
- 用来生成配置菜单,配置各种config symbol
- 生成对应的配置变量:CONFIG_XXX
- 每个目录下都有一个Kconfig文件
- 各个Kconfig文件通过source命令构建多级菜单
- 解析工具:scripts/kconfig/*conf
4.要想使用make menuconfig图形界面,第一次使用前要先安装图形库:apt-get install libncurses-dev。
5.Kconfig语法:
- menu:定义了菜单名字,必须以endmenu结束,menu后跟的名字会显示在菜单中,menu和endmenu之间的config选项都是菜单子条目,mainmenu用来定义主菜单
- config:用来定义菜单选项,选项类型可以是bool(y、n两种选择)、tristate(y、n、m三种选择)、int、hex、string。
- comment:注释,用于在图形化界面中显示一行注释
- if/endif:if ...enif: 就是if的意思,需要enif结束
- source:导入其他Kconfig文件的内容到当前位置,相当于宏定义替换,生成一个树型菜单
- help:帮助信息,内容会显示在帮助的help选项上
- default y: 表示当前选项默认是勾上的,也可以写为default m或者default n
- depends on:表示当前config菜单选项依赖另外一个菜单选项,只有所依赖的那个菜单选项被设置为y当前菜单选项才会在图形界面中显示出来并可供选择。depends on还可以是依赖于编译器的编译选项等(即在所依赖的编译选项开启时才可供选择)
- select:强反向依赖,比如在A选项下面使用了select B,那么当A模块被设置成y选中时,B模块也会自动被设置成y选中并且无法再被更改
- imply:弱反向依赖,在上述情况下B模块被选中后,还可以更改B的设置
- menuconfig:在使用config选项中使用depends on时,如果依赖的选项被选中,当前config选项会在同一个界面显示出来,而使用menuconfig选项之后会使用层次结构显示配置选项,即会转到一个新的界面显示依赖于menuconfig选项的config选项,有以下两种等价写法: 上图中的A0、A1必须紧跟着A,B0、B1必须紧跟着B,否则将不会按照层次结构显示新的菜单界面,而只会在当前界面显示
- choice:将多个类似的配置选项组合在一起,供用户单选或多选,以endchoice结束,示例如下:
6.最常用的进行编译选项设置并生成配置文件的命令使make menuconfig,但是下图所示的几种命令也可以使用,还有一种最基本的是make config命令,它是最基础的配置方式,完全基于文本。
生成的配置文件.config会被保存在源码根目录下。
7.Kbuild中的一些变量:
- build:定义在scripts/Kbuild.include里面,build := -f $(srctree)/scripts/Makefile.build obj,srctree在scripts/ Makefile中有定义,例如:srctree = $(CURDIR),$(CURDIR) 是一个内置的 Makefile 变量,用于表示当前工作目录的绝对路径。在Makefile中,obj变量通常用来指定目标对象的目录。
- obj-m:将当前文件编译为独立的模块
- obj-y:将当前文件编译进内核
- xxx-objs:一个模块依赖的多个源文件,如mconf-objs表示mconf依赖的所有源文件
8.scripts/Makefile.*各类规则文件:
- scripts/Makefile.build:通用规则,用来编译built-in.a、lib.a
- scripts/Makefile.lib:负责分析obj-y、obj-y和子目录中的subdir-y等
- scripts/Makefile.include:一些通用定义,被Makefile.*包含使用
- scripts/Makefile.host:编译各种主机工具
- scripts/Makefile.headerinst:头文件安装规则
- scripts/Makefile.modinst:模块install规则
- scripts/Makefile.modpost:模块编译,由.o和.mod生成module.ko
- scripts/Makefile.modsign:模块签名
- scripts/Makefile.clean:clean规则,make clean时调用
9.Kbuild Makefile工作流程:
- 根据ARCH变量,首先include arch/S(ARCH)/Makefile
- 读取.config文件:读取用户的各种配置变量
- 解析预定义目标、目标,构建依赖关系
- 编译各个模块或组件(使用scripts/Makefile.):将每个目录下的源文件编译为对应的.o目标文件,将.o目标文件归档为built-in.a
- 将所有对象链接成 vmlinux
- 编译模块…
10.vmlinux是Linux内核编译后的完整产物,以ELF格式存在,包含所有代码、数据和调试信息。它用于生成压缩的启动镜像,如zImage和bzImage,这些通常被引导加载程序使用来启动系统。vmlinux对开发者在调试和分析内核时至关重要。
11.zImage和bzImage是Linux内核的两种压缩映像文件,主要用于系统启动过程。zImage通常用于ARM架构,通过gzip压缩,适用于较小的内核,兼容性好。它被设计为位置无关码,可以在任何地址运行。bzImage意为“big zImage”,适用于x86架构,使用gzip压缩,支持更大的内核,通常解压到1MB以上的内存位置。两者都是在引导时由启动加载程序加载并解压,然后跳转到内核入口点执行。主要区别在于支持的内核大小和压缩算法,以及解压后的内存位置。
12.linux内核编译流程,uImage是可以通过u-boot启动的镜像:
vmlinux是Linux内核的可执行文件,包含了内核的所有代码和符号信息,负责管理系统资源和提供服务。通过编译Linux内核源代码生成,通常存储在内核源代码的arch/<architecture>/boot/目录下。启动时,vmlinux被引导加载程序(如GRUB)加载,并与压缩格式的内核映像(如bzImage或zImage)一起使用。它在系统调试中也非常重要,开发者可利用其符号信息进行内核调试和问题追踪。与上图对应的编译过程如下图:
第一个vmlinux是由built-in.o编译生成的原始elf文件,然后使用objcopy工具将其中的不必要的段去除,并压缩生成原始纯二进制内核镜像arch/arm/boot/Image,再利用gzip压缩工具将其再次压缩为arch/arm/compressed/piggy_data,并生成arch/arm/compressed/piggy.o。之后再将head.o、misc.o、decompress.o、piggy.o等一起链接为arch/arm/compressed/vmlinux,再次用objcopy工具将其中的不必要的段去除,并压缩生成纯二进制内核镜像arch/arm/boot/zImage。还可以根据需要利用mkimage工具将zImage进一步转换为uImage,如下图所示:
13.obj-y中的文件会被编译并链接到内核镜像(如vmlinux)中,成为内核的一部分,且在内核启动时自动加载,无法动态加载或卸载。也就是说,所有的obj-y文件被编译为.o文件后,和其他内核组件一起被链接,生成一个统一的内核镜像文件。而obj-m中的文件会被编译为独立的.ko文件(可加载内核模块),每个模块对应一个.ko文件。它们可根据需要通过insmod或modprobe命令动态加载,使用rmmod卸载,适用于内核运行时灵活扩展功能。
14.module.mod文件存储的信息是当前目标模块module所依赖的所有源文件、头文件、或者其他的模块,如:hello.mod文件只有一行内容drivers/char/hello.o表示hello模块依赖于drivers/char/hello.o,drivers/char/hello.o是由hello.c源文件编译而成的目标文件。modules.order文件是内核构建过程中的一个辅助文件,通常每个独立的内核构建目录中会有一个自己的 modules.order 文件,这个文件是在内核编译过程中由 make 命令自动生成的,用于记录在该特定目录下编译的所有内核模块及其编译顺序(如需要生成的所有.ko文件的信息)。.ko文件是Linux内核模块文件的扩展名,它表示"Kernel Object",是内核模块,Linux内核模块是可以动态加载或卸载的代码段,提供新的功能或驱动支持,而不需要重新启动系统。module.mod.c是Linux内核模块编译过程中生成的中间文件,包含模块的元信息、符号表和初始化函数引用,用于帮助生成最终的 .ko 文件(module.ko 文件是由module.o和module.mod.o链接而成的)。Module.symvers文件在Linux内核模块编译中用于记录导出符号及其CRC值(循环冗余校验),不同内核版本或配置可能生成不同的Module.symvers文件。外部模块可以共用一个Module.symvers文件,也可以有自己的版本,这取决于构建设置。
要想自己添加外部模块需要自己在顶层Makefile中添加形如obj-$(CONFIG_HELLO)+=hell.o的内容,然后在相应的Kconfig文件中添加是否开启hello模块的选项,然后就可以通过make menuconfig命令设置是否启用模块了。
15.想要安装模块时可以使用make modules_install命令,执行此命令时,kbuild系统会根据每个子目录下的modules.order 文件将相应的编译好的.ko文件拷贝到/lib/modules /$(uname -r)/kernel/目录下面(其中$(uname -r)是你的当前运行内核版本)进行安装,并在/lib/modules /$(uname -r)/目录下生成modules.dep文件,modules.dep文件是Linux内核模块依赖关系的数据库文件,它包含了系统中所有内核模块的依赖信息,记录了模块之间的依赖关系,例如哪些模块依赖于其他模块。
16.include/uapi目录包含了用户空间(userspace)程序可以使用的头文件。这些头文件定义了用户空间与内核空间之间共享的接口,包括系统调用、数据结构、常量等。这些文件是为了让用户空间程序在编译时能够访问到内核定义的接口,而无需关心内核的具体实现细节。include目录下的其他子目录,如include/linux、include/asm等,包含了内核空间(kernelspace)程序使用的头文件,这些头文件定义了内核内部使用的数据结构、宏、函数原型等,它们是内核实现其功能的基础,这些文件通常不适用于用户空间程序。在Linux内核的构建过程中,可以使用make headers_install命令来安装这些内核头文件,这个命令会将/include/uapi里面的头文件安装到/usr/include中供用户使用。