閒置了這麼久的博客,也要拾起來了(為自己的懶惰感到羞愧)。最近一段時間一直和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的其他編譯目標,上文中如有不正確的部分歡迎指正。