閒置了這麼久的博客,也要拾起來了(為自己的懶惰感到羞愧)。最近一段時間一直和linux的核心代碼打交道,也積累了一些經驗,現在想把自己對linux的理解寫下來,也歡迎朋友指正。
我認為對linux代碼的閱讀首先要讀懂她的Makefile,Makefile是代碼的外部框架,熟悉她會讓我們對Linux的代碼層次有一定的了解,這對我們深讀代碼很有幫助。工作過程中接手一個新項目的維護工作時,我也會首先讀她的Makefile,因為這在一定程序上代表了項目開發者的架構思路,這也是我要學習的地方。
好了,現在我們開始我們的代碼之旅。。。
編譯內核首先要執行make menuconfig,那我們就從這條命令開始。(P.S. 內核版本 3.8.0)
[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
[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$(Q)$(MAKE) $(build)=scripts/basicbuild定義在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
無執行代碼。
$(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的其他編譯目標,上文中如有不正確的部分歡迎指正。