注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

内核构建系统--目录递归  

2011-12-31 23:20:48|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、文件/目录添加
现在内核对于编译的控制不仅精确到了单个文件,而且对每个文件中的单个功能都可以进行精确控制,在编译的时候配置选项不同也会触发读该选项有依赖的文件的重新编译。对于编译文件的控制体现在如果想把一个文件添加到内核中就必须修改Makefile,将该文件对应的.o添加到构建的某个依赖中。这一点是一个非常有用的特征,因为对于一个经常被开发或者修改的功能,本地很可能会有一些多余的文件,例如,我们经常就是把原始文件留一个备份,名字为“复件XXX.c”。如果使用粗放的wildcard来搜索一个文件夹下所有文件的话,不仅会搜索到这个复件文件,更为严重的是会造成重定义链接错误。
向内核中某个源文件添加一些代码这个应该是最为基本的方法了,估计是个程序员,只要不脑残都可以修改。如果添加一个新的源文件到内核的构建中,那么这个也比较好办,那就是在随便一个Makefile中的obj-y变量中添加这个源文件对应的.o,例如obj-y+=MyFile.o,从而让MyFile文件被直接编译到内核中。这就是框架的力量,让我们可以快速的照猫画虎,简单而且不易出错。
但是添加一个目录呢?可能很多人没有做过这么操作,因为没有这种需要。我们很多的修改都是在原有文件的基础上进行一些修改,或者添加一个新的文件到内核,这些都是有很多例子可以随手拈来。但是对于目录的操作就用的比较少,因为添加一个目录就意味着一个比较大的模块,而内核的模块添加现在应该算是比较少的了。
二、构建结构
1、构建基础
通过make V=1构建内核,我们可以看到内核构建的主Makefile并不是各层目录下的Makefile
make -f scripts/Makefile.build obj=drivers
make -f scripts/Makefile.build obj=drivers/acpi
make -f scripts/Makefile.build obj=drivers/acpi/dispatcher
make -f scripts/Makefile.build obj=drivers/acpi/events
例如,其中的各个主Makefile统一为scripts/Makefile.build文件,而不是各个文件夹(例如drivers\acpi\dispatcher)下的Makefile,虽然这个名字是make程序的默认主入口文件(也就是如果make不通过-f明确说明使用的makefile,那么将搜索该文件)。而各个模块的Makefile只是作为主Makefile的一个普通文件被包含进来。
但是在drivers\acpi\Makefile中只看到了它的直接子文件夹
obj-y                += osl.o utils.o \
                   dispatcher/ events/ executer/ hardware/ \
                   namespace/ parser/ resources/ tables/ \
                   utilities/
也就是各层只包含了自己的直接子目录,所以在make的命令行中看到的完整路径应该是需要make来自己拼接出来,也就是需要有一个递归下降的过程。
在Makefile.build的最开始有src := $(obj),将会读取命令行中传入的obj变量,该变量定义了将要Make的文件夹,这个应该就是一个完整路径,例如上面看到的driver/acpi/events文件夹,在之后将会包含入各个子文件夹定义的Makefile文件,其代码同样位于Makefile.build文件中
# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) 如果obj是一个绝对路径则使用src,否则添加srctree,形成绝对路径
include $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, $(kbuild-dir)/Makefile)这里在用户obj指定的文件夹里搜索Kbuild或者Makefile,如果两者都存在,则优先使用Kbuild文件。
这里可能比较奇怪的是,为什么明明是源文件,为什么变量的名字却是obj。这个事实上是一个镜像的关系,就是obj路径下的.o文件是和源文件一一对应的。例如,在drivers/acpi/dispatcher/dsinit.c,就有一个obj文件夹下的drivers/acpi/dispatcher/dsinit.o相对应,所以obj变量和src变量是一致的。
2、递归规则
同样是在Makefile.build文件中,有下面的规则,这个就是make递归到子文件夹的指令所在
# Descending
# ---------------------------------------------------------------------------

PHONY += $(subdir-ym)
$(subdir-ym):
    $(Q)$(MAKE) $(build)=$@
subdir-ym的依赖由来为:
PHONY := __build
__build:  #这里的__build是整个Makefile.build文件的第一个目标,所以也就是每个make的默认目标,从这个依赖关系链中我们将会看到对subdir-ym变量。
……
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
     $(if $(KBUILD_MODULES),$(obj-m)) \
     $(subdir-ym) $(always)
3、subdir_ym变量的定义
在Makefile.lib(该文件被Makefile.build直接包含)文件中
__subdir-y    := $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y    += $(__subdir-y)
__subdir-m    := $(patsubst %/,%,$(filter %/, $(obj-m)))
subdir-m    += $(__subdir-m)
obj-y        := $(patsubst %/, %/built-in.o, $(obj-y))
obj-m        := $(filter-out %/, $(obj-m))
其中的subdir-m和subdir-y是两个类似操作,只是它们被添加的变量一个是obj-m,一个是obj-y。__subdir-y是通过搜索obj-y变量中最后一个字符为路径分隔符的单词,例如linux-2.6.21\drivers\acpi\Makefile中定义的
obj-y                += osl.o utils.o \
                   dispatcher/ events/ executer/ hardware/ \
                   namespace/ parser/ resources/ tables/ \
                   utilities/
dispatcher,events等它们都是以路径分隔符结束的单词,所以这里的__subdir-y将会被替换为dispatcher,events等(即删除这些变量最后的路径分隔符)。这里也隐含了一个规则:所有的子文件夹必须以路径分隔符结束,反过来说,以路径分隔符结束的变量将会被认为是一个文件夹。
在Makefile.lib中接下来对subdir-ym进行了第一次赋值:
subdir-ym    := $(sort $(subdir-y) $(subdir-m))
但是根据分析,这里是Makefile所在文件夹的一个直接子目录,而没有包含绝对路径,所以之后应该有一个地方将这个路径拼接以形成一个更为完整的路径,那就是接下来的指令
subdir-ym    := $(addprefix $(obj)/,$(subdir-ym))
可以看到,它将命令行中传入的obj变量添加在了所有单词的前面。还是以刚才的例子说明,看执行的路径
make -f scripts/Makefile.build obj=drivers
make -f scripts/Makefile.build obj=drivers/acpi
make -f scripts/Makefile.build obj=drivers/acpi/dispatcher
可以看到,里面的路径是一个层次迭代,逐渐变深的过程,所以obj变量将会在make的递归执行中不断累积,从而形成一个完整路径。
4、单个源文件生成目标文件规则
# Built-in and composite module parts

%.o: %.c FORCE
    $(call cmd,force_checksrc)
    $(call if_changed_rule,cc_o_c)

由于在__build目标中添加了所有的.o文件的依赖,所以此处就可以根据.c文件生成对应的目标文件,此处的两个%通配符也体现了之前说的那个镜像的含义,那就是源文件的目录结构和目标文件的目录结构对应的关系。这里的

if_changed_rule = $(if $(strip $(any-prereq) $(arg-check) ),                 \
    @set -e;                                                             \
    $(rule_$(1)))
这里事实上使用了两个规则变量,一个是rule_cc_o_c变量,一个是cmd_cc_o_c变量,不过还好它们都定义在Makefile.build文件中
cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $<这个规则是用来将一个c文件转换为o文件的真正命令。
……
define rule_cc_o_c  这个整个rule都是make即将执行的命令。
    $(call echo-cmd,checksrc) $(cmd_checksrc)              \
    $(call echo-cmd,cc_o_c) $(cmd_cc_o_c); 这里的cmd_cc_o_c展开为真正的编译动作                 \
    $(cmd_modversions)                          \
    scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' >    \这里将完整编译命令写入.o文件对应的cmd文件,从而当之后编译选项变化之后可以触发再次编译。
                                                  $(dot-target).tmp;  \
    rm -f $(depfile);                          \
    mv -f $(dot-target).tmp $(dot-target).cmd
endef
5、builtin.o或者lib.a规则
Makefile.build文件中

ifneq ($(strip $(lib-y) $(lib-m) $(lib-n) $(lib-)),) 如果说一个文件夹中定义了lib-y,lib-m lib-n说明该文件夹可能生成一个库文件,所以目标添加一个lib.a
lib-target := $(obj)/lib.a
endif

ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(lib-target)),)如果该文件夹将会生成obj文件,则添加一个built-in.o目标,这个目标不需要在各层的Makefile里自己定义,当有obj-y是会自动生成该目标。
builtin-target := $(obj)/built-in.o
endif
在接下来
$(builtin-target): $(obj-y) FORCE
    $(call if_changed,link_o_target)
可以看到,一个模块中所有的obj-y中定义的变量都将会被添加到built-in.o中。
如果想看lib-y的使用,可以参考lib/Makefile中对于lib-y的定义。
6、上层链入自己直接子文件夹的built-in.o
Makefile.lib
obj-y        := $(patsubst %/, %/built-in.o, $(obj-y))
也就是将obj-y中所有的目录下的built-in.o作为自己的一个链接输入,当前文件夹下的目标文件一起参与本目录下built-in.o的生成。
7、vmlinux的生成
这个是在顶层makefile中实现
各个子模块的输入通过vmlinux-main变量来计算和添加,这个文件只管理自己的直接子文件夹,所以还算是比较简单的。
三、自己添加的例子
作为练习,有个很好的例子,就是vmware的vmnet模块,可能尝试将它直接编入内核或者以module的形式加入内核,该模块的源代码在
ftp://ftp.technion.ac.il/pub/supported/asri/general/LINUXexplo/vmware/vmware-any-any-update104/
  评论这张
 
阅读(1148)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017