3.2 Kconfig,Makefile和.config文件三者的关系
1. 内核简介
1.1 内核版本及特点
Linux内核的版本号可以从源代码的顶层目录下的Makefile中看到,如图所示构成了linux的版本号:
其中的”VERSION”,”PATCHLEVEL”组成主版本号,比如5.3、5.4等。稳定的版本的主版本号用偶数表示(比如:5.2、5.4),每隔2~3年出现一个稳定版本。开发中的版本号用奇数来表示(比如5.1、5.3),他是下一个稳定版本的前身。
“SUBLEVL”称为次版本号,他不分奇偶,顺序递增。每隔1~2个月发布一个稳定版本。
“EXTRAVERSION”称为扩展版本号,他不分奇偶,顺序递增。每周发布几次扩展版本号,修正最新的稳定版本的问题。
1.2 获取内核源码
登陆Linux内核的官方网站www.kernel.org 可以看到如图所示内容:
上面标记了Linux内核的最新稳定版本、正在开发的测试版本。一般而言,各种补丁文件都是基于内核的某个正式版本生成的。使用内核补丁时,直接打补丁即可,补丁是迭代更新的。
1.3 内核启动过程简述
在裁剪内核之前,先了解他的启动过程。Linux的启动过程可以分为两部分:架构/开发板相关的引导流程,后续的通用启动流程。
1.3.1 内核引导阶段
内核映像被加载到内存并获得控制权之后,内核启动流程开始。通常,内核映像以压缩形式存储,并不是一个可以执行的内核。因此,内核阶段的首要工作是自解压内核映像。内核编译生成vmliunx后,通常会对其进行压缩,得到zImage(小内核,小于512KB)或bzImage(大内核,大于512KB)。在它们的头部嵌有解压缩程序。引导阶段通常使用汇编语言编写,他首先检查内核是是否支持当前架构的处理器,然后检查是否支持当前开发板。通过检查后,就为调用下一阶段的start_kernel函数作准备了,这主要分如下两个步骤:
② 调用C函数start_kernel之前的常规工作,包括复制数据段、清除BBS段、调用start_kernel函数。内核引导阶段如图所示:
1.3.2 内核初始化阶段
第二阶段的关键代码主要使用C语言编写。他进行内核初始化的全部工作,最后调用rest_init函数启动init过程,创建系统第一个进程:init进程。在这一阶段,仍有部分架构、开发板相关的代码。函数所在的位置:kernel/init/Main.c。内核初始化阶段的流程图如图所示:
2. 内核源码结构及Makefile分析
Linux内核庞大,但是这些文件的结构还是有章可循的,分别位于不同的目录下,各个目录功能相对独力。
目录名 |
|
Block |
|
Fs |
|
内核头文件,有基本文件(存放在include/linux/目录下)、各种驱动或功能部件的头文件(比如include/media、include/mtd、include/net)、各种体系相关的头文件(比如include/asm–arm/)。当配置内核后,include/asm是某个include/asm–xxx/(比如include/asm–arm/)的链接 |
|
Init |
内核的初始化代码(不是系统的引导代码),其中的main.c文件中的start_kernel函数是内核引导后运行的第一个函数 |
Ipc |
|
Lib |
|
Mm |
|
Net |
|
安全、秘钥相关的代码 |
|
内核文档 |
|
2.1 Linux内核Makefile分析
内核中的那些文件将被编译?他们是怎么被编译的?他们的连接顺序如何确定?哪个文件在最前边?那些文件或者函数先执行?这些都是通过Makefile来管理的。从最简单的角度总结Makefile的作用,有以下3点:
1)决定编译哪些文件
2)怎样编译这些文件
Linux内核源码中含有很多个Makefile文件,这些Makefile文件又要包含其他一些文件。这些文件构成了linux的Makefile体系,可以分为表中的5类。
描述 |
|
顶层Makefile |
|
配置文件,在配置内核时生成。所有Makefile文件(包括顶层目录及各级子目录)都是根据.config来决定使用哪些文件 |
|
内核文档中Documentation/kbuild/makefiles.rst对Makefile有一定的讲解。以下根据前边总结的Makefile的三大作用分析这5类文件。
2.1.1决定编译那些文件
Linux内核的编译过程从顶层Makefile开始,然后递归地进入各级子目录调用他们的Makefile,分为三个步骤。
1)顶层Makefile决定内核根目录下哪些子目录将被编进内核。
2)Arch/$(ARCH)/Makefile决定Arch/$(ARCH)目录下哪些文件、哪些目录将被编进内核。
3)各级子目录下的Makefile决定所在目录下哪些文件将被编进内核,哪些文件将被编进模块(即驱动程序),进入哪些子目录继续调用他们的Makefile。
在顶层Makefile中,可以看到如图所示:
可见,顶层Makefile将这9个子目录分为7类:init-y.drivers-y,drivers-$(CONFIG_SAMPLES),net-y,libs-y,core-y,virt-y。除去include目录和后边两个不包含内核代码的目录外,还有一个arch目录没有出现在内核中。他在arch/$(ARCH)/Makefile中被包含进内核,在顶层Makefile中直接包含了这个Makefile,如下图2-2所示:
对于SRCARCH变量,可以在执行make命令时传入。比如“make SRCARCH=…”。
对于步骤2)的arch/$(ARCH)/Makefile,在arch/arm/Makefile中可以看见如下内容:
head-y := arch/arm/kernel/head$(MMUEXT).o
除了前边的7类子目录意外,又出现了另一类:head-y,不过他直接以文件名出现。
如图所示,第297行,扩展了libs-y的内容,这些都是体系结构相关的目录。上图中$(…)在配置内核时定义,他的值有三种:
Y:表示编进内核
M:表示编为模块
空:表示不使用
编译内核时,将依次进入:init-y,core-y,libs-y.drivers-y和net-y所列出的目录中执行他们的Makefile,每一个目录都会生成一个built-in.o(libs-y所列目录下,有可能生成lib.a文件)。最后,head-y所表示的文件将和这些built-in.o、lib.a一起被连接成内核映像文件vmlinux。
对于步骤3)在配置内核时,生成配置文件.config(具体的配置过程在下文的config分析中讲述)。内核顶层Makefile间接包含.config文件,以后就根据.config中定义的各个变量决定编译哪些文件。之所以说是间接包含,是因为包含的是include/config/auto.conf文件,而他只是将.config文件中的注释去掉,并根据顶层Makefile中定义的变量增加了一些变量而已。如图所示:
在include/config/auto.conf文件中,变量的值主要有两类:“y”和“m”。各级子目录的Makefile使用这些变量来决定哪些文件被编进内核中,哪些文件被编成模块(即驱动程序),要进入哪些下一级子目录继续编译,这要通过以下四种方法来确定:
Obj-y中定义的.o文件由当前目录下的.c或.s文件编译生成,他们连同下级子目录的built.o文件被组合成(使用“$(LD)-r”命令)当前目录下的built-in.o文件,这个built-in.o被上一层的Makefile文件使用。
Obj-y中各个.o文件的顺序是有意义的,因为内核中使用moduel_init()或initcall定义的函数将按照他们的连接顺序被调用。
Obj-m中定义的.o文件由当前目录下的.c或.s文件编译生成,他们不会被编译进built-in.o中,而是被编成可加载模块。
一个模块可以由一个或几个.o文件组成。对于只有一个源文件的模块,在obj-m中直接增加他的.o文件即可。对于有多个源文件的模块,除了在obj-m中增加一个.o文件外,还要定义一个<modem_name>-objs变量来告诉Makefile这个.o文件由哪些文件组成。
例:当下边的CONFIG_ISDN在.config文件中被定义为m时,将会生成一个isdn.o文件,他由isdn-objs中定义的…三个文件组合,而isdn.o最后被知错成isdn.ko模块。
Obj-$(CONFIG_ISDN) += isdn.o
Isdn-objs F isdn_net_lib.o isdn_v110.o isdn_commom.o
就这样,本应该被编译成isdn_net_lib.o isdn_v110.o isdn_commom.o的isdn_net_lib.c isdn_v110.c isdn_commom.c文件,被编译成了isdn.o文件。
Lib-y用来定义那些文件被编译成库文件。Lib-y定义的.o文件由当前目录下的.c或.s文件编译生成,他们被打包成当前目录下的一个库文件lib.a,同时出现在obj-y、lib-y中的.o文件不会被包含进lib.a中。要把这两个lib,a编进内核中,需要在顶层Makefile中libs-y变量中列出当前目录,要编成库文件的内核代码一般都在这两个目录下:lib/、arch/$(ARCH)/lib/。Obj-y、obj-m还可以用来指定要进入的下一层子目录
Linux中的一个Makefile文件只负责生成当前目录下的目标文件,子目录下的目标文件由子目录下的Makefile生成。Linux的编译系统会自动进入这些子目录调用他们的Makefile,知识在这之前需要制定这些子目录。这就要用到obj-y、obj-m,只要在其中增加这些子目录名即可。
例:obj-$(CONFIG_JFFS2_FS) +=jffs2/
2.1.2 怎样编译这些文件
即编译选项、连接选项是什么。这些选项分为三类:全局的,适用于整个内核代码树;局部的,仅适应于某个Makefile中的所有文件;个体的,仅适用于某个文件。全局选项在顶层Makefile和arch/$(ARCH)/Makefile中定义,这些选项的名称是:CFLAGS、AFLAGS、LDFLAGS、ARFLAGS,他们分别是编译C文件的选项,编译汇编文件的选项,连接文件的选项,制作库文件的选项。需要使用局部选项时,他们在各个子目录中定义,名称为EXTRA_CFLAGS、…、用途和上边的相同,只针对当前Makefile中的所有文件。个体编译:可以使用CFLAGS_$@…。$@表示某个文件目标文件名,例:CFLAGS_aha152x.0
2.1.3 怎样链接这些文件
前边分析了有哪些文件需要编进内核,顶层Makefile和arch/$(ARCH)/Makefile定义了7类目录。在顶层目录中,这些目录名的后面直接加上built-in.o或者lib.a,表示要连接进内核的文件,如下图所示:
Patsubst是个字符串处理函数,用法如下:$(patsubse pattern,replacement,text)。表示寻找“text”中符合格式“pattern”的字,用“replacement”替换他们。比如上边的init-y初值为“init/”,经过交换后,“init-y”变成“init/built-in.o”。顶层Makefile中,再往下看。如图所示
表示alldirs构成内核映像文件vmlinux的目标文件,由上图可知,这些目标文件的顺序。对于ARM体系,连接脚本是arch/arm/kernel/vmlinux.lds,他由arch/arm/kernel/vmlinux.lds.S文件生成,规则在script/Makefile.build中。
下边对分析Makefile做一下总结:配置文件.config中定义了一系列的变量,Makefile将结合他们来决定哪些文件被编进内核,哪些文件被编成模块,涉及哪些子目录。顶层Makefile和arch/$(ARCH)/Makefie决定根目录下哪些子目录、arch/$(ARCH)目录下哪些文件和目录将被编进内核。最后,各级子目录下的Makefile决定所在目录下哪些文件将被编进内核,哪些温江将被编成模块,进入哪些子目录继续调用他们的Makefile。顶层Makefile和arch/$(ARCH)/Makefile设置了可以影响所有文件的编译、连接选项:CFLAGS,AFLAGS,LDFLAGS,ARFLAGS.各级子目录下的Makefile中可以设置能够影响当前目录下所有文件的编译,连接选项。顶层Makefile按照一定的顺序组织文件,根据连接脚本arch/$(ARCH)/kernel/vmlinux.lds生成内核映象文件。
3. Kconfig分析
在内核目录下执行“make menuconfig ARCH=arm“时,就会看到一个如图3.1所示的菜单,这就是内核的配置界面。通过配置界面,可以选择芯片类型,选择需要支持的文件系统,去除不需要的选项等,这就称为配置内核。也就其他形式的配置方式,比如”make config“命令启动字符配置界面。”make xconfig“命令启动X-windows图形配置界面。有兴趣可以尝试一下后两种方式。
所有的配置工具都是通过读取arch/$(ARCH)/Kconfig文件来生成配置界面,这个文件是所有配置文件的总入口,他会包含其他目录的Kconfig文件。
3.1 Kconfig文件的基本要素
3.1.1 Config条目
Config条目常备其他条目包含,用来生成菜单、进行多项选择等。Config条目用来配置一个选项,或者说,它用来生成一个变量,这个变量会连用他的值被写进.config文件中。举个例子:
选自fs/Kconfig文件,用于配置CONFIG_TMPFS_POSIX_ACL选项。第128行中,config是关键字,表示一个配置项的开始;TMPFS_POSIX_ACL是配置项的名称,省略了前缀“CONFIG_”。129行中,bool表示变量类型。一共有5中类型:bool,tristate,string,hex,int。Bool之后的字符串是提示信息。130行表示依赖关系,只有TMPFS配置选项被选中时,才能设置当前配置选。注意:如果依赖条件不满足,则他取默认值。131和132行表示,当TMPFS_POSIX_ACL被选中时,配置TMPFS_XATTR和FS_POSIX_ACL也会被自动选中。当一行的缩进距离比第一行帮助信息的缩进距离小时,表示帮助信息结束。
3.2.2 Menu条目
109行,Menu之后的字符串是菜单名,“menu”和“endmenu”之间有很多config条目,在配置界面会出现menu名称字样,移动光标选中它回车进入,就会看到这些config条目。
3.2.3 Choice条目
Choice条目将多个类似的选项组合在一起,供用户单选或多选。实际使用中,也是在“choice”和“endchoice”之间定义多个config条目,比如init/Kconfig中有如下代码。
第119行中,prompt “Kernel compression mode”给出提示消息“Kernel compression mode”,光标进入后,就能看到多个config条目定义的配置选项。Choice条目中,定义的变量类型只能有两种:bool和tristate,并且不能同时有着两种类型的变量。对于bool类型的choice条目,只能在多个选项中选择一个。对于tristate类型的chioce条目,要么就把多个(可以是一个)选项都设为m,要么就像bool类型的条目一样,只能选择一个。
3.2.4 Select条目
反向依赖关系,该选项选中时,同时选中select后面定义的那一项。
3.2.5 Depend条目
该选项依赖于另一个选项,只有当依赖项被选中时,当前配置项的提示信息才会出现,才能设置当前配置项。如图所示:
3.2.6 Comment条目
用于定义一些帮助信息,会显示在配置界面上,也会在.config中以注释的形式显示
3.2.7 Source条目
3.2 Kconfig,Makefile和.config文件三者的关系
简单来说就是去饭店点菜:Kconfig是菜单,Makefile是做法,.config就是你点的菜。
Kconfig:一个文本形式的文件,内核的配置菜单。
.config:编译内核所依据的配置。
Linux 内核源码树的每个目录下都有两个文档Kconfig和Makefile。分布到各目录的Kconfig构成了一个分布式的内核配置数据库,每个 Kconfig分别描述了所属目录源文档相关的内核配置菜单。在执行内核配置make menuconfig时,从Kconfig中读出菜单,用户选择后保存到.config的内核配置文档中。在内核编译时,主Makefile调用这 个.config,就知道了用户的选择。这个内容说明了,Kconfig就是对应着内核的每级配置菜单。
1.Makefile
(1)直接编译
obj-y += xxx.o
表示由xxx.c或xxx.s编译得到xxx.o并直接编进内核。
(2)条件编译
obj -$(CONFIG_HELLO) += xxx.o
根据.config文件的CONFIG_XXX来决定文件是否编进内核。
(3)模块编译
obj-m +=xxx.o
表示xxx作为模块编译,即执行make modules时才会被编译。
2、Kconfig
每个config菜单项都有类型定义: bool布尔类型、 tristate三态(内建、模块、移除)、string字符串、 hex十六进制、integer整型。
作用:决定make menuconfig时展示的菜单项
3、.config
通过前俩个文件的分析,.config的含义已经很清晰:内核编译参考文件,查看里面内容可以知道哪些驱动被编译进内核。
配置内核方式有3种(任选其一):
(1)make menuconfig
(3)直接修改.config
注意: 如果直接修改.config,不一定会生效,因为有些配置可能存在依赖关系,make时会根据依赖关系,进行规则的检查,直接修改.config有时无效,所以不推荐直接修改。
4.Linux内核配置选项
4.1 菜单形式的配置界面操作方法
在源码目录下执行make menuconfig ARCH=arm进入内核配置界面,如图所示:
4.2 配置界面主菜单类别
表中讲解了主菜单的类别,可以根据自己所要设置的功能进入某个菜单,然后根据其中各个选项的帮助信息进行配置。
配置界面主菜单 |
描述 |
代码成熟度选项:用于包含一些正在开发或者不成熟的代码、驱动程序。 |
|
General setup |
|
可加载模块支持:一般都会打开Enable loadable module support、Module unloading、Automatic kernel module loading |
|
Bus support |
PCMCIA/CardBus总线支持 |
Kernel Features |
|
Boot options |
|
Networking |
网络协议选项:一般都选择”Networking support“以支持网络功能,选择”Packet socket“以支持socket接口功能,选择”TCP/IP networking”以支持TCP/IP网络协议。通常可以选择”Networking support“后,使用默认设置 |
设备驱动程序:几乎包含Linux的所有的驱动程序 |
|
File system |
|
调试内核时的各种选项 |
|
加密选项 |
|
库子程序:比如CRC32检验函数、zlib压缩函数等 |
4.3 “System Type”菜单:系统类型
对于arm平台(在顶层Makefile中修改“ARCH”=arm),执行”make menuconfig”后,或者执行”make menuconfig ARCH=arm”后在配置界面可以看到“System Type“字样,进入他的另一个界面,如图所示:
进入ARM system type用来选择体系结构,进入之后选择Qualcomm MSM,如图所示:
查看帮助信息可以知道它对应的是:CONFIG_ARCH_QCOM,如图所示:
4.4 “Device Drivers”菜单:设备驱动程序
执行”make menuconfig ARCH=arm”后在配置界面可以看到“Device Drivers”界面,如图所示:
图中的各个子菜单和内核源码drivers/目录下各个子目录一一对应.如表所示。在配置过程中可以参考这个表格找到对应的配置选项:在添加新驱动时,也可以参考它来决定代码放在哪个目录下。表中的各个子菜单与内核源码drivers/目录下的各个子目录一一对应,如表所示:
描述 |
|
对应drivers/pnp目录,支持各种即插即用的设备 |
|
Serial ATA and paraller ATA |
对应devices/ata目录,支持SATA与PATA设备 |
对应devices/md目录,表示多设备支持(RAID和LVM)。RAID和LVM的功能是使多个物理设备组建成一个单独的逻辑磁盘 |
|
对应drivers/char目录,他包含各种字符设备的驱动程序。串口的配置选项也是从这个菜单调用的,但是串口的代码在drivers/serial目录下 |
|
对应devices/hwmon目录。当前主板大多数都有一个监控硬盘健康的设备用于监视温度/电压/风扇转速等,这些功能需要I2C支持,在嵌入式开发板中一般用不到 |
|
Misc devices |
|
Multifunction device drivers |
|
LED devices |
|
Multimedia devices |
|
Sound |
对应sound/目录(他不在devices/目录下),用来支持各种声卡 |
HID devices |
对应drivers/hid目录,用来支持各种USB-HID设备,或者符合USB-HID规范的设备(比如蓝牙设备)。HID表示human interface device,比如各种USB接口的鼠标/键盘/游戏杆/手写板等输入设备 |
USB support |
对应devices/usb目录,包括各种USB Host和USB Devices设备 |
MMN/SD card support |
对应drivers/mmc目录,用来支持各种MMC/SD卡 |
Real Time Clock |
5.Kernel裁剪方法
前边已经讲了Kconfig、.config、Makefile和make menuconfig的关系,在这里便可以很轻易的知道.config文件的修改方法。在内核源码目录下执行make menuconfig ARCH=arm进入到内核配置界面后,根据自己的配置要求将不用的选项配置成n,保存退出配置界面,这时就会生成一个新的.config文件,与之前的.config文件进行对比(可以使用Beyond Compare进行对比),找出差异项。举个例子,在内核源码目录下执行make menuconfig ARCH=arm,将Device Drivers —> PCI support —> PCI Express Port Bus support置为不参与编译,如图所示。
将Device Drivers —> PCI support —> PCI Express Port Bus support修改之后保存退出配置界面,会生成对应的.config文件将之前的.config文件覆盖,对比现在和之前的.config文件,如图所示:
我们可以发现CONFIG_PCIEPORTBUS=y,CONFIG_PCIEAER=y,CONFIG_PCIEASPM=y,CONFIG_PCIEASPM_POWER_SUPERSAVE=y,CONFIG_PCIE_PME=y这5个配置选项被修改。
在源码下进入对应平台目录下,进入目录kernel/msm-5.4/arch/arm/configs/下,在配置文件中修改.config文件中的差异项,即可根据自己的配置要求配置出选项。某些差异项是在defconfig文件中找不到的,这就要用到其他方法来配置该配置选项。举个例子,如上的CONFIG_PCIEPORTBUS=y,将CONFIG_PCIEPORTBUS=y修改为CONFIG_PCIEPORTBUS=y is not set即可,如所示。再依次找到CONFIG_PCIEAER=y,CONFIG_PCIEASPM=y,CONFIG_PCIEASPM_POWER_SUPERSAVE=y,CONFIG_PCIE_PME=y,将他们分别修改为#CONFIG_XXXXXXX is not set即可,这样我们便是将Device Drivers —> PCI support —> PCI Express Port Bus support配置成不编译进入内核。
参考文献:
[1] 韦东山.嵌入式LINUX应用开发完全手册.人民邮电出版社
[2] 陈皓.跟我一起学makefile
[3] [J/OL] .Linux内核架构及内核裁剪.https://blog.csdn.net/u012516571/article/details/79649828
[4][J/OL]. linux内核裁剪的具体过程和方法.https://blog.csdn.net/u011124985/article/details/80453772
[5][J/OL]. linux4.10.8 内核移植(三).https://www.cnblogs.com/kele-dad/p/7107544.html
原文地址:https://blog.csdn.net/qq_42723832/article/details/131925964
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_23760.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!