歡迎您光臨本站 註冊首頁

Linux內核構建系統之四

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

回到我們的主框架上面來,討論完配置目標的處理后,就輪到框架中 "ifeq ($(config-targets),1)-endif" 塊的 else 部分了.這部分是為了處理那些構建目標以及和.config無關的目標,其對這些目標處理的代碼都位於框架中的E部分中.在E部分之前,有一個不小的 "ifeq ($(dot-config),1)-endif" 塊,我們暫先不去理會,且看這個 ifeq-endif 塊之前有一小段註釋:

   # ===========================================================================   # Build targets only - this includes vmlinux, arch specific targets, clean   # targets and others. In general all targets except *config targets.

這段註釋字面上的意思是說 "ifeq ($(config-targets),1)-endif" 塊的 else 部分處理的都是 Build targets,也就是除了配置目標之外的其他目標.注意他這裡對 Build targets 分類方法,其實和我們之前的分類方法是有差異的,他這裡所謂的 Build targets ,除了包括我們之前分類中所說的構建目標外,還包括之前我們說的和 .config 文件無關的那些目標.這其實是對同個東西的兩種不同分類罷了,不影響我們的分析.其實不管哪種分類,都改變不了在本 else 部分既處理真正的構建目標,又處理那些和 .config 文件無關目標的事實.

好,鑒於我們已有這樣的事實認同.那接下來理解前面說的那個不小的 "ifeq ($(dot-config),1)-endif" 塊就比較容易了.很顯然這個時候如果變數 dot-config 等於 1,那說明針對的是那些真正的構建目標,因為他們需要文件 .config 來完成真正的構建.而如果這個變數不為1,那麼針對的就是那些和 .config 完全無關的目標了.

當 dot-config 等於 1 時,構建系統會嘗試性的包含 include/config/auto.conf 文件.為什麼說是嘗試性的?這是 GNU make 做 "-include" 的特性.其意思和是否存在所包含的文件以及是否能根據所存在的規則去重新創建所包含文件有關係.

GNU Make 是這樣一個大致的讀取Makefile的流程:它讀入主Makefile,在讀的過程中,如果碰到 "include" 或 "-include",它就會包含對應的文件.如果對應的文件不存在,則暫時跳過做包含的地方,繼續讀入.待所有makefie都讀完后.GNU Make會考慮將每個makefile作為目標,在全局範圍內查找是否有能生成這些目標的規則,如果發現有一個makefile可以被一條規則生成,那麼GNU Make就會先生成這個makefile.生成后,GNU Make又會從零開始讀入主Makefile以及所有被包含的makefile,然後再檢查是否有makefile可以被remade….這樣一次又一次,直到所有的makefile都不需要再次生成了,它才處理依賴規則鏈.它之這樣做,是為了保證所有 makefile 都是 update-to-date 的.

那如果你的子makefile是被 "include" 所包含的,但是這個makefile本身不存在,且無法用一條規則去Remake出來,那麼 GNU Make就會報錯並退出.相反,如果你用的是 "-include",那麼 GNU Make就什麼都不做,就好象什麼也沒發生過那樣繼續處理後面的事情.,我們說這裡是嘗試性的,通俗點就是”有則包含,沒有也罷了“:).

接下來回到主框架,假如你的 make 命令是 "make ARCH=arm CROSS_COMPILE=arm-linux- zImage",那麼dot-config 等於 1,並且變數 KBUILD_EXTMOD 會等於空.構建系統又會先嘗試性的包含文件 include/config/auto.conf.cmd,然後繼續處理主框架中的 G1部分.我們先看看G1部分的代碼:

   # To avoid any implicit rule to kick in, define an empty command   $        

(KCONFIG_CONFIG) include/config/auto.conf.cmd: ; # If .config is newer than include/config/auto.conf, someone tinkered # with it and forgot to run make oldconfig. # if auto.conf.cmd is missing then we are probably in a cleaned tree so # we execute the config step to be sure to catch updated Kconfig files include/config/auto.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig

:),你看到這裡也許會露出些許微笑,因為你還記得那個 "-include include/config/auto.conf".沒錯,針對你的make命令,GNU Make在處理任何目標之前,它一定會努力的為我們生成文件 include/cofig/auto.conf ,用的就是這裡的這條對應規則.注意這裡的細節,GNU Make同樣也會努力重新用上面這條規則去生成 include/config/auto.conf.cmd 文件,可是無奈上面這條規則既沒有依賴,又沒有命令,GNU Make 是怎麼也沒辦法生成 auto.conf.cmd.那麼 auto.conf.cmd 文件又是從什麼地方生成呢?答案是在生成 include/config/auto.conf 的時候.稍後,我們會看到,它同樣在這個時候生成了另外一個文件 include/linux/autoconf.h.

既然已經說到這幾個文件了,那我就預先來回答一下上面提出來的問題.什麼問題?就是配置過程產生了用於記載配置結果的 .config ,那麼其中的配置結果由誰使用,又是如何使用的問題.我們說在整個Linux內核系統中,有兩方面的用戶需要關注所記載的配置結果.一個自然是內核構建系統,它需要根據配置結果產生具有指定功能的內核映像;另外一個就是大部分代碼為C語言代碼的Linux內核本身,它也需要用戶的配置結果,主要用來預處理C代碼.前者使用配置結果,並不是直接通過 .config 文件來的,而是將其轉換成兩個文件:include/config/auto.conf 和 include/config/auto.conf.cmd.後者也沒辦法直接通過 .config 文件來使用配置結果,它需要將其轉換成C語言頭文件的形式使用,在這裡就是文件 include/linux/autoconf.h .關於這三個文件的內容,我們稍後敘述.

還是回到上面處理 auto.conf 的規則中來,變數 KCONFIG_CONFIG 指代的就是配置文件 .config .目標 auto.conf 依賴於 .config 和 auto.conf.cmd .當GNU Make使用這條規則來remake auto.conf的時候,這兩個文件即使不存在也沒有關係.因為這樣的話,GNU Make 會因為有上面這條沒有依賴也沒有命令的規則而認為這兩個依賴是最新的,此時auto.conf規則的命令總是會被得到執行.

這是有意思的地方,因為這是不是意味著我可以在一個剛剛解壓出來的Linux內核目錄中直接用命令 "make ARCH=arm CROSS_COMPILE=arm-linux- zImage"來完成構建呢?咱們試一下:

從結果看前面都沒問題,直到用conf進行一步的配置時出現了錯誤.至於錯誤原因呢,是因為找不到文件 .config .我們可以找出出現錯誤的代碼,就在文件scripts/kconfig/conf.c 的main函數中,如下所示的代碼片段:

if (sync_kconfig) {                   name = conf_get_configname()        

; if (stat(name, &tmpstat)) { fprintf(stderr, _("***n" "*** You have not yet configured your kernel!n" "*** (missing kernel config file "%s")n" "***n" "*** Please run some configurator (e.g. "make oldconfig" orn"

"*** "make menuconfig" or "make xconfig").n" "***n"), name); exit(1); } }

就像前面講的,這個時候GNU Make會執行auto.conf規則的對應命令"$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig".而針對這個命令,配置程序 conf 會在main函數上面這個代碼片段的前面,將上面這個代碼片段中的 sync_kconfig 設置為1.接下來取得配置文件的名稱放在 name 變數裡面,再使用 stat 函數查詢這個文件的狀態.結果該函數返回非0值,是的GNU Make認為配置文件.config不存在而報錯.

回到處理 auto.conf 的那條規則上來.考慮一下如果這個時候 auto.conf 文件和 .config 文件都存在,但是 .config 比較新的話會怎麼樣呢?想想,這會是一個怎麼樣的 Scenario呢?如果你剛剛編譯過內核並已生成映像文件,這個時候你從你朋友那裡取來一個 .config 文件,並且將其放到了你的內核源碼樹下面,這個時候就產生了這種情況.這個時候你需要根據你的內核的版本來做處理.如果你的版本和你的朋友一樣,那沒問題,你大可以直接 "make ... zImage".但是,如果你們的版本不一樣,你最好做下 "make ... oldconfig".這個命令的作用是針對原來版本內核的配置文件設置來配置新版本的內核.新版內核中沒變的配置選項可以設置成原來的值,但是新添加的配置項就需要你自己手動設置了.

再次回到處理 auto.conf 的那條規則上來,我們看到它的命令 "$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig",這個命令最終會導致GNU Make執行文件 scripts/kconfig/Makefile 中針對目標 silentoldconfig 的命令:

$(obj)/conf -s arch/arm/Kconfig

conf配置程序在前面已經有所提及,其對應的代碼都在目錄 scripts/kconfig/ 中.conf 的主函數main即定義在 conf.c 文件中.其實,目標silentoldconfig 和 目標oldconfig類似,只不過它多了生成auto.conf、auto.conf.cmd以及autoconf.h等三個文件的任務.這是怎麼做到的?答案就在conf.c文件中 main 函數的一段代碼:

int main(int ac, char **av)   {           int opt;           const char *name;           struct stat tmpstat;               .....           .....                      if (sync_kconfig) {        

/* silentoldconfig is used during the build so we shall update autoconf. * All other commands are only used to generate a config. */ if (conf_get_changed() && conf_write(NULL)) { fprintf(stderr, _("n*** Error during writing of the kernel configuration.nn")); exit(1); } if

(conf_write_autoconf()) { fprintf(stderr, _("n*** Error during update of the kernel configuration.nn")); return 1; } } else { if (conf_write(NULL)) { fprintf

(stderr, _("n*** Error during writing of the kernel configuration.nn")); exit(1); } } return 0; }

前面我們已經介紹過,當用 conf 處理 silentoldconfig 時,變數sync_kconfig會被設置為1.實際上,也只有處理此目標時,它才會被設置成1,其他的目標都不會.對於oldconfig、menuconfig等目標來說,conf程序會直接調用函數 conf_write 將配置結果寫到配置文件 .config 中去.該函數的定義在同目錄的另外一個文件 confdata.c 中,這裡我們不再細究下去了,你可以自己探究.而對於 silentoldconfig 目標來說,conf 程序除了調用 conf_write 來寫 .config 文件外,它還會調用 conf_write_autoconf 函數來完成 auto.conf、auto.conf.cmd 和 autoconf.h 三個文件的生成.我們且來看看同樣定義在 confdata.c 文件中的 conf_write_autoconf 函數:

該函數一開始就寫 include/config/auto.conf.cmd 文件,然後將對應的內容寫入到兩個臨時文件 .tmpconfig 和 .tmpconfig.h 中,並在將這兩個文件分別重新命名為include/config/auto.conf 和 include/linux/autoconf.h.注意上面代碼中對 conf_split_config 函數的調用,其目的是在目錄 include/config 中產生一系列的頭文件,至於這些頭文件如何產生的、以及它們的作用,我們這裡先留下一個伏筆,回頭再來探究.

知道了這幾個文件是如何產生的.我們再來注意一下它們的內容.文件 auto.conf.cmd 裡面記錄的是 auto.conf 目標的相關依賴,而文件 auto.conf 和文件 autoconf.h 的內容和 .config 文件的內容直接相關.舉例來說,如果你找到有個定義在 .config 文件里的變數形式:CONFIG_MMU=y 表示要將虛擬內存管理的功能編譯進內核,那麼在 auto.conf 裡面 也會有完全相同的定義形式:CONFIG_MMU=y.而在文件 autoconf.h 文件中,則會有一個這樣形式的宏定義:#define CONFIG_MMU 1.假如.config中的形式是 CONFIG_IKCONFIG=m,那麼在 auto.conf 中的形式也會是:CONFIG_IKCONFIG=m ,而在文件 autoconf.h 中的定義形式則變成:#define CONFIG_IKCONFIG_MODULE 1 .

前面說的是當 KBUILD_EXTMOD 為空的時候,那麼當這個變數取值不為空(也就是我們嘗試在用 "make ... -M=..." 之類的命令來編譯外部模塊)時,GNU Make 只是簡單的處理上面框架中的G2部分.編譯外部模塊時並不需要關心 auto.conf/autoconf.h 是不是最新的.它只是檢查他們是否存在,如果不存在,它就報錯並退出.

之前我們說過構建目標以及和.config無關的目標是混在一塊處理的.那當 dot-config 等於 1 時,處理的是構建目標,而在 dot-config 等於 0 時,構建系統處理的是那些和文件 .config沒有關係的目標.注意看我們的框架,處理和.config無關目標的時候,構建系統只是簡單的針對 auto.conf 目標定義了一個既沒有依賴又沒有命令的規則:

   # Dummy target needed, because used as prerequisite   include/config/auto.conf: ;

這樣做的意思是因為 auto.conf 在後面會為多個其他目標所依賴.我們在這裡只是登記一下說:“嘿,兄弟我(auto.conf)已經是最新的了,你們別再管我了,只管繼續做你們自己的事情就好.”

關於對上面框架代碼E部分中和 .config 文件無關目標的處理,這裡限於篇幅,我們就不再多講,您可自己研究.

【注意,全文可至這裡瀏覽: http://yihect.juliantec.info/julblog//post/4/20

本文出自 「巨立安技術老師寫的文章」 博客,請務必保留此出處http://juliantec.blog.51cto.com/2773953/500223


[火星人 ] Linux內核構建系統之四已經有645次圍觀

http://coctec.com/docs/linux/show-post-48141.html