0x00 前言
在学习Linux内核过程中,我们经常会看到,驱动程序的注册过程通常分为以下两种步骤:
但是呢!模块初始化,中的模块化机制module,不仅用于支撑驱动的加载和卸载!!!
喵呜,那让我们来演示一下还能怎么用!
#include <linux/module.h>
#include <linux/init.h>
static int hello_init(void)
{
printk(KERN_ALERT "Hello World\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Bye Bye World\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("Dual BSD/GPL");
像上面这种模块代码有两种运行方式 一种是静态连接编译进内核,在系统启动过程初始化;另外一种是编译成可以动态加载的module,通过insmod动态加载重定位到内核。
方式一:静态编译进内核
在搜索“静态编译进内核”的过程中,果不其然出现了“驱动编译进内核”的关键词眼,于是乎,我进入文章大致看了一下,主要用到了Kconfig和Makefile,说是
Linux内核源码树的每个目录下都有两个文档Kconfig和Makefile。分布到各目录的Kconfig构成了一个分布式的内核配置数据库,每个Kconfig分别描述了所属目录源文档相关的内核配置菜单。在执行内核配置make menuconfig时,从Kconfig中读出菜单,用户选择后保存到.config的内核配置文档中。在内核编译时,主Makefile调用这个.config,就知道了用户的选择。这个内容说明了,Kconfig就是对应着内核的每级配置菜单。
看着有点懵,那我们就浅浅学一下Kconfig和Makefile! 点我浅浅学一下吧!
方式二:编译成动态加载的modlue
相信你点击“点我浅浅学一下”一路走下来,到这里也就明白了!文章终于形成了闭环。
0x01Kconfig
举例
我们从linux-3.10\drivers\usb\Makefile中截了一段
#
# Makefile for the kernel USB device drivers.
#
# Object files in subdirectories
obj-$(CONFIG_USB) += core/
obj-$(CONFIG_USB_DWC3) += dwc3/
obj-$(CONFIG_USB_MON) += mon/
这里有个小知识!
在makefile中obj-y就是编译进内核! obj-m就是编译成模组,生成.ko文件,在需要这个模组的时候,insmod动态加载这个mod(跟上文呼应了哦)
那么,obj-$这一串是什么??
其实,他是在_linux3.10.config_中定义好的! 我仔细找了找,却没有发现.config这个文件,我纳闷了,感觉这个体系还没有梳理清楚 于是我就在想,Makefile和Kconfig还有.config是啥关系??? 让我赶紧学一下 好,相信你点上方锚点跳转后,已经学习完毕了,那我们继续回到原来的问题,其实在学习三个东西关系的时候,也解决了这个问题。我们会在.config菜单中显示的选择,$这一串就是变量,会因我们的选择而定。同时我们也可以在.config中定义这个变量具体代表的内容,比如我定义CONFIG_TEST就是y,那么obj-$这一串就是编译到内核了。 $(xxxxxxxx)也被称为“引用变量”,引用的是Kconfig文件定义的config句柄的变量(config XXX)xxx就是变量,在这个条目下会有bool int 等定义变量类型
解决了刚才的“小插曲”(但是Makefile,config,Kconfig建议认真看,非常重要),下来 我们简单学习一下Kconfig的语法 然后接着简单学一下makefile!
0x04 Kconfig语法
首先确保你理解了这里,为了实现配置选项,每一个config都要有一个“主变量”,去控制,包括但不限于 bool ,tristate,int,hex,string
写到这有点累了,我先去看书了哈!2022/3/20 0:20 来了,不好意思,鸽子了一天,干别的事情去了
变量
config xxx “xxx”就是变量名,在makefile中将会被引用。而变量类型将又此config下具体内容被指定,如:
config CPU_S5PC100
bool "选项名"
同样,还有int,tristate(三态,编译,不便宜,和编译成模块)等等 变量的值将会在“选择界面”中被指定。
单一选项
Kconfig的作用是配置选项,我们会选中或不选中某些内容,那么我们就会通过一个bool变量来操控是否选中如下例子:
config CPU_S5PC100
bool "选项名"
select S5P_EXT_INT
select SAMSUNG_DMADEV
help
Enable S5PC100 CPU support
default y
如果bool为True则编译为N则不编译 select:代表着默认选择(进入当前配置界面自动选择) default:默认变量的值为y(True),如果不显式设置值,则在makefile中引用的话,会默认编译
Menu条目:
还记得我们在这里的比喻吗,kconfig,就像问路人,那让我们想想,编译内核的时候,什么时候的操作像问路人?欸,菜单界面对不对,没错,菜单界面实现例子如下:
menu "这是一个菜单"
config CAI_DAN_1
..............
config CAI_DAN_2
.............
endmenu
于是在配置界面你就会遇到这个:
这是一个菜单 --->
[] CAI_DAN_1
[] CAI_DAN_2
Kconfig语法我们暂时简单了解到这里。现在继续学Makefile吧
0x05 Makefile
啧啧啧!!!我本身不太会Makefile,但是我先尝试学Makefile基本语法,然后突然发现,跟我在Kernel用的Makefile不太一样??不搜不知道,一搜吓一跳,原来在内核中我见到的Makefile只是一个细小分类的一个功能,即Kbuild Makefile,才有我们上述的什么obj啥的
Linux Kernel Makefile分类
内核makefile.txt中将makefile分为 5部分,Kernel Makefile、ARCH Makefile、KBuild Makefile、.config文件以及scripts/Makefile.*
Kernel Makefile
Kernel Makefile 位于Linux 内核源代码的顶层目录,也叫 Top Makefile 。它主要用于指定编译Linux Kernel 目标文件(vmlinux )和模块(module )路径。它根据**.config文件决定了内核根目录下那些文件、子目录被编译进内核。对于内核或驱动开发人员来说,这个文件几乎不用任何修改。**(别忘了.config是要指定平台后执行指令或手动选择完成后生成的)
ARCH Makefile
ARCH Makefile 位于ARCH/$(ARCH)/Makefile ,是系统对应平台的Makefile 。Kernel Top Makefile 会包含这个文件来指定平台相关信息。ARCH Makefile同样根据.config文件,决定了ARCH/$(ARCH) 目录下 哪些文件、子目录被编译进内核 只有平台开发人员会关心这个文件。
Kbuild Makefile
从Linux 内核2.6 开始,Linux 内核的编译采用Kbuild 系统 ,这同过去的编译系统有很大的不同,Kbuild 系统使用Kbuild Makefile 来编译内核或模块。当Kernel Makefile 被解析完成后,Kbuild 会读取相关的Kbuild Makefile 进行内核或模块的编译。Kbuild Makefile 有特定的语法指定哪些编译进内核中、哪些编译为模块、及对应的源文件是什么等。内核及驱动开发人员需要编写这个Kbuild Makefile 文件
内核虽然有自己的构建系统Kbuild,但是kbuild 并不是什么新的东西,我们可以把kbuild 看作利用GNU Make组织的一套复杂的构建系统,虽然kbuild 也在Make 基础上做了适当的扩展,但是因为内核的复杂性,所以kbuild 要比一般的项目的Makefile 的组织要复杂的多。为了方便Linux开发者能够编写Makefile,kbuild考虑得不可谓不周到,比如,kbuild将所有与编译过程相关的共用规则和变量都提取到scripts 目录下的Makefile.build中,而具体的子目录下的 Makefile 文件则可以编写的非常简单与直接。 大多的Kbuild 文件的名字都是Makefile 。为了与其他Makefile 文件相区别,你也可以指定Kbuild Makefile 的名字为 Kbuild 。而且如果“Makefile ”和“Kbuild ”文件同时存在,则Kbuild 系统会使用“Kbuild ”文件。在这里,我们的 Kbuild Makefile 就是各个子目录的Makefile ,它的作用就是 指定当前目录下的文件,哪些被编译进当前目录的built-in.o、那些被编译成模块、那些不编译。
举例:
Kbuild Makefile 的一个最主要功能就是指定编译什么,这个功能是通过下面两个对象指定的obj-? 和xxx-objs 例如: obj-y += foo.o obj-m += abc.o 大多数情况下是这样: obj-$(CONFIG_DM9000) += dm9000.o 具体的 CONFIG_DM9000 是y 还是m取决于配置过程,配置过程产生.config文件,.config又产生auto.conf以及autoconf.h文件,包含在makefile文件里。 参考:http://blog.csdn.net/lizuobin2/article/details/51429937
关于Makefile以及Linux内核的Makefile的更多学习,单另起一期博客学习。
0x02 Makefile,Kconfig,.config的关系
首先我们抛出几个疑问!
- Linux内核如此庞大(几万个文件),目录又分为很多层,它是如何将各层目录下的文件关联起来的?
- Linux支持如此多的架构(X86、ARM、AVR、mips等等),为何我们在使用某一架构的芯片,比如RK3288时,其他架构的代码不会被编译?并且同为ARM架构下的其他系列SOC架构相关的代码不被编译?
- 在编译内核前,执行命令:make menuconfig的意义为何?
- Linux内核中各层目录下的Makefile文件和Kconfig文件是如何编写的。
首先,对于问题一哈:
我在google上找了好久,但是最好的解释只有这个了:
关于此问题,如果要准确的回答,估计只有真正的大神才能回答了,这里只讲自己的理解。Linux内核源码的代码管理是非常科学的,在Linux内核源码的顶层目录下,分配了相应的目录,在对应的目录下,代表这就一些功能或者是属性的集群,这样就实现了模块化,便于管理。 比如arch目录与平台架构相关、include目录存放着大量的内核头文件、drivers目录存放着各种驱动代码,比如显卡、网卡、USB总线、PCI总线等等、kernel目录存放着支持体系结构特有的诸如信号量处理和SMP之类特征的实现、mm目录存放着体系结构特有的内存管理程序的实现等等;然而在各个子目录下,又会进行细分,比如arch目录下,就存在和x86架构相关的目录x86、与ARM架构相关的目录arm、与MIPS目录相关的目录mips等等,以此类推。所以这就构成了一颗树形结构。 学习过数据结构的童鞋应该知道,对于一棵非标准树,还是有办法将其进行遍历的,只是算法比较复杂而已。 那么在Linux内核源码的这棵树,就是通过Kconfig文件建立各层子目录之间的连接,通过Makefile文件来选择各个目录下的对应的文件是否被编译,而.config文件就像是作为总控制台吧,控制着Makefile文件去编译指定的程序代码文件(主要是C和汇编)。而这一切控制关系是由Kconfig文件建立起来的。
在我理解而言,这其实就是在遍历树形结构时,用的一种方法,即指路法。每当我们在某个陌生的地方问路时,假设从A地到E地,情景通常如下:
非常形象! 那么,.config就相当于指路人,Kconfig就是问路人,Makefile就是具体实现的“两条腿” 但是,好像还没有解决我们刚才说的,找不到.config的问题哦…我们继续看下一个问题吧,说不定会解决。
问题二
当我们想要在特定架构下编译内核时,如rk3288,我们执行make firefly-rk3288-linux_defconfig或者make menuconfig,对,就是这个命令产生了.config文件!! make menuconfig 只是为了修改一些驱动模块和要编译的一些程序
通常执行顺序
先执行make firefly-rk3288-linux_defconfig,生成了一个基于Firefly平台的RK3288相关配置的.config文件 然后再执行命令make menuconfig选择一些模块代码进行编译
至此,我们得到了问题二的答案 没错,就是**基于firefly-rk3288-linux_defconfig文件生成了基础的,默认的.config配置文件,**此文件的内容就包含了架构相关的东西。所以在进行Linux内核源码的编译时,根据.config文件的基本配置,寻找架构相关的代码进行编译和设备相关的代码进行编译。总之以.config这个控制台为准。
问题三
其实上个问题已经大概回答了,make menuconfig就是以菜单的形式打开内核源码的树形结构,然后程序员在默认配置的基础上自行配置和选择需要编译的模块代码 这里有一段话,说的非常好:
其实能做这个工作的命令有很多,比如: ?make config:基于文本的为传统的配置界面,太复杂,不直观,不推荐使用。 ?#make xconfig:基于图形窗口模式的配置界面,直观明了,Xwindow界面下推荐使用。 ?make oldconfig:如果只想在原来内核配置的基础上修改一些小地方,会省去不少麻烦,可以使用。 ?make menuconfig:基于文本选单的配置界面,直观明了,字符终端下推荐使用。 大概好像这几种吧,其实还有一种就是,手动修改.config文件,呵呵!相信基本上没人会去干这种事的。
问题四
首先我们先确定一点,在Linux内核源码的各层目录下。都存在一个Kconfig文件和一个Makefile文件,.config文件存在顶层目录。如下图:
比如,我们在driver下新建了my_dr文件夹,然后在里面新建了hello.c
/driver/my_dr/hello
所以在my_dr目录下就会有他的Kconfig和makefile,具体图就不展示了,里面的内容应该是这样的: 一共有两个文件
obj-${CONFIG_HELLO} += hello.o
config HELLO
tristate "Test Driver Moudle"
help
help for local;
Makefile:
如果变量CONFIG_HELLO为真或假,则判断这是否将目录下的hello.c文件编译为hello.o文件 CONFIG_HELLO变量的值来自于.config文件的配置。.config的配置又来自于通过Kconfig文件的显式选择(就是通过菜单选择)
Kconfig:
Config是配置的关键字,Hello是配置项 ,**tristate为三态选择器,**此关键字在为_上层提供菜单配置时,有三种选择,分别是不编译、编译成内核镜像和编译成模块驱动_,除了这个关键字,还有另一个bool,等等(一会会学)这个不难理解,即为布尔类型,也有两种选择,编译或者不编译;help为帮助提示。这基本上是比较经典的格式了。所以通常在进行编写这样的配置时,通常的做法是_参考Linux内核源码中存在的Kconfig文件和Makefile文件_,然后模仿他们的格式进行编写。
/driver/my_dr
obj-y += hello/
menu "my Drivers"
source "drivers/my_dr/hello/Kconfig"
endmenu
Makefile: Makefile文件,一句话指引找到hello目录 Kconfig: my_dr目录下可能要存在很多驱动程序,所以必须要建立好一个菜单,进行对各个驱动程序的选择,所以第1行代码就算建立名为my Drivers的菜单;第5行代表可以在相对路径drivers/my_dr/hello/Kconfig找到Kconfig资源(注意,这里的路径是相对于Linux内核源码的顶层目录的)。后的第7行表示使用关键字endmenu结束文件。
/driver
这层目录也是同理,就不再细说了
最后再执行执行make menuconfig,就会看到一个GUI菜单,也就可以看到我们之前创建的驱动了!
至此。Makefile和Kconfig还有.config的关系我们已经大致理清楚了!!!
最后我们总结一下
Kconfig —> (每个源码目录下)提供选项 .config —> (源码顶层目录下)保存选择结果 Makefile—> (每个源码目录下)根据.config中的内容来告知编译系统如何编译 既然我们已经看到这里了,不如再梳理一下这些配置文件的流程 来学一下!
0x03 defconfig .config Kconfig Makefile make menuconfig流程
回忆一下
对了,deconfig我们没有提到 deconfig 一般由__平台厂商提供_,内核编译用做.config的参考_,注意:如果,缺少该文件,无法进行编译。 文件位于:/kernel/…/arch/arm/configs/xxx_defconfig Kconfig 分布在各个目录下的Kconfig构成了一个分布式的内核配置数据库,每个Kconfig分别描述了所属目录文件的内核配置菜单,在运行make menuconfig时,从Kconfig中读取配置菜单,用户配置完以后,保存到.config(顶层目录中) makefile 分布在各个目录下,用于继承编译 .config 在内核编译时,主Makefile调用这个.config,用于或者用于的内核编译配置。当我们在内核源码目录下输入make menuconfig时,在出现的菜单界面中选择一项时,它会自动更新.config相应项的值。如果我们没有选择,则会在.config问下插入一行注释(# CONFIG_TOUCHSCREEN_GT9xx is not set) 文件位于 :kernel/…/out/xxx_defconfig/.config
流程:
defconfig----->.config (第二步) kconfig------->.config---------->makefile(第三步)
make menuconfig(配置)流程
当执行了这个命令以后,系统干了多少事情? 涉及到了以下文件: 1.各层目录下的Kconfig文件 2.makfile文件 3.根目录的.config文件,还有config文件 4.autoconf.h文件 5.根目录下的script文件夹
1)scripts文件夹存放的是跟make menuconfig配置界面的图形绘制相关的文件,我们作为使用者无需关心这个文件夹的内容
2)当我们执行make menuconfig命令出现上述蓝色配置界面以前,系统帮我们做了以下工作: 首先系统会读取arch/$ARCH/目录下的Kconfig文件生成整个配置界面选项(Kconfig是整个linux配置机制的核心),那么ARCH环境变量的值等于多少呢? 它是由linux内核根目录下的makefile文件决定的,在makefile下有此环境变量的定义:
或者通过 make ARCH=arm menuconfig命令来生成配置界面,默认生成的界面是所有参数都是没有值的。比如教务处进行考试,考试科数可能有外语、语文、数学等科,这里相当于我们选择了arm科可进行考试,系统就会读取arm/arm/kconfig文件生成配置选项(选择了arm科的卷子),系统还提供了x86科、milps科等10几门功课的考试题 3)假设教务处比较“仁慈”,为了怕某些同学做不错试题,还给我们准备了一份参考答案(默认配置选项),存放在arch/$ARCH/configs下,对于arm科来说就是arch/arm/configs文件夹: 此文件夹中有许多选项,系统会读取哪个呢?内核默认会读取linux内核根目录下.config文件作为内核的默认选项(试题的参考答案),我们一般会根据开发板的类型从中选取一个与我们开发板最接近的系列到Linux内核根目录下(选择一个最接近的参考答案)
#cp arch/arm/configs/s3c2410_defconfig .config 4).config 假设教务处留了一个心眼,他提供的参考答案并不完全正确(.config文件与我们的板子并不是完全匹配),这时我们可以选择直接修改.config文件然后执行make menuconfig命令读取新的选项 但是一般我们不采取这个方案,我们选择在配置界面中通过空格、esc、回车选择某些选项选中或者不选中,最后保存退出的时候,Linux内核会把新的选项(正确的参考答案)更新到.config中,此时我们可以把.config重命名为其它文件保存起来(当你执行make distclean时系统会把.config文件删除),以后我们再配置内核时就不需要再去arch/arm/configs下考取相应的文件了,省去了重新配置的麻烦,直接将保存的.config文件复制为.config即可.
注意!如果你看着有点懵,一定要注意上述的几个类比!“选择了arm科卷子”–kconfig “参考答案”—configs “答案不完全正确“—解决方案 更新config,手动选择。
参考答案configs如下:
好了!这个板块到此为止,相信你收获了不少东西!现在让我们回到“最开始那一层”吧
参考: [1] CSDN - Lonwayne :《defconfig、 .config 、kconfig 与makefile和make menuconfig流程》 [2] CSDN - Linux知识积累 :《Linux内核中Makefile、Kconfig和.config的关系》 [3] CSDN - 赤壁淘沙:《Kconfig语法详解》 [4] CSDN - Linux学习之路:《Linux内核Makefile分析》
|