Makefile与Kconfig和.config使用

这里所说的只是一个大概的了解,对初学的人来说看了心中会有个基本的概念,在linux2.6.x/Documentation/kbuild目录下有周详的介绍有关kconfig、makefile的知识。        

        内核源码树的目录下都有两个文档 Kconfig (2.4版本是Config.in)和 Makefile。分布到各目录的Kconfig构成了一个分布式的内核配置数据库,每个Kconfig分别描述了所属目录源文档相关的内核配置菜单。   在内核配置make menuconfig时,从Kconfig中读出菜单,用户选择后保存到.config的内核配置文档中。并且生成相应的C语言格式的文件,在/include/linux/version.h,autoconfig.h。当 make distclean 时会删除掉这些文件。 在内核编译make zImage时,主Makefile调用这个.config,就知道了用户的选择。

        上面的内容说明了,Kconfig就是对应着内核的配置菜单。假如要想添加新的驱动到内核的源码中,能够修改Kconfig,这样就能够选择这个驱动,假如想使这个驱动被编译,要修改Makefile。所以,添加新的驱动时需要修改的文档有两种(注意不只是两个)*Kconfig      *Makefile。

 

Kconfig

1.先了解一下Kconfig的语法:

一个典型的内核配置菜单如下:

menu "Network device support"

config NETDEVICES

bool "Enable Net Devices"

depends on NET

default y

help

This is help desciption。

...

endmenu

包含在menu/endmenu中的内容会成为Network device support的子菜单。每一个子菜单项都是由config来定义的。congfig下方的那些bool、depends on、default、help等为config的属性,用于定义该菜单项的类型、依赖项、默认值、帮助信息等。

2. 补充说明一下类型定义部分:

每个config菜单项都要有类型定义: bool布尔类型、 tristate三态(内建、模块、移除)、string字符串、 hex十六进制、 integer整型。

例如:

config HELLO_MODULE

bool "hello test module"

bool 类型的只能选中或不选中,显示为[ ]; tristate类型的菜单项多了编译成内核模块的选项,显示为< > , 假如选择编译成内核模块,则会在.config中生成一个 CONFIG_HELLO_MODULE=m的配置,假如选择内建,就是直接编译成内核映像,就会在.config中生成一个 CONFIG_HELLO_MODULE=y的配置. hex十六进制类型显示为( )。

3. 目录层次迭代

在Kconfig中有类似语句:source "drivers/usb/Kconfig"

用来包含(或嵌套)新的Kconfig文件,这样便可以使各个目录管理各自的配置内容,而不必把那些配置都写在同一个文件里,方便修改和管理。

 

Makefile

2.6内核的Makefile分为5个组成部分:

1. 最顶层的Makefile

2. 内核的.config配置文件

3. 在arch/$(ARCH) 目录下的体系结构相关的Makefile

4. 在scripts目录下的 Makefile.* 文件,是一些Makefile的通用规则

5. 各级目录下的大概约500个kbuild Makefile文件

顶层的Makefile文件读取 .config文件的内容,并总体上负责build内核和模块。Arch Makefile则提供补充体系结构相关的信息。 scripts目录下的Makefile文件包含了所有用来根据kbuild Makefile 构建内核所需的定义和规则。

 

Kbuild Makefile

对于Makefiles的不同组成部分,有一些不同的语法规则。针对的对象也不同,对于大部分内核模块或设备驱动的开发者和使用者来说,最常接触到的就是各层目录下基于kbuild架构的kbuild Makefile文件。Kbuild Makefile核心内容主要包括:

1.目标定义

目标定义就是用来定义哪些内容要做为模块编译,哪些要编译链接进内核。如:

obj-y += foo.o

表示要由foo.c或者foo.s文件编译得到 foo.o并链接进内核,而obj-m则表示该文件要作为模块编译。除了y,m以外的obj-x形式的目标都不会被编译。而更常见的做法是根据.config文件的CONFIG_ 变量来决定文件的编译方式(该变量如何起作用见文末另一篇文章的链接),如:obj-$(CONFIG_EXT2) += ext2.o 。除了obj-形式的目标以外,还有lib-y library库,hostprogs-y 主机程序等目标,但是基本都应用在特定的目录和场合下。

2.多文件模块的定义

最简单的kbuild Makefile如上一节一句话的形式就够了,如果一个模块由多个文件组成,那么稍微复杂一些,采用模块名加 –objs后缀或者 –y后缀的形式来定义模块的组成文件。如以下例子:

obj-$(CONFIG_EXT2) += ext2.o

ext2-y := balloc.o bitmap.o

或者写成如-objs的形式:

obj-$(CONFIG_EXT2) += ext2.o

ext2-objs := balloc.o bitmap.o

模块的名字为ext2,如果CONFIG_EXT2 的值是m,由balloc.o和bitmap.o两个目标文件最终链接生成ext2.o 直至ext2.ko文件,如果CONFIG_EXT2的值是y,生成的 ext2.o将被链接进built-in.o最终链接进内核。

3.目录层次的迭代

如下例:

obj-$(CONFIG_EXT2) += ext2/

如果CONFIG_EXT2 的值为y或m,kbuild将会将ext2目录列入向下迭代的目标中。

 

模块的编译

编译模块的时候,你可以将模块放在代码树中,用Make modules的方式来编译你的模块,

此时Makefile内容很简单,例如:obj-$(CONFIG_EXT2) += ext2.o 即可。

 

你也可以将模块相关文件目录放在代码树以外的位置,用如下命令来编译模块:

make -C <path to kernel src> M=$PWD modules

 

‘-C’指定代码树的位置,M=$PWD 或 M=`PWD` 告诉kbuild回到当前目录来执行build操作。

当然,我们也可以为其写一个Makefile,这里介绍一个较通用的Makefile(2.6版本):

# Makefile2.6

ifneq ($(KERNELRELEASE),)

#kbuild syntax. dependency relationshsip of files and target modules are listed here.

obj-m := hello.o

hello-objs := hello.o

else

PWD := $(shell pwd)

KDIR := /lib/modules/$(shell uname -r)/build

all:

$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:

rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions

endif

此例子驱动目录叫做hello,实际中需要将下面的hello换成自己的目录名称。其中代码树路径是自动获取的。之后在目录下直接执行make命令即可,不再用敲上面一大长串命令。在添加新驱动时,需要创建Kconfig、Makefile文件,且需要修改父目录Kconfig、Makefile这两个文件以便将自己的驱动包含进去。

 

详解 Makefile

        假如有一个上百个文件的代码构成的项目,如果只是对其中一个或少数几个文件进行了修改,若用gcc编译工具就不得不把整个项目里的文件重新编译一遍。编译过程分为编译、汇编链接等阶段。其中,编译阶段仅检查语法错误以及函数与变量的声明是否正确,在链接阶段则主要完成函数链接和全局变量的链接。因此,没有改动的源代码根本不需要重新编译,而只需将它们重新链接进去就可以了。

        make工程管理器是一个“自动编译管理器”,这里的“自动”是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量。同时,它通过读入makefile文件的内容来执行大量的编译工作。用户只需编写一次简单的编译语句就可以了。

1. 编写makefile文件

makefile的书写规则:

目标文件:依赖文件

(TAB)产生目标文件的命令

注意:

gcc前面的空格是用TAB生成,而不是按空格键。

 

2. 使用makefile编译文件的操作步骤

编写代码,分成多个文件

例如:

分成文件 2-5-main.c,2-5-fun1.c,2-5-fun2.c,my.h

编辑makefile文件

例如:

2-5:2-5-main.o 2-5-fun1.o 2-5-fun2.c

gcc 2-5-main.o 2-5-fun1.o 2-5-fun2.o -o 2-5

2-5-main.o:2-5-main.c my.h

gcc 2-5-main.c -c

2-5-fun1.o:2-5-fun1.c

gcc 2-5-fun1.c -c

2-5-fun2.o:2-5-fun2.c

gcc 2-5-fun2.c -c

 

在编辑makefile文件时可以自定义一些变量来代表一串文字,例如

CC=gcc

objects=2-5-main.o 2-5-fun1.o 2-5-fun2.o

2-5:$(objects)

$(CC) $(objects) -o 2-6

..........

区别一下:

=     :左侧是变量,右侧是值,值可以定义于文件任何处(一般取最后一次定义处)

:=    :左侧是变量,右侧是值,值只能定义于文件之前(一般取之前最后一个)

?=    :左侧是变量,右侧是值,如果变量未定义则赋值,如果定义了则什么都不做

+=    :追加

 

在这个makefile文件中,目标文件(target)包含可执行文件2-5和中间目标文件(*.o)。依赖文件(prerequisites)包含冒号后面大的那些.c文件和.h文件。每一个.o文件都有一组依赖文件,而这些.o文件又是可执行文件2-5的依赖文件。依赖关系的实质是说明目标文件由哪些文件生成。在定义好依赖关系后,后续的代码定义如何生成目标文件的操作系统命令,其一定要以一个Tab键作为开头。

用make命令编译

编写好makefile文件后,用make命令编译,指定makefile文件需要在make后面加参数-f,例如:

make -f makefile2-5

 

在默认方式下,只输入make命令,其会做如下工作:

1) make会在当前目录下查找名字为makefile 的文件或“makefile文件夹”的文件。如果找到,它会找文件中的第一个目标文件(target)。如果该目标文件不存在,或是该目标文件所依赖的.o文件的修改时间比它新,就会执行后面所定义的命令来生成第一个目标文件。

2) 如果第一个目标文件所依赖的.o文件也存在,make会在当前文件中找目标为.o文件的依赖性,如果找到,则会根据规则生成.o文件。

3) c文件和h文件如果存在,make会生成.o文件,然后再用.o文件生成make的最终结果,也就是可执行文件,即第一个目标文件。

这就是整个make的依赖性,make会一层又一层地去找文件的依赖性,直到最终编译出第一个目标文件。在寻找的过程中,如果出现错误,则make会直接退出,并报错。对所定义的命令的错误,或是编译不成功,make就不会处理。如果在make找到了依赖关系之后,冒号后面的文件不存在,make仍不工作(这就是伪目标.PHONY,需要make 伪目标 来执行)。

<think>我们正在讨论Linux内核配置系统中的三个核心组件:menuconfigKconfig.config。根据用户提供的引用材料,我们可以总结如下: 1. **Kconfig**:这是分布在内核源代码树中的配置文件(通常每个目录都有一个),用于定义配置选项的结构、属性、依赖关系等。例如,在`drivers/block/Kconfig`中添加一个新的配置项(如`config ANDREA_BLOCK_TEST`),这个选项就会在menuconfig界面中出现[^3]。 2. **.config**:这是用户配置结果的存储文件,位于内核源码的顶层目录下(隐藏文件)。它记录了用户通过menuconfig界面选择的具体配置值(如`CONFIG_USB=y`或`CONFIG_ANDREA_BLOCK_TEST=m`)。编译时,Makefile会读取这个文件来决定哪些代码编译进内核、哪些编译为模块、哪些不编译[^2][^3]。 3. **menuconfig**:这是一个基于文本的图形化配置工具(需要ncurses库支持),它读取所有Kconfig文件生成菜单结构,并加载`.config`文件中的值来初始化每个选项的状态。用户通过这个界面修改配置后,保存时会更新`.config`文件[^1][^3]。 ### 三者关系总结: - **Kconfig是“蓝图”**:定义配置选项的元数据(如名称、类型、依赖、帮助文本等)。 - **menuconfig是“交互界面”**:基于Kconfig的蓝图生成菜单,并允许用户修改配置,最终将结果写入.config。 - **.config是“最终配置”**:存储用户的实际选择,直接影响内核编译行为。 ### 工作流程示例(引用[3]的实验): 1. **修改Kconfig**:在`drivers/block/Kconfig`末尾添加: ```kconfig config ANDREA_BLOCK_TEST tristate "hello hello hello" ``` 这定义了一个新的三态(`y/m/n`)配置项,描述为"hello hello hello"。 2. **启动menuconfig**: ```bash make menuconfig # 在源码顶层目录执行 ``` 此时: - 工具扫描所有Kconfig文件(包括刚修改的),生成菜单树。 - 加载现有`.config`(若有历史配置)初始化选项状态。 - 新增的"hello hello hello"选项出现在菜单中(路径:`Device Drivers` → `Block devices`)。 3. **用户操作**: - 在menuconfig界面中找到该选项,选择编译为模块(`<M>`)。 4. **保存配置**: - 退出menuconfig时,工具更新`.config`文件,添加一行: ```config CONFIG_ANDREA_BLOCK_TEST=m ``` 5. **编译生效**: - 后续执行`make`编译时,Makefile根据`.config`中的`CONFIG_ANDREA_BLOCK_TEST=m`,将对应代码编译为模块(`.ko`文件)。 ### 关键依赖影响: - **Kconfig修改 ⇒ menuconfig菜单变化**:增/删Kconfig中的配置项会直接影响menuconfig显示的菜单结构[^3]。 - **menuconfig操作 ⇒ .config更新**:用户通过界面修改选项后,保存时会覆盖`.config`文件[^1][^3]。 - **.config ⇒ 编译结果**:`=y`编译进内核;`=m`生成模块;未设置或`=n`则不编译[^2]。 ### 注意事项: - **备份.config**:执行`make clean`会删除`.config`,因此配置完成后建议备份[^2]。 - **菜单依赖**:Kconfig中的`depends on`语句(如`depends on ARCH_PXA`)会控制选项的可见性——只有依赖满足时,menuconfig才显示该选项[^4]。 - **配置迁移**:升级内核时,可将旧`.config`复制到新源码树,运行`make oldconfig`或`make menuconfig`适配变更(处理新增/废弃的配置项)。 --- ### §§ 相关问题 §§ 1. 如何通过命令行直接修改`.config`中的配置值(非交互式)? 2. `Kconfig`语法中`depends on``select`有什么区别? 3. 当内核源码升级后,如何安全地将旧版`.config`迁移到新版源码树? 4. 如何为自定义内核驱动添加`Kconfig``Makefile`条目? > 参考资料: > [^1]: menuconfig提供图形界面,读取.config并更新.config > [^2]: .config控制功能开关,编译依据其值处理代码 > [^3]: 修改Kconfig后menuconfig显示新选项,配置结果存入.config > [^4]: Kconfig定义选项依赖关系(如depends on)</think>在Linux内核开发中,`menuconfig`、`Kconfig``.config`三者构成配置系统的核心框架,其关系可总结为: ### 一、核心关系图解 ``` Kconfig(配置模板) → menuconfig(交互工具) → .config(编译依据) ``` 1. **Kconfig 是基础** - 定义配置选项的结构、属性依赖关系 - 分布在内核源码树的各个目录(如 `drivers/Kconfig`) - **作用**:声明配置项的名称、类型(bool/tristate)、依赖条件(`depends on`)、帮助文本等[^3][^4] 2. **menuconfig 是桥梁** - 基于ncurses库的文本图形界面工具 - **输入**:读取所有Kconfig文件 + 当前.config文件 - **输出**:生成/更新.config文件 - **作用**:将Kconfig定义的选项可视化,允许用户交互式修改配置[^1][^3] 3. **.config 是结果** - 记录用户选择的实际配置值(如 `CONFIG_USB=y`) - 位于内核源码根目录的隐藏文件 - **作用**:编译时被Makefile读取,决定代码编译方式(`=y`编译进内核,`=m`编译为模块)[^2][^3] ### 二、工作流程详解 1. **初始化** ```bash make menuconfig # 启动配置界面 ``` - 工具扫描所有Kconfig文件,构建菜单树 - 加载现有.config初始化选项状态[^1] 2. **用户交互** - 通过菜单导航(如 `Device Drivers → USB support`) - 修改选项状态(`<Y>`, `<M>`, `<N>`) - 实时验证依赖关系(如依赖未满足的选项变灰)[^4] 3. **保存配置** - 退出时生成/更新 `.config` 文件 - 格式示例: ```config CONFIG_USB=y CONFIG_NET=m # 编译为模块 # CONFIG_DEBUG is not set # 未启用 ``` 4. **编译生效** - Makefile 根据 `.config` 决定: - `=y`:代码静态链接进内核 - `=m`:编译为独立.ko模块 - 未设置:跳过编译[^2] ### 三、关键特性对比 | 组件 | 角色 | 修改方式 | 示例内容 | |-------------|--------------------|-------------------|------------------------------| | **Kconfig** | 配置模板 | 手动编辑源码 | `config USB`<br>`tristate "USB support"` | | **.config** | 编译依据 | menuconfig自动生成| `CONFIG_USB=y` | | **menuconfig**| 交互界面 | 图形化操作 | 显示菜单树选项状态 | ### 四、典型场景验证 1. **添加新驱动**(实验验证[^3]): ```kconfig # 在drivers/block/Kconfig末尾添加 config ANDREA_BLOCK_TEST tristate "hello test driver" ``` - 执行 `make menuconfig` 后: - 菜单中出现新选项"hello test driver" - 选择`<M>`保存后,`.config`新增: ```config CONFIG_ANDREA_BLOCK_TEST=m ``` 2. **配置继承** - 通过 `source subdir/Kconfig` 实现配置树层级集成 - 依赖关系传递(如 `depends on ARCH_ARM` 限制选项可见性)[^4] ### 五、注意事项 1. **备份.config** - `make clean` 会删除.config,重要配置需备份[^2] 2. **配置迁移** - 内核升级时使用 `make oldconfig` 适配旧配置 3. **依赖冲突** - `menuconfig` 自动处理 `depends on` `select` 的连锁反应 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值