Linux内核配置文件.config与Makefile

傻小烨 2011-04-09

==========================================

Makefile初探

==========================================

Linux的内核配置文件有两个,一个是隐含的.config文件,嵌入到主Makefile中;另一个是include/linux/autoconf.h,嵌入到各个c源文件中,它们由makeconfig、makemenuconfig、makexconfig这些过程创建。几乎所有的源文件都会通过linux/config.h而嵌入autoconf.h,如果按照通常方法建立文件依赖关系(.depend),只要更新过autoconf.h,就会造成所有源代码的重新编绎。

为了优化make过程,减少不必要的重新编绎,Linux开发了专用的mkdep工具,用它来取代gcc来生成.depend文件。mkdep在处理源文件时,忽略linux/config.h这样的头文件,识别源文件宏指令中具有"CONFIG_"特征的行。例如,如果有"#ifdefCONFIG_SMP"这样的行,它就会在.depend文件中输出$(wildcard/usr/src/linux/include/config/smp.h)。

include/config/下的文件是另一个工具split-include从autoconf.h中生成,它利用autoconf.h中的CONFIG_标记,生成与mkdep相对应的文件。例如,如果autoconf.h中有"#undefCONFIG_SMP"这一行,它就生成include/config/smp.h文件,内容为"#undefCONFIG_SMP"。这些文件名只在.depend文件中出现,内核源文件是不会嵌入它们的。每配置一次内核,运行split-include一次。split-include会检查旧的子文件的内容,确定是不是要更新它们。这样,不管autoconf.h修改日期如何,只要其配置不变,make就不会重新编绎内核。

如果系统的编绎选项发生了变化,Linux也能进行增量编绎。为了做到这一点,make每编绎一个源文件时生成一个flags文件。例如编绎sched.c时,会在相同的目录下生成隐含的.sched.o.flags文件。它是Makefile的一个片断,当make进入某个子目录编绎时,会搜索其中的flags文件,将它们嵌入到Makefile中。这些flags代码测试当前的编绎选项与原来的是不是相同,如果相同,就将自已对应的目标文件加入FILES_FLAGS_UP_TO_DATE列表,然后,系统从编绎对象表中删除它们,得到FILES_FLAGS_CHANGED列表,最后,将它们设为目标进行更新。

下一步准备逐步深入的剖析Makefile代码。

==========================================

Makefile解读之二:sub-make

==========================================

Linux各级内核源代码的子目录下都有Makefile,大多数Makefile要嵌入主目录下的Rule.make,Rule.make将识别各个Makefile中所定义的一些变量。变量obj-y表示需要编绎到内核中的目标文件名集合,定义O_TARGET表示将obj-y连接为一个O_TARGET名称的目标文件,定义L_TARGET表示将obj-y合并为一个L_TARGET名称的库文件。同样obj-m表示需要编绎成模块的目标文件名集合。如果还需进行子目录make,则需要定义subdir-y和subdir-m。在Makefile中,用"obj-$(CONFIG_BINFMT_ELF)+=binfmt_elf.o"和"subdir-$(CONFIG_EXT2_FS)+=ext2"这种形式自动为obj-y、obj-m、subdir-y、subdir-m添加文件名。有时,情况没有这么单纯,还需要使用条件语句个别对待。Makefile中还有其它一些变量,如mod-subdirs定义了subdir-m以外的所有模块子目录。

Rules.make是如何使make进入子目录的呢?先来看subdir-y是如何处理的,在Rules.make中,先对subdir-y中的每一个文件名加上前缀"_subdir_"再进行排序生成subdir-list集合,再以它作为目标集,对其中每一个目标产生一个子make,同时将目标名的前缀去掉得到子目录名,作为子make的起始目录参数。subdir-m与subdir-y类似,但情况稍微复杂一些。由于subdir-y中可能有模块定义,因此利用mod-subdirs变量将subdir-y中模块目录提取出来,再与subdir-m合成一个大的MOD_SUB_DIRS集合。subdir-m的目标所用的前缀是"_modsubdir_"。

一点说明,子目录中的Makefile与Rules.make都没有嵌入.config文件,它是通过主Makefile向下传递MAKEFILES变量完成的。MAKEFILES是make自已识别的一个变量,在执行新的Makefile之前,make会首先加载MAKEFILES所指的文件。在主Makefile中它即指向.config。

==========================================

Makefile解读之三:模块的版本化处理

==========================================

模块的版本化是内核与模块接口之间进行严格类型匹配的一种方法。当内核配置了CONFIG_MODVERSIONS之后,makedep操作会在include/linux/modules/目录下为各级Makefile中export-objs变量所对应的源文件生成扩展名为.ver的文件。

例如对于kernel/ksyms.c,make用以下命令生成对应的ksyms.ver:

gcc-E-D__KERNEL__-D__GENKSYMS__ksyms.c|/sbin/genksyms-k2.4.1>ksyms.ver

-D__GENKSYMS__的作用是使ksyms.c中的EXPORT_SYMBOL宏不进行扩展。genksyms命令识别EXPORT_SYMBOL()中的函数名和对应的原型,再根据其原型计算出该函数的版本号。

例如ksyms.c中有一行:

EXPORT_SYMBOL(kmalloc);

kmalloc原型是:

void*kmalloc(size_t,int);

genksyms程序对应的输出为:

#define__ver_kmalloc93d4cfe6

#definekmalloc_set_ver(kmalloc)

在内核符号表和模块中,kmalloc将变成kmalloc_R93d4cfe6。

在生成完所有的.ver文件后,make将重建include/linux/modversions.h文件,它包含一系列#include指令行嵌入各个.ver文件。在编绎内核本身export-objs中的文件时,make会增加一个"-DEXPORT_SYMTAB"编绎标志,它使源文件嵌入modversions.h文件,将EXPORT_SYMBOL宏展开中的函数名字符串进行版本名扩展;同时,它也定义_set_ver()宏为一空操作,使代码中的函数名不受其影响。

在编绎模块时,make会增加"-include=linux/modversion.h-DMODVERSIONS"编绎标志,使模块中代码的函数名得到相应版本扩展。

由于生成.ver文件比较费时,make还为每个.ver创建了一个后缀为.stamp时戳文件。在makedep时,如果其.stamp文件比源文件旧才重新生成.ver文件,否则只是更新.stamp文件时戳。另外,在生成.ver和modversions.h文件时,make都会比较新文件和旧文件的内容,保持它们修改时间为最旧。

==========================================

Makefile解读之四:Rules.make的注释

==========================================

[code:1:974578564b]

#

#ThisfilecontainsruleswhicharesharedbetweenmultipleMakefiles.

#

#

#Falsetargets.

#

#

.PHONY:dummy

#

#Specialvariableswhichshouldnotbeexported

#

#取消这些变量通过环境向make子进程传递。

unexportEXTRA_AFLAGS#as的开关

unexportEXTRA_CFLAGS#cc的开关

unexportEXTRA_LDFLAGS#ld的开关

unexportEXTRA_ARFLAGS#ar的开关

unexportSUBDIRS#

unexportSUB_DIRS#编绎内核需进入的子目录,等于subdir-y

unexportALL_SUB_DIRS#所有的子目录

unexportMOD_SUB_DIRS#编绎模块需进入的子目录

unexportO_TARGET#ld合并的输出对象

unexportALL_MOBJS#所有的模块名

unexportobj-y#编绎成内核的文件集

unexportobj-m#编绎成模块的文件集

unexportobj-n#

unexportobj-#

unexportexport-objs#需进行版本处理的文件集

unexportsubdir-y#编绎内核所需进入的子目录

unexportsubdir-m#编绎模块所需进入的子目录

unexportsubdir-n

unexportsubdir-

#

#Getthingsstarted.

#

first_rule:sub_dirs

$(MAKE)all_targets

#在内核编绎子目录中过滤出可以作为模块的子目录。

both-m:=$(filter$(mod-subdirs),$(subdir-y))

SUB_DIRS:=$(subdir-y)

#求出总模块子目录

MOD_SUB_DIRS:=$(sort$(subdir-m)$(both-m))

#求出总子目录

ALL_SUB_DIRS:=$(sort$(subdir-y)$(subdir-m)$(subdir-n)$(subdir-))

#

#Commonrules

#

#将c文件编绎成汇编文件的规则,$@为目标对象。

%.s:%.c

$(CC)$(CFLAGS)$(EXTRA_CFLAGS)$(CFLAGS_$@)-S$<-o$@

#将c文件生成预处理文件的规则。

%.i:%.c

$(CPP)$(CFLAGS)$(EXTRA_CFLAGS)$(CFLAGS_$@)$<>$@

#将c文件编绎成目标文件的规则,$<为第一个所依赖的对象;

#

在目标文件的目录下生成flags文件,strip删除多余的空格,subst将逗号替换成冒号

%.o:%.c

$(CC)$(CFLAGS)$(EXTRA_CFLAGS)$(CFLAGS_$@)-c-o$@$<

@(\

echo'ifeq($(strip$(subst$(comma),:,$(CFLAGS)$(EXTRA_CFLAGS)

$(CFLAGS_$@))),$$(strip$$(subst$$(comma),:,$$(CFLAGS)$$(EXTRA_CFLAGS)

$$(CFLAGS_$@))))';\

echo'FILES_FLAGS_UP_TO_DATE+=$@';\

echo'endif'\

)>$(dir$@)/.$(notdir$@).flags

#汇编文件生成目标文件的规则。

%.o:%.s

$(AS)$(AFLAGS)$(EXTRA_CFLAGS)-o$@$<

#Oldmakefilesdefinetheirownrulesforcompiling.Sfiles,

#butthesestandardrulesareavailableforanyMakefilethat

#wantstousethem.Ourplanistoincrementallyconvertall

#theMakefilestothesestandardrules.--rmk,mec

ifdefUSE_STANDARD_AS_RULE

#汇编文件生成预处理文件的标准规则。

%.s:%.S

$(CPP)$(AFLAGS)$(EXTRA_AFLAGS)$(AFLAGS_$@)$<>$@

#汇编文件生成目标文件的标准规则。

%.o:%.S

$(CC)$(AFLAGS)$(EXTRA_AFLAGS)$(AFLAGS_$@)-c-o$@$<

endif

#c文件生成调试列表文件的规则,$*扩展为目标的主文件名。

%.lst:%.c

$(CC)$(CFLAGS)$(EXTRA_CFLAGS)$(CFLAGS_$@)-g-c-o$*.o$<

$(TOPDIR)/scripts/makelst$*$(TOPDIR)$(OBJDUMP)

#

#

#

all_targets:$(O_TARGET)$(L_TARGET)

#

#Ruletocompileasetof.ofilesintoone.ofile

#

ifdefO_TARGET

$(O_TARGET):$(obj-y)

rm-f$@

#$^扩展为全部依赖对象,如果obj-y为空就生成一个同名空的库文件。

ifneq"$(strip$(obj-y))"""

$(LD)$(EXTRA_LDFLAGS)-r-o$@$(filter$(obj-y),$^)

else

$(AR)rcs$@

endif

#生成flags文件的shell语句。

@(\

echo'ifeq($(strip$(subst$(comma),:,$(EXTRA_LDFLAGS)

$(obj-y))),$$(strip$$(subst$$(comma),:,$$(EXTRA_LDFLAGS)$$(obj-y))))';

\

echo'FILES_FLAGS_UP_TO_DATE+=$@';\

echo'endif'\

)>$(dir$@)/.$(notdir$@).flags

endif#O_TARGET

#

#Ruletocompileasetof.ofilesintoone.afile

#

#将obj-y组合成库L_TARGET的方法。

ifdefL_TARGET

$(L_TARGET):$(obj-y)

rm-f$@

$(AR)$(EXTRA_ARFLAGS)rcs$@$(obj-y)

@(\

echo'ifeq($(strip$(subst$(comma),:,$(EXTRA_ARFLAGS)

$(obj-y))),$$(strip$$(subst$$(comma),:,$$(EXTRA_ARFLAGS)$$(obj-y))))';

\

echo'FILES_FLAGS_UP_TO_DATE+=$@';\

echo'endif'\

)>$(dir$@)/.$(notdir$@).flags

endif

#

#Thismakedependenciesquickly

#

#wildcard为查找目录中的文件名的宏。

fastdep:dummy

$(TOPDIR)/scripts/mkdep$(wildcard*.[chS]local.h.master)>.depend

ifdefALL_SUB_DIRS

#

将ALL_SUB_DIRS中的目录名加上前缀_sfdep_作为目标运行子make,并将ALL_SUB_DIRS

通过

#变量_FASTDEP_ALL_SUB_DIRS传递给子make。

$(MAKE)$(patsubst%,_sfdep_%,$(ALL_SUB_DIRS))

_FASTDEP_ALL_SUB_DIRS="$(ALL_SUB_DIRS)"

endif

ifdef_FASTDEP_ALL_SUB_DIRS

#

与上一段相对应,定义子目录目标,并将目标名还原为目录名,进入该子目录make。

$(patsubst%,_sfdep_%,$(_FASTDEP_ALL_SUB_DIRS)):

$(MAKE)-C$(patsubst_sfdep_%,%,$@)fastdep

endif

#

#Aruletomakesubdirectories

#

#下面2段完成内核编绎子目录中的make。

subdir-list=$(sort$(patsubst%,_subdir_%,$(SUB_DIRS)))

sub_dirs:dummy$(subdir-list)

ifdefSUB_DIRS

$(subdir-list):dummy

$(MAKE)-C$(patsubst_subdir_%,%,$@)

endif

#

#Aruletomakemodules

#

#求出有效的模块文件表。

ALL_MOBJS=$(filter-out$(obj-y),$(obj-m))

ifneq"$(strip$(ALL_MOBJS))"""

#取主目录TOPDIR到当前目录的路径。

PDWN=$(shell$(CONFIG_SHELL)$(TOPDIR)/scripts/pathdown.sh)

endif

unexportMOD_DIRS

MOD_DIRS:=$(MOD_SUB_DIRS)$(MOD_IN_SUB_DIRS)

#编绎模块时,进入模块子目录的方法。

ifneq"$(strip$(MOD_DIRS))"""

.PHONY:$(patsubst%,_modsubdir_%,$(MOD_DIRS))

$(patsubst%,_modsubdir_%,$(MOD_DIRS)):dummy

$(MAKE)-C$(patsubst_modsubdir_%,%,$@)modules

#安装模块时,进入模块子目录的方法。

.PHONY:$(patsubst%,_modinst_%,$(MOD_DIRS))

$(patsubst%,_modinst_%,$(MOD_DIRS)):dummy

$(MAKE)-C$(patsubst_modinst_%,%,$@)modules_install

endif

#makemodules的入口。

.PHONY:modules

modules:$(ALL_MOBJS)dummy\

$(patsubst%,_modsubdir_%,$(MOD_DIRS))

.PHONY:_modinst__

#拷贝模块的过程。

_modinst__:dummy

ifneq"$(strip$(ALL_MOBJS))"""

mkdir-p$(MODLIB)/kernel/$(PDWN)

cp$(ALL_MOBJS)$(MODLIB)/kernel/$(PDWN)

endif

#makemodules_install的入口,进入子目录安装。

.PHONY:modules_install

modules_install:_modinst__\

$(patsubst%,_modinst_%,$(MOD_DIRS))

#

#Aruletodonothing

#

dummy:

#

#Thisisusefulfortesting

#

script:

$(SCRIPT)

#

#Thissetsversionsuffixesonexportedsymbols

#Separatetheobjectinto"normal"objectsand"exporting"objects

#Exportingobjectsare:allobjectsthatdefinesymboltables

#

ifdefCONFIG_MODULES

#list-multi列出那些由多个文件复合而成的模块;

#从编绎文件表和模块文件表中过滤出复合模块名。

multi-used:=$(filter$(list-multi),$(obj-y)$(obj-m))

#取复合模块的构成表。

multi-objs:=$(foreachm,$(multi-used),$($(basename$(m))-objs))

#求出需进行编译的总模块表。

active-objs:=$(sort$(multi-objs)$(obj-y)$(obj-m))

ifdefCONFIG_MODVERSIONS

ifneq"$(strip$(export-objs))"""

#如果有需要进行版本化的文件。

MODINCL=$(TOPDIR)/include/linux/modules

#The-woption(enablewarnings)forgenksymswillreturnherein2.1

#Sowherehasitgone?

#

#AddedtheSMPseparatortostopmoduleaccidentsbetweenuniprocessor

#andSMPIntelboxes-AC-frombitsbyMichaelChastain

#

ifdefCONFIG_SMP

genksyms_smp_prefix:=-psmp_

else

genksyms_smp_prefix:=

endif

#从源文件计算版本文件的规则。

$(MODINCL)/%.ver:%.c

@if[!-r$(MODINCL)/$*.stamp-o$(MODINCL)/$*.stamp-ot$<];then\

echo'$(CC)$(CFLAGS)-E-D__GENKSYMS__$<';\

echo'|$(GENKSYMS)$(genksyms_smp_prefix)-k

$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)>[email protected]';\

$(CC)$(CFLAGS)-E-D__GENKSYMS__$<\

|$(GENKSYMS)$(genksyms_smp_prefix)-k

$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)>[email protected];\

if[-r$@]&&cmp-s$@[email protected];thenecho$@isunchanged;rm-f

[email protected];\

[email protected]$@;[email protected]$@;fi;\

fi;touch$(MODINCL)/$*.stamp

#

将版本处理源文件的扩展名改为.ver,并加上完整的路径名,它们依赖于autoconf.h?br>?br>$(addprefix$(MODINCL)/,$(export-objs:.o=.ver)):

$(TOPDIR)/include/linux/autoconf.h

#updates.verfilesbutnotmodversions.h

#通过fastdep,逐个生成export-objs对应的版本文件。

fastdep:$(addprefix$(MODINCL)/,$(export-objs:.o=.ver))

#updates.verfilesandmodversions.hlikebefore(isthisneeded?)

#makedep过程的入口

dep:fastdepupdate-modverfile

endif#export-objs

#updatemodversions.h,butonlyifitwouldchange

#刷新版本文件的过程。

update-modverfile:

@(echo"#ifndef_LINUX_MODVERSIONS_H";\

echo"#define_LINUX_MODVERSIONS_H";\

echo"#include<linux/modsetver.h>";\

cd$(TOPDIR)/include/linux/modules;\

forfin*.ver;do\

if[-f$$f];thenecho"#include<linux/modules/$${f}>";fi;\

done;\

echo"#endif";\

)>$(TOPDIR)/include/linux/modversions.h.tmp

@if[-r$(TOPDIR)/include/linux/modversions.h]&&cmp-s

$(TOPDIR)/include/linux/modversions.h

$(TOPDIR)/include/linux/modversions.h.tmp;then\

echo$(TOPDIR)/include/linux/modversions.hwasnotupdated;\

rm-f$(TOPDIR)/include/linux/modversions.h.tmp;\

else\

echo$(TOPDIR)/include/linux/modversions.hwasupdated;\

mv-f$(TOPDIR)/include/linux/modversions.h.tmp

$(TOPDIR)/include/linux/modversions.h;\

fi

$(active-objs):$(TOPDIR)/include/linux/modversions.h

else

#如果没有配置版本化,modversions.h的内容。

$(TOPDIR)/include/linux/modversions.h:

@echo"#include<linux/modsetver.h>">$@

endif#CONFIG_MODVERSIONS

ifneq"$(strip$(export-objs))"""

#版本化目标文件的编绎方法。

$(export-objs):$(export-objs:.o=.c)$(TOPDIR)/include/linux/modversions.h

$(CC)$(CFLAGS)$(EXTRA_CFLAGS)$(CFLAGS_$@)-DEXPORT_SYMTAB-c$(@:.o=.c)

@(\

echo'ifeq($(strip$(subst$(comma),:,$(CFLAGS)$(EXTRA_CFLAGS)

$(CFLAGS_$@)-DEXPORT_SYMTAB)),$$(strip$$(subst$$(comma),:,$$(CFLAGS)

$$(EXTRA_CFLAGS)$$(CFLAGS_$@)-DEXPORT_SYMTAB)))';\

echo'FILES_FLAGS_UP_TO_DATE+=$@';\

echo'endif'\

)>$(dir$@)/.$(notdir$@).flags

endif

endif#CONFIG_MODULES

#

#includedependencyfilesiftheyexist

#

#嵌入源文件之间的依赖关系。

ifneq($(wildcard.depend),)

include.depend

endif

#嵌入头文件之间的依赖关系。

ifneq($(wildcard$(TOPDIR)/.hdepend),)

include$(TOPDIR)/.hdepend

endif

#

#Findfileswhoseflagshavechangedandforcerecompilation.

#Forsafety,thisworksintheconversedirection:

#everyfileisforced,exceptthosewhoseflagsarepositively

up-to-date.

#

#已经更新过的文件列表。

FILES_FLAGS_UP_TO_DATE:=

#Foruseinexpungingcommasfromflags,whichmungourchecking.

comma=,

#将当前目录下所有flags文件嵌入。

FILES_FLAGS_EXIST:=$(wildcard.*.flags)

ifneq($(FILES_FLAGS_EXIST),)

include$(FILES_FLAGS_EXIST)

endif

#将无需更新的文件从总的对象中删除。

FILES_FLAGS_CHANGED:=$(strip\

$(filter-out$(FILES_FLAGS_UP_TO_DATE),\

$(O_TARGET)$(L_TARGET)$(active-objs)\

))

#Akludge:.Sfilesdon'tgetflagdependencies(yet),

#becausethatwillinvolvechangingalotofMakefiles.Also

#suppressobjectfilesexplicitlylistedin$(IGNORE_FLAGS_OBJS).

#Thisallowshandlingofassemblyfilesthatgettranslatedinto

#multipleobjectfiles(seearch/ia64/lib/idiv.S,forexample).

#

#将由汇编文件生成的目件文件从FILES_FLAGS_CHANGED删除。

FILES_FLAGS_CHANGED:=$(strip\

$(filter-out$(patsubst%.S,%.o,$(wildcard*.S)

$(IGNORE_FLAGS_OBJS)),\

$(FILES_FLAGS_CHANGED)))

#将FILES_FLAGS_CHANGED设为目标。

ifneq($(FILES_FLAGS_CHANGED),)

$(FILES_FLAGS_CHANGED):dummy

endif

</pre>

[/code:1:974578564b]

相关推荐