歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

Linux Kernel Makefile解析(一)

閒置了這麼久的博客,也要拾起來了(為自己的懶惰感到羞愧)。最近一段時間一直和linux的核心代碼打交道,也積累了一些經驗,現在想把自己對linux的理解寫下來,也歡迎朋友指正。

我認為對linux代碼的閱讀首先要讀懂她的Makefile,Makefile是代碼的外部框架,熟悉她會讓我們對Linux的代碼層次有一定的了解,這對我們深讀代碼很有幫助。工作過程中接手一個新項目的維護工作時,我也會首先讀她的Makefile,因為這在一定程序上代表了項目開發者的架構思路,這也是我要學習的地方。

好了,現在我們開始我們的代碼之旅。。。

編譯內核首先要執行make menuconfig,那我們就從這條命令開始。(P.S. 內核版本 3.8.0)

1. Makefile變量的初始化

KBUILD_EXTMOD 為空,因為命令行中沒有”M= xxx“,KBUILD_SRC為空,因為沒有“O = xxx“。
[code]
no-dot-config-targets := clean mrproper distclean \
             cscope gtags TAGS tags help %docs check% coccicheck \
             $(version_h) headers_% archheaders archscripts \
            no-dot-config-targets kernelversion %src-pkg

config-targets := 0
mixed-targets  := 0
dot-config     := 1

ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
    ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
        # MAKECMDGOALS 中僅包含no-dot-config-targets提供的命令
        dot-config := 0
    endif
endif

ifeq ($(KBUILD_EXTMOD),)
        ifneq ($(filter config %config,$(MAKECMDGOALS)),)
        # MAKECMDGOALS 包含config
                config-targets := 1
                ifneq ($(filter-out config %config,$(MAKECMDGOALS)),)
                # MAKECMDGOALS 還包含其他命令
                        mixed-targets := 1
                endif

endif

由上面代碼可以得出如下幾個變量的值:

config-targets = 1

mixed-targets = 0

dot-config = 1

2. 確定執行規則

根據前面得到的幾個變量,那麼就可以確定執行Makefile line487~505的代碼了。
[code]include $(srctree)/arch/$(SRCARCH)/Makefile
export KBUILD_DEFCONFIG KBUILD_KCONFIG

config: scripts_basic outputmakefile FORCE
    $(Q)mkdir -p include/linux include/config
    $(Q)$(MAKE) $(build)=scripts/kconfig $@

%config: scripts_basic outputmakefile FORCE
    $(Q)mkdir -p include/linux include/config
    $(Q)$(MAKE) $(build)=scripts/kconfig $@

我們要先執行依賴規則 scripts_basic 和 outputmakefile,而後執行target。查找代碼可以確認依賴規則如下:

[code]# Makefile line 418
scripts_basic:
    $(Q)$(MAKE) $(build)=scripts/basic
    $(Q)rm -f .tmp_quiet_recordmcount

# Makefile line 429
outputmakefile:
ifneq ($(KBUILD_SRC),) # 不執行
    $(Q)ln -fsn $(srctree) source
    $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
        $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif

3. 執行依賴規則

scripts_basic

$(Q)$(MAKE) $(build)=scripts/basic
build定義在scripts/Kbuild.include中,含義如下
build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj

那麼這行代碼展開即是

make -f scripts/Makefile.build obj=scripts/basic
,跳到scripts/Makefile.build中執行,條件是obj=scripts/basic那我們把目光轉向scripts/Makefile.build

[code]src := $(obj) 

kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)
查找scripts/basic目錄下所有的Kbuild或者Makefile,include 它們,然後編譯其中的小模塊,在這裡編譯出可執行程序fixdep.

outputmakefile

無執行代碼。

4. 執行目標規則

$(Q)mkdir -p include/linux include/config
# 創建目錄
$(Q)$(MAKE) $(build)=scripts/kconfig $@

命令展開為

make -f scripts/Makefile.build obj=scripts/kconfig menuconfig
跳到scripts/Makefile.build中執行,條件是obj=scripts/kconfig,目標是menuconfig。

繼續解析,執行執行scripts/kconfig中的Makefile,目標規則menuconfig。

menuconfig: $(obj)/mconf 

$< $(Kconfig)
其中$(obj)/mconf的創建是Kernel Makefile中最基礎也是非常精彩的部分,linux中大部分子模塊均是按照這樣的方式組織的。

在scripts/kconfig/Makefile中有如下定義:

mconf-objs     := mconf.o zconf.tab.o $(lxdialog) 

lxdialog := lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o 

lxdialog += lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o 

hostprogs-y += mconf
注意這裡的mconf-objs和hostprogs-y

進入scripts/Makefile.host

__hostprogs := $(sort $(hostprogs-y) $(hostprogs-m)) 

host-cmulti := $(foreach m,$(__hostprogs),\ 

                $(if $($(m)-cxxobjs),,$(if $($(m)-objs),$(m)))) 

host-cobjs  := $(sort $(foreach m,$(__hostprogs),$($(m)-objs))) 

__hostprogs     := $(addprefix $(obj)/,$(__hostprogs)) 

host-cmulti := $(addprefix $(obj)/,$(host-cmulti)) 

host-cobjs  := $(addprefix $(obj)/,$(host-cobjs))
這裡

__hostprogs := scripts/kconfig/mconf

host-cmulti := scripts/kconfig/mconf

host-cobjs := mconf.o zconf.tab.o lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o(不用關心這些.o文件是什麼,我們只關心makefile的執行流程,這裡在每個前面要加上前綴$(obj),即”scripts/kconfig/”)

通過如下代碼編譯成可執行文件

cmd_host-cmulti = $(HOSTCC) $(HOSTLDFLAGS) -o $@ \ 

          $(addprefix $(obj)/,$($(@F)-objs)) \ 

          $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F)) 

$(host-cmulti): $(obj)/%: $(host-cobjs) $(host-cshlib) FORCE 

$(call if_changed,host-cmulti)
上面第二個規則匹配目標mconf,而$(host-cobjs)的匹配規則如下

cmd_host-cobjs  = $(HOSTCC) $(hostc_flags) -c -o $@ $< 

$(host-cobjs): $(obj)/%.o: $(src)/%.c FORCE 

$(call if_changed_dep,host-cobjs)
實質上是將.c 文件編譯成 .o文件,而後調用cmd_host-cobjs生成
$(host-cobjs)
各個目標文件。返回
$(host-cmulti)
規則,調用cmd_host-cmulti生成最終的可執行文件mconf,歎為觀止啊。

最後返回最初的menuconfig規則,現在只剩下最後一條命令:

$< $(Kconfig)
,展開為scripts/Kconfig/mconf Kconfig,這是一條shell命令,使用剛編出來的可執行文件mconf來完成make menuconfig的最後步驟,即我們看到的圖形化內核配置菜單,至此全部完成。後面介紹Linux Makefile的其他編譯目標,上文中如有不正確的部分歡迎指正。

Copyright © Linux教程網 All Rights Reserved