关于内核编译及配置总结:①auto.conf与②autoconf.h的‘自动’生成

1. autoconf.h生成是在make config完了之后, 由scripts/kconfig/confdata.c生成的,读取.config中的配置,然后通过 header_print_symbol将=y的定义重置为#define CONFIG_XXX 1的模式写到autoconf.h文件当中。
2. 作用就是使得你在menuconfig中选中的配置成为C语言当中能识别的头文件格式, 以便在以后使用的时候作为宏定义出现。

 

一、编译
1.解压缩
①tar xvf linux-2.6.22.6.tar.bz2
②进入linux-2.6.22.6目录  
cd linux-2.6.22.6/
 
 
2.打补丁
patch -p1<../linux-2.6.22.6_jz2440.patch 
 
 
3.配置内核(具体来说就是:支持哪个架构的单板)
->生成.config
 
①make menuconfig
->选择你所需的东西,编进内核(①做为模块编进内核 ②静态编进内核built-in.o)
->这种方法很复杂.因为配置选项太多.
 
②使用默认的配置,在上面修改
->在内核目录(linux-2.6.22.6/)下查找
find -name “*defconfig*”   –>会搜索出很多文件
->进入 ./arch/arm/configs/ 目录看一下:
 cd ./arch/arm/configs/    ->>里面有很多配置文件 “ XXX_defconfig”  (XXX 代表很多类似的文件名)
  ->>我们找到和我们的2440开发板相似的配置文件 s3c2410_defconfig  
->返回 /work/system/linux-2.6.22.6$ 目录
执行:make s3c2410_defconfig   ->>生成了 .config文件 (也就是Linux内核的配置文件)
->>我们的 make menuconfig 命令也是去 读 .config文件,然后出现菜单.
->.config里面的内容就是我们所需的配置内容,不同的开发板需要去做相应的修改
 
③使用厂家提供的配置文件(如:config_ok)
->厂商给我们提供了配置文件config_ok  
那我们讲config_ok的内容拷贝到.config就可以了
cp config_ok .config
->执行make menuconfig 
出现配置菜单,配置它
4.编译
①编译uImage
->make uImage
二、配置
(1) 为什么我们在编译内核之前需要配置呢? 原因很简单:需要哪些东西(也就是支持哪些东西)就配置它,让内核支持它.
从内核源码的角度来看的话,内核源码中出现了很多”判断语句”(官方的说法是:条件指示符),这样就会出现“动态编译”
的情况,内核怎么知道需要把哪些”条件指示符”所包含的内容 编译进内核呢? 就必须依靠配置文件(.config)了
总结:配置主要从两个方面出发
①从Makefile的角度
-> 静态编进内核(直接编进内核)
-> 动态编进内核(做为模块编进内核)
-> 在/include/config目录下自动生成auto.conf 给顶层Makefile使用
->>在顶层Makefile中搜索auto.conf : 可以看到 include/config/auto.conf     (当然,没有配置的内核代码是找不到 include/目录下的config目录的)
在auto.conf中可以看到类似于:                          这样的配置选项,这是提供给顶层Makefile使用的.
                             ONFIG_ROOT_NFS        = y               
                             CONFIG_TMPFS          = y                 
                             CONFIG_GENERIC_GPIO   = y          
                             CONFIG_S3C2410_PM     = y  
         
②从内核源码的角度
->“条件指示符”所指示的”代码”到底需不需要被编进内核.
->在include/linux目录下自动生成了autoconf.h这个头文件 (当然,没有配置的内核代码是找不到 autoconf.h这个头文件的)       
在autoconf.h中可以看到类似于:                            这样的配置选项,这是提供给Linux源码使用.  
                            #define CONFIG_CPU_S3C2410_DMA       1       
                            #define CONFIG_CRYPTO_ECB_MODULE     1     
                            #define CONFIG_SMDK2440_CPU2440           
                            #define CONFIG_KGDB_PORT_NUM         0         
                            #define CONFIG_SERIAL_8250_SHARE_IRQ 1 
 
 
(2) 为了更好的说明这种情况我们来看个例子: 让内核支持 网卡(DM9000)
 
①在/work/system/linux-2.6.22.6 目录下搜索 CONFIG_DM9000
-> grep “CONFIG_DM9000” * -nR (搜索出一堆东西,我们来看我们所需的信息)
->> config_ok:599:CONFIG_DM9000=y
->> drivers/net/Makefile:197:obj-$(CONFIG_DM9000) += dm9000.o
->> arch/arm/plat-s3c24xx/common-smdk.c:46:#if defined(CONFIG_DM9000) || defined(CONFIG_DM9000_MODULE)  
<注意:CONFIG_DM9000在原始的Linux内核源码可能找不到,要在打好补丁或者配置后的源码中才能找到,根据具体的厂商而定>
 
②我们看到config_ok 中有这么一句: CONFIG_DM9000 = y  (y 的含义是静态编进内核 M 是以模块编进内核) 
然后drivers/net/目录下的 Makefile 文件会把 dm9000.o 静态编进内核: obj-y += dm9000.o
这是站在Makefile的角度来说的.
<当然它还会自动生成一个配置文件auto.conf(在/include/config目录下) 给顶层Makefile使用>
 
③上面的例子不足以说明.config对源码层次的影响,我们再来看一个源码级别的例子.
-> 在s3c2410fb.c(driver/video)文件中找到了这么一段代码.
        
#ifdef CONFIG_FB_S3C2410_DEBUG
static int debug   = 1;     
#else                         
static int debug   = 0;     
#endif            
->我们看到 CONFIG_FB_S3C2410_DEBUG  这么个宏定义.我们在内核源码里面是搜索不到的.其实它被定义在.config当中
(当然,定义在.config当中是不能被内核代码使用的,需要被定义为宏才能被内核代码使用, 这样就必须自动生成 XXX.h文件)
->我们在内核源码目录下搜索CONFIG_FB_S3C2410_DEBUG :   grep “CONFIG_FB_S3C2410_DEBUG” * -nR
       arch/arm/configs/s3c2410_defconfig:929:# CONFIG_FB_S3C2410_DEBUG is not set
       config_ok:948:# CONFIG_FB_S3C2410_DEBUG is not set                         
       drivers/video/s3c2410fb.c:109:#ifdef CONFIG_FB_S3C2410_DEBUG               
       通过上面搜索的信息,我相信大家可以明白 .config(config_ok)的配置对 “源码”级别的影响了.
       
     ->注意:当然这个和“宏”还是有区别的,宏只能在 XX.h中定义, Linux中 include/linux/autoconfig.h 是自动生成的,换句话说,是根据.config生成的.
      include/linux/autoconfig.h 中的内容就来源于 .config 换句话说,include/linux/autoconfig.h 中的内容是根据
      .config 中的内容而生成的.
       
(3)总结
                              生成.config
————————————————————————-
    编译内核:                 make uImage
 ————————————————————————-
自动生成->  auto.conf                 autoconf.h(宏)   
————————————————————————-
      ①提供给顶层Makefile使用                     ②提供给内核C源码使用
   (如: -include include/config/auto.conf  )          (如: include “autoconf.h)
     
(4)autoconf.h的身影
   不仅在.C文件中能够看到autoconf.h的身影,在GCC的编译规则中也能体现出来,下面我们来看一个.C文件的‘依赖文件’(这个依赖文件是自动生成的). drivers/leds/目录下的 .led-core.o.cmd 文件
cmd_drivers/leds/led-core.o := arm-linux-gcc -Wp,-MD,drivers/leds/.led-core.o.d  
-nostdinc -isystem /work/tools/gcc-3.4.5-glibc-2.3.6/lib/gcc/arm-linux/3.4.5/include 
-D__KERNEL__ -Iinclude  -include include/linux/autoconf.h 
-mlittle-endian 
-Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Os -marm -fno-omit-frame-pointer 
-mapcs -mno-sched-prolog -mapcs-32 -mno-thumb-interwork 
-D__LINUX_ARM_ARCH__=4 -march=armv4t -mtune=arm9tdmi -malignment-traps -msoft-float 
-Uarm -fno-omit-frame-pointer -fno-optimize-sibling-calls -g  -Wdeclaration-after-statement     
-D”KBUILD_STR(s)=\#s” -D”KBUILD_BASENAME=KBUILD_STR(led_core)”  -D”KBUILD_MODNAME=KBUILD_STR(led_core)” 
-c -o drivers/leds/led-core.o drivers/leds/led-core.c
这是一个自动生成的临时文件(里面记录了led-core.c 的依赖规则)
或许你会问:为什么autoconf.h 会出现在这个GCC命令行里面,出现在这个依赖文件里面?
答案其实很简单,前面说过, .C文件中 常常含有 类似与 CONFIG_FB_S3C2410_DEBUG 这样的宏,在内核被编译之前,CONFIG_FB_S3C2410_DEBUG这样的宏是没有定义的.它被定义在autoconf.h 文件中, 而autoconf.h 文件需要先配置好内核才能自动生成(具体配置看是什么芯片/平台,前面已经讲过了).
正因为.C文件有许多‘宏’在 autoconf.h 中,所以依赖肯定会包含这个‘自动生成的头文件’.

分析内核的配置与编译过程

对内核的操作分为两类:
一.配置内核
二.编译内核

一.配置内核的过程如下:
make menuconfig时可以修改配置项,这主要是Kconfig的功能,
然后最终的配置结果会保存在.config文件中。下次再执行make menuconfig时
回去读取这个文件。这是内核配置的过程。

二.编译内核的过程(即make uImage的过程)如下:
1.查看内核配置文件.config:

# Automatically generated make config: don’t edit
# Linux/arm 2.6.38 Kernel Configuration
# Mon Jul 18 11:42:55 2011
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
CONFIG_GENERIC_GPIO=y
CONFIG_ARCH_USES_GETTIMEOFFSET=y
CONFIG_HAVE_PROC_CPU=y
CONFIG_NO_IOPORT=y
CONFIG_STACKTRACE_SUPPORT=y
。。。
主要的内容就是一堆配置项:CONFIG_XXX=y/CONFIG_XXX=m/ CONFIG_XXX=n
=y表示编进内核,=m表示编译成模块,=n表示不参与编译

2.make uImage的前期工作
通过grep “CONFIG_XXX” * -nwr可知,一共4类文件在使用这些配置项:
<1>Makefile
<2>c源码
<3>/include/config/auto.conf
<4>include/generated/autoconf.h
当make uImage刚开始时,内核会根据.config自动生成auto.conf和autoconf.h这
两个文件。其中autoconf.h是供c源码使用(宏开关),而auto.conf供Makefile使
用(是否被编译)。
auto.conf和.config内容很类似,都是下面这种格式:
CONFIG_CRC32=y
CONFIG_I2C_BOARDINFO=y
CONFIG_HAVE_AOUT=y
CONFIG_MINI6410_SD_CH0=y
而autoconf.h有点不同,在.config中被定义为m/y的配置项在autoconf.h被define
为1,在.config中被定义为n的配置项在autoconf.h被define为0。它的格式如下:
#define CONFIG_TOUCHSCREEN_MINI6410 1
#define CONFIG_HAS_DMA 1
#define CONFIG_USB_SERIAL_VISOR 1
#define CONFIG_SCSI 1
。。。
3.分析Makefile
通过分析makefile,我们可以彻底明白make uImage做了什么。
我把整个内核的makefile分成三类:
<1>各级子目录makefile(每个子目录都有makefile)
<2>/arch/arm/Makefile(架构相关的makefile)
<3>顶层目录makefile(auto.conf和/arch/arm/Makefile被包含在其中:include
include/config/auto.conf
和include (srctree)/arch/(SRCARCH)/Makefile)
随便打开一个子目录makefile,可以看到类似的内容:
obj-(CONFIG_BFIN_JTAG_COMM)    += bfin_jtag_comm.o
obj-
(CONFIG_MVME147_SCC)       += generic_serial.o vme_scc.o
obj-(CONFIG_MVME162_SCC)       += generic_serial.o vme_scc.o
obj-
(CONFIG_BVME6000_SCC)      += generic_serial.o vme_scc.o
obj-(CONFIG_ROCKETPORT)        += rocket.o
。。。
很显然,
在auto.conf中CONFIG_XXX=y,对应的源码文件会被Makefile编译进内核;
在auto.conf中CONFIG_XXX=m,对应的源码文件会被Makefile编译成模块;
在auto.conf中CONFIG_XXX=n,对应的源码文件不会被makefile编译;

继续分析
当在顶层目录执行make uImage 时,目标是uImage,/arch/arm/makefile(该
makefile被顶层makefile所包含)中可以找到相关条目:
zImage Image xipImage bootpImage uImage: vmlinux(Q)(MAKE)(build)=(boot) MACHINE=(MACHINE) (boot)/@
可以看出,uImage依赖于vmlinux,
继续分析,vmlinux的依赖在顶层makefile中,如下:
vmlinux: (vmlinux-lds)(vmlinux-init) (vmlinux-main) vmlinux.o(kallsyms.o)
其中
vmlinux-lds:链接脚本
vmlinux-init:初始化相关的代码
vmlinux-main:核心代码

继续查看依赖,在顶层makefile中上述三者的依赖如下:
vmlinux-init := (head-y)(init-y)
vmlinux-main := (core-y)(libs-y) (drivers-y)(net-y)
vmlinux-lds  := arch/(SRCARCH)/kernel/vmlinux.lds

逐个分析:
(1)vmlinux-init
head-y在/arch/arm/makefile中定义:
head-y          := arch/arm/kernel/head(MMUEXT).o               arch/arm/kernel/init_task.o
MMUEXT没人定义,所以head-y就是由head.o和init_task.o两个文件组成
init-y在顶层makefile中定义:
init-y          := init/

(2)vmlinux-main
core-y、libs-y、drivers-y、net-y均在顶层makefile中定义:
core-y          := usr/(即源码树中的/usr目录,下面类似)
core-y          += kernel/  mm/  fs/  ipc/  security/  crypto/  block/
libs-y          := lib/
drivers-y       := drivers/  sound/  firmware/
net-y           := net/

在顶层makefile中还有下面这几句话:
init-y          := (patsubst %/, %/built-in.o,(init-y))
core-y          := (patsubst %/, %/built-in.o,(core-y))
drivers-y       := (patsubst %/, %/built-in.o,(drivers-y))
net-y           := (patsubst %/, %/built-in.o,(net-y))
libs-y1         := (patsubst %/, %/lib.a,(libs-y))
libs-y2         := (patsubst %/, %/built-in.o,(libs-y))
libs-y          := (libs-y1)(libs-y2)

patsubst是makefile的一个函数,它的意思是说init-y,core-y,drivers-y,net-y,libs-y
所对应的每一个目录下的所有涉及的文件最终都会被编译合成一个built-in.o文件,即
init-y          := init/built-in.o
core-y          := usr/built-in.o  kernel/
built-in.o  mm/built-in.o  fs/built-in.o  ipc/built-in.o  security/built-in.o  crypto/built-in.o  block/built-in.o
libs-y          := lib/built-in.o  lib/lib.a
drivers-y       := drivers/built-in.o  sound/built-in.o  firmware/built-in.o
net-y           := net/built-in.o
各级的子目录的makefile会被上一级的makefile所调用,在子目录中makefile也会
将涉及的文件编译成built-in.o文件,这些子目录中的built-in.o会被上一级目录的
built-in.o所包含。最终,init-y,core-y,libs-y,net-y对应目录下的built-in.o文件组成
内核。

 

发表回复