前面我们介绍模块编程的时候介绍了驱动进入内核有两种方式:模块和直接编译进内核,并介绍了模块的一种编译方式——在一个独立的文件夹通过makefile配合内核源码路径完成
那么如何将驱动直接编译进内核呢?
在我们实际内核的移植配置过程中经常听说的内核裁剪又是怎么麽回事呢?
我们在进行linux内核配置的时候经常会执行make menuconfig这个命令,然后屏幕上会出现以下界面:
这个界面是怎么生成的呢?
跟我们经常说的内核配置与与编译又有什么关系呢?
下面我们借此来讲解一下linux内核的配置机制及其编译过程。
一、配置系统的基本结构
Linux内核的配置系统由三个部分组成,分别是:
1、Makefile:分布在 Linux 内核源代码根目录及各层目录中,定义 Linux 内核的编译规则;
2、配置文件(config.in(2.4内核,2.6内核)):给用户提供配置选择的功能;
3、配置工具:包括配置命令解释器(对配置脚本中使用的配置命令进行解释)和配置用户界面(提供基于字符界面、基于 Ncurses 图形界面以及基于 Xwindows 图形界面的用户配置界面,各自对应于 Make config、Make menuconfig 和 make xconfig)。
这些配置工具都是使用脚本语言,如 Tcl/TK、Perl 编写的(也包含一些用 C 编写的代码)。本文并不是对配置系统本身进行分析,而是介绍如何使用配置系统。所以,除非是配置系统的维护者,一般的内核开发者无须了解它们的原理,只需要知道如何编写 Makefile 和配置文件就可以。
二、makefile menuconfig过程讲解
当我们在执行make menuconfig这个命令时,系统到底帮我们做了哪些工作呢?
这里面一共涉及到了一下几个文件我们来一一讲解
Linux内核根目录下的scripts文件夹
arch/$ARCH/Kconfig文件、各层目录下的Kconfig文件
Linux内核根目录下的makefile文件、各层目录下的makefile文件
Linux内核根目录下的的.config文件、arm/$ARCH/下的config文件
Linux内核根目录下的 include/generated/autoconf.h文件
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即可.
5)经过以上两步,我们可以正确的读取、配置我们需要的界面了
那么他们如何跟makefile文件建立编译关系呢?
当你保存make menuconfig选项时,系统会除了会自动更新.config外,还会将所有的选项以宏的形式保存在
Linux内核根目录下的 include/generated/autoconf.h文件下
内核中的源代码就都会包含以上.h文件,跟宏的定义情况进行条件编译。
当我们需要对一个文件整体选择如是否编译时,还需要修改对应的makefile文件,例如:
我们选择是否要编译s3c2410_ts.c这个文件时,makefile会根据CONFIG_TOUCHSCREEN_S3C2410来决定是编译此文件,此宏是在Kconfig文件中定义,当我们配置完成后,会出现在.config及autconf中,至此,我们就完成了整个linux内核的编译过程。
CONFIG_TOUCHSCREEN_S3C2410的值可以是以下三种
y:加载
n:不加载
m:作为模块加载
如果内核模块是由多个源文件编译而成,那你就要采用上面那个例子一样的方法去声明你所要编译的模块。Kbuild需要知道你所编译的模块是基于哪些文件,所以你需要通过变量$(
例子:
#drivers/isdn/i4l/Makefile
obj-$(CONFIG_ISDN) += isdn.o
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
在这个例子中,模块名将是isdn.o, Kbuild将编译在$(isdn-objs)中列出的所有文件,然后使用"$(LD) -r"生成isdn.o。
Kbuild能够识别用于组成目标文件的后缀-objs和后缀-y。这就让Kbuild Makefile可以通过使用 CONFIG_ 符号来判断该对象是否是用来组合对象的。
例子:
#fs/ext2/ Makefile
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
在这个例子中,如果 $(CONFIG_EXT2_FS_XATTR) 是 'y', xattr.o将是复合对象 ext2.o的一部分。
注意:当然,当你要将其编译进内核时,上面的语法同样适用。所以,如果你的 CONFIG_EXT2_FS=y,那Kbuild会按你所期望的那样,生成 ext2.o文件,然后将其联接到 built-in.o中。
最后我们会发现,整个linux内核配置过程中,留给用户的接口其实只有各层Kconfig、makefile文件以及对应的源文件。
比如我们如果想要给内核增加一个功能,并且通过make menuconfig控制其声称过程
首先需要做的工作是:修改对应目录下的Kconfig文件,按照Kconfig语法增加对应的选项;
其次执行make menuconfig选择编译进内核或者不编译进内核,或者编译为模块,.config文件和autoconf.h文件会自动生成;
最后修改对应目录下的makefile文件完成编译选项的添加;
最后的最后执行make zImage命令进行编译。
三、具体实例
下面我们以前面做过的模块实验为例,讲解如何通过make menuconfig机制将前面单独编译的模块编译进内核或编译为模块
假设我已经有了这么一个驱动:
modules.c
#include /*module_init()*/
#include /* printk() */
#include /* __init __exit */
#define DEBUG //open debug message
#ifdef DEBUG
#define PRINTK(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
#else
#define PRINTK(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
#endif
/* Module Init & Exit function */
static int __init myModule_init(void)
{
/* Module init code */
PRINTK("myModule_init\n");
return 0;
}
static void __exit myModule_exit(void)
{
/* Module exit code */
PRINTK("myModule_exit\n");
return;
}
module_init(myModule_init);
module_exit(myModule_exit);
MODULE_AUTHOR("dengwei"); /*模块作者,可选*/
MODULE_LICENSE("GPL"); /*模块许可证明,描述内核模块的许可权限,必须*/
MODULE_DESCRIPTION("A simple Hello World Module"); /*模块说明,可选*/
Step1:将modules.c拷到drivers/char/目录下(这个文件夹一般存放常见的字符驱动)
Step2: vi driver/char/Kconfig,在
config DEVKMEM后添加以下信息
config MODULES
tristate "modules device support"
default y
help
Say Y here,the modules will be build in kernel.
Say M here,the modules willbe build to modules.
Say N here,there will be nothing to be do.
Step3:make menuconfig
Device driver-character devices
[*]modules device suppor
Step4:vi driver/char/Makefile,在js-rtc后添加
obj-$(CONFIG_MODULES)+= modules.o
CONFIG_MODULES 必须跟上面的Kconfig中保持一致,系统会自动添加CONFIG_前缀
modules.o必须跟你加入的.c文件名一致
最后执行:make zImage modules就会被编译进内核中
第三步:
Step3:make menuconfig
Device driver-character devices
[M]modules device suppor
把星号在配置界面通过空格改为M,最后执行make modules,在driver/char/目录下会生成一个modules.ko文件
跟我们前面讲的单独编译模块效果一样,也会生成一个模块,将它考入开发板执行insmod moudles.ko,即可将生成的模块插入内核使用。
参考文献:
Linux 内核配置机制(make menuconfig、Kconfig、makefile)讲解_wuxianglonghaohao的博客-CSDN博客
Makefile之编译可装载模块 -obj-m_makefile obj-m_wuxianglonghaohao的博客-CSDN博客
Linux 内核裁剪 步骤之 make menuconfig_wuxianglonghaohao的博客-CSDN博客