linux 内核编译:内核配置原理与常见配置问题的解决方法&&内核版本控制解析

摘要:在进行内核编译的时候,内核配置无疑是其中最为重要的环节,直接决定了编译的内核是否能够满足要求。而且,对于新加入的内核特性,我们往往无法在make menuconfig中找到相应的选项。本文主要为你解决如下问题:1)linux内核编译之前,配置文件的读取顺序是什么?2)对于比较新的内核特性,我们无法在.config之中找到某个配置,应该如何处理?3)如何将自己新添加的驱动添加进入内核?

一.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过程讲解——内核配置Kconfig文件的读取和.config的生成过程

当我们在执行make menuconfig这个命令时,系统到底帮我们做了哪些工作呢?

这里面一共涉及到了以下几个文件我们来一一讲解

(1)Linux内核根目录下的scripts文件夹

(2)arch/$ARCH/Kconfig文件、各层目录下的Kconfig文件

(3)Linux内核根目录下的makefile文件、各层目录下的makefile文件

(4)Linux内核根目录下的的.config文件、arm/$ARCH/下的config文件

(5)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几门功课的考试题


Koncfig文件包含了配置选项的依赖关系,层次关系,各个选项的默认值。


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(linux)/autoconf.h文件下,它实际上是makefile文件读取的内容


内核中的源代码就都会包含以上.h文件,跟宏的定义情况进行条件编译。

当我们需要对一个文件整体选择如是否编译时,还需要修改对应的makefile文件,例如:


    我们选择是否要编译s3c2410_ts.c这个文件时,makefile会根据CONFIG_TOUCHSCREEN_S3C2410来决定是编译此文件,此宏是在Kconfig文件中定义,当我们配置完成后,会出现在.config及autconf中,至此,我们就完成了整个linux内核的编译过程。

    最后我们会发现,整个linux内核配置过程中,留给用户的接口其实只有各层Kconfig、makefile文件以及对应的源文件。

    比如我们如果想要给内核增加一个功能,并且通过make menuconfig控制其生成过程

    首先需要做的工作是:修改对应目录下的Kconfig文件,按照Kconfig语法增加对应的选项;

    其次执行make menuconfig选择编译进内核或者不编译进内核,或者编译为模块,.config文件和autoconf.h文件会自动生成;

    最后修改对应目录下的makefile文件完成编译选项的添加;

    最后的最后执行make zImage命令进行编译。


总结一下,这个过程大致是这样的:各个目录之下存在Kconfig文件,里面的内容是相关的选项,depends和select;执行make menuconfig命令以后,会读取这些Kconfig里面的配置选项(配置选项的默认值也在各个Kconfig文件之中),生成如下图的图形界面。


我们对相关的symbol进行配置,然后点击保存,就写入了.config文件和相应头文件。大致如此:Kconfig *******menuconfig*********.config&& include/generated/autoconf.h************make


三、如何向内核中添加自己的配置

下面我们以前面做过的模块实验为例,讲解如何通过make menuconfig机制将前面单独编译的模块编译进内核或编译为模块

假设我已经有了这么一个驱动:

modules.c

  1. #include <linux/module.h>       /*module_init()*/   
  2. #include <linux/kernel.h> /* printk() */   
  3. #include <linux/init.h>       /* __init __exit */   
  4.   
  5. #define DEBUG   //open debug message   
  6.   
  7. #ifdef DEBUG   
  8. #define PRINTK(fmt, arg...)     printk(KERN_WARNING fmt, ##arg)   
  9. #else   
  10. #define PRINTK(fmt, arg...)     printk(KERN_DEBUG fmt, ##arg)   
  11. #endif   
  12.   
  13. /* Module Init & Exit function */  
  14. static int __init myModule_init(void)  
  15. {  
  16.     /* Module init code */  
  17.     PRINTK("myModule_init\n");  
  18.     return 0;  
  19. }  
  20.   
  21. static void __exit myModule_exit(void)  
  22. {  
  23.     /* Module exit code */  
  24.     PRINTK("myModule_exit\n");  
  25.     return;  
  26. }  
  27.   
  28. module_init(myModule_init);  
  29. module_exit(myModule_exit);  
  30.   
  31. MODULE_AUTHOR("dengwei");                          /*模块作者,可选*/  
  32. MODULE_LICENSE("GPL");                             /*模块许可证明,描述内核模块的许可权限,必须*/  
  33. 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,即可将生成的模块插入内核使用。


四 常见问题解答

    1)linux内核编译之前,配置文件的读取顺序是什么?

    参考第二部分的总结


    2)对于比较新的内核特性,我们无法在.config之中找到某个配置,应该如何处理?

    如果一个配置没有出现在.config之中,我们首先要在make menuconfig的界面中进行寻找,如果在这个界面中能够找到,那么多半是我们没有将这个对应symbol的depends满足,所以导致config之中没有相关的选项。此时,我们只要查看相关symbl的depends,然后满足它们即可,就可以在.config之中找到相应选项。如果在make menuconfig的界面中也无法找到,说明Kconfig之中没有相关的配置选项,就说明它还没有被编入内核。


3)如何将自己新添加的驱动添加进入内核?

    参看第三部分


五 内核编译实践经验


1)比那一centos 6.3出错“crypto/signature/ksign-publickey.c: key.h: No such file or directory

解决方法:

run make menuconfig.
1) Select "Enable loadable module support", then "Module signature verification (EXPERIMENTAL)". Disable it.
2) Then go back to the main menu, select "Cryptographic API" then "In-kernel signature checker (EXPERIMENTAL)" and disable that one too.
Then go back again, save your config and make your kernel. At least, it will pass this phase successfully.


六、关于内核版本号


Linux内核使用三种不同的版本编号方式。
  第一种方式用于1.0版本之前(包括1.0)。第一个版本是0.01,紧接着是0.02、0.03、0.10、0.11、0.12、0.95、0.96、0.97、0.98、0.99和之后的1.0。
第二种方式用于1.0之后到2.6,数字由三部分“A.B.C”,A代表主版本号,B代表次主版本号,C代表较小的末版本号。只有在内核发生很大变化时(历史上只发生过两次,1994年的1.0,1996年的2.0),A才变化。可以通过数字B来判断Linux是否稳定,偶数的B代表稳定版,奇数的B代表开发版。C代表一些bug修复,安全更新,新特性和驱动的次数。以版本2.4.0为例,2代表主版本号,4代表次版本号,0代表改动较小的末版本号。在版本号中,序号的第二位为偶数的版本表明这是一个可以使用的稳定版本,如2.2.5,而序号的第二位为奇数的版本一般有一些新的东西加入,是个不一定很稳定的测试版本,如2.3.1。这样稳定版本来源于上一个测试版升级版本号,而一个稳定版本发展到完全成熟后就不再发展。
第三种方式从2004年2.6.0版本开始,使用一种“time-based”的方式。3.0版本之前,是一种“A.B.C.D”的格式。七年里,前两个数字A.B即“2.6”保持不变,C随着新版本的发布而增加,D代表一些bug修复,安全更新,添加新特性和驱动的次数。3.0版本之后是“A.B.C”格式,B随着新版本的发布而增加,C代表一些bug修复,安全更新,新特性和驱动的次数。第三种方式中不使用偶数代表稳定版,奇数代表开发版这样的命名方式。举个例子:3.7.0代表的不是开发版,而是稳定版!

内核编译时候对版本号的控制:
KERNELRELEASE:这个变量的值,我们可以在/include/config/kernel.release文件中找到。
内核版本的构成和控制部分如下:
#       $(KERNELVERSION)
#         $(VERSION)                    eg, 2

#         $(PATCHLEVEL)                 eg, 6

#         $(SUBLEVEL)                   eg, 18
#         $(EXTRAVERSION)               eg, -rc6
#       $(localver-full)
#         $(localver)
#           localversion*               (files without backups, containing '~')
#           $(CONFIG_LOCALVERSION)      (from kernel config setting)
#         $(localver-auto)              (only if CONFIG_LOCALVERSION_AUTO is set)
#           ./scripts/setlocalversion   (SCM tag, if one exists)
#           $(LOCALVERSION)             (from make command line if provided)
#  Note how the final $(localver-auto) string is included *only* if the
# kernel config option CONFIG_LOCALVERSION_AUTO is selected.  Also, at the
# moment, only git is supported but other SCMs can edit the script
# scripts/setlocalversion and add the appropriate checks as needed.

注意:VERSION,PATCHLEVEL,SUBLEVEL之间是会自动加上“.”的,但是EXTRAVERSION之前不会自动加“.”。版本控制是通过脚本文件scripts/setlocalversion来执行的,如果CONFIG_LOCALVERSION_AUTO=y

CONFIG_LOCALVERSION_AUTO=N:

                 如果没有定义LOCALVERSION,版本号后面会添加“+”号

                 没有定义LOCALVERSION,版本后不会添加“+”

如果既不想添加字符,又不想有“+”号:不定义CONFIG_LOCALVERSION_AUTO,将LOCALVERSION变量定义为空:LOCALVERSION=


自定义版本号的方法:

在scripts/setlocalversion文件中还有有这么一段:
# localversion* files in the build and source directory
res="$(collect_files localversion*)"
if test ! "$srctree" -ef .; then
 res="$res$(collect_files "$srctree"/localversion*)"
fi

# CONFIG_LOCALVERSION and LOCALVERSION (if set)
res="${res}${CONFIG_LOCALVERSION}${LOCALVERSION}"
由此可看出,如果想往版本号里添加字符,有几种方式:
1. 使用LOCALVERSION变量(或者在命令行,或者添加为环境变量)
2. 在linux-2.6.35目录下添加文件localversion,文件内容会自动添加到版本号里去。
3. 定义CONFIG_LOCALVERSION变量
4. 如果linux-2.6.35目录下有文件localversion(其内容为.33),也使用了LOCALVERSION变量,也定义了CONFIG_LOCALVERSION=".XYZ"。
make LOCALVERSION=.44 include/config/kernel.release
此时对2.6.35.7的内核,include/config/kernel.release的内容为2.6.35.7.33.XYZ.55。
可看到添加的三种字符的顺序:文件localversion内容在前,然后是CONFIG_LOCALVERSION的值,最后是LOCALVERSION的值。


转自:http://blog.csdn.net/trochiluses/article/details/9663743

本文来源:


linux 内核编译:内核配置原理与常见配置问题的解决方法:http://blog.csdn.net/trochiluses/article/details/9663743


外部参考:http://blog.csdn.net/dianhuiren/article/details/6917132

【2】linux内核配置系统解析:http://www.ibm.com/developerworks/cn/linux/kernel/l-kerconf/

【3】linux内核版本号:http://blog.csdn.net/adaptiver/article/details/7225980

猜你喜欢

转载自blog.csdn.net/RHEL_admin/article/details/46327735