目錄
Linux 工程目錄分析
頂層Makefile詳解
make xxx_defconfig過程
Makefile.build腳本分析
make過程
built-in.o文件編譯生成過程
make zImage過程
Linux 工程目錄分析
將正點原子提供的Linux源碼進行解壓,解壓完成以后的目錄如圖所示:
?
?圖就是正點原子提供的未編譯的Linux源碼目錄文件,我們在分析Linux之前一定要先在Ubuntu中編譯一下Linux,因為編譯過程會生成一些文件,而生成的這些恰恰是分析Linux不可或缺的文件。
編譯后的Linux目錄如圖所示:?
?
?
?
arch目錄
這個目錄是和架構(gòu)有關的目錄,比如arm、arm64、avr32、x86等等架構(gòu)。每種架構(gòu)都對應一個目錄,在這些目錄中又有很多子目錄,比如 boot、common、configs等等,以arch/arm為例,其子目錄如圖所示:
?
?圖是arch/arm的一部分子目錄,這些子目錄用于控制系統(tǒng)引導、系統(tǒng)調(diào)用、動態(tài)調(diào)·頻、主頻設置等。arch/arm/configs目錄是不同平臺的默認配置文件: xxx_defconfig,如圖所示:
在arch/arm/configs中就包含有I.MX6U-ALPHA開發(fā)板的默認配置文件: imx_v7_defconfig,執(zhí)行“make imx_v7_defconfig”即可完成配置。arch/arm/boot/dts 目錄里面是對應開發(fā)平臺的設備樹文件,正點原子I.MX6U-ALPHA開發(fā)板對應的設備樹文件如圖所示:
?arch/arm/boot目錄下會保存編譯出來的Image和zImage鏡像文件,而zimage就是我們要用的linux鏡像文件。
arch/arm/mach-xxx目錄分別為相應平臺的驅(qū)動和初始化文件,比如mach-imx目錄里面就是I.MX系列CPU的驅(qū)動和初始化文件。
block目錄
block是Linux下塊設備目錄,像SD卡、EMMC、NAND、硬盤等存儲設備就屬于塊設備,block目錄中存放著管理塊設備的相關文件。
crypto目錄
.crypto目錄里面存放著加密文件,比如常見的crc、crc32、md4、md5、hash等加密算法。
Documentation目錄
此目錄里面存放著Linux相關的文檔,如果要想了解Linux某個功能模塊或驅(qū)動架構(gòu)的功能,就可以在Documentation目錄中查找有沒有對應的文檔。
drivers目錄
驅(qū)動目錄文件,此目錄根據(jù)驅(qū)動類型的不同,分門別類進行整理,比如drivers/i2c就是12C相關驅(qū)動目錄, drivers/gpio就是GPIO相關的驅(qū)動目錄,這是我們學習的重點。
?firmware目錄
此目錄用于存放固件。
fs目錄
此目錄存放文件系統(tǒng),比如fs/ext2、fs/ext4、fs/f2fs等,分別是ext2、ext4和f2fs等文件系統(tǒng)。
include目錄
頭文件目錄。
init目錄
此目錄存放Linux內(nèi)核啟動的時候初始化代碼。
ipc目錄
IPC為進程間通信,ipe目錄是進程間通信的具體實現(xiàn)代碼。
kernel目錄
Linux內(nèi)核代碼。
lib目錄
lib是庫的意思,lib目錄都是一些公用的庫函。
mm目錄
此目錄存放內(nèi)存管理相關代碼。
net目錄
此目錄存放網(wǎng)絡相關代碼。
samples目錄
此目錄存放一些示例代碼文件。
scripts目錄
腳本目錄,Linux 編譯的時候會用到很多腳本文件,這些腳本文件就保存在此目錄中。
security目錄
此目錄存放安全相關的文件。
sound目錄
此目錄存放音頻相關驅(qū)動文件,音頻驅(qū)動文件并沒有存放到drivers目錄中,而是單獨的目錄。
tools目錄
此目錄存放一些編譯的時候使用到的工具。
usr目錄
此目錄存放與 initramfs 有關的代碼。
virt目錄
此目錄存放虛擬機相關文件。
.config文件
跟uboot一樣, .config保存著Linux最終的配置信息,編譯Linux的時候會讀取此文件中的配置信息。最終根據(jù)配置信息來選擇編譯Linux哪些模塊,哪些功能。
Kbuild文件
有些Makefile會讀取此文件。
Kconfig文件
圖形化配置界面的配置文件。
Makefile文件
Linux頂層Makefile文件,建議好好閱讀一下此文件。
README文件
此文件詳細講解了如何編譯Linux源碼,以及Linux源碼的目錄信息,建議仔細閱讀一下此文件。
關于Linux源碼目錄就分析到這里,接下來分析一下Linux的頂層Makefile。
頂層Makefile詳解
Linux的頂層Makefile和uboot的頂層Makefile非常相似,因為uboot 參考了Linux,前602行幾乎一樣,所以前面部分我們大致看一下就行了。
版本號
頂層Makefile一開始就是Linux內(nèi)核的版本號,如下所示:
?
可以看出,Linux內(nèi)核版本號為4.1.15。?
MAKEFLAGS變量
MAKEFLAGS變量設置如下所示:
?
命令輸出
Linux 編譯的時候也可以通過“V=1”來輸出完整的命令,這個和 uboot 一樣,相關代碼如下所示:
?
?
靜默輸出
Linux編譯的時候使用"make-s”就可實現(xiàn)靜默編譯,編譯的時候就不會打印任何的信息,同uboot一樣,相關代碼如下:
?
設置編譯結(jié)果輸出目錄
Linux編譯的時候使用"O-xxx”即可將編譯產(chǎn)生的過程文件輸出到指定的目錄中,相關代碼如下:
?
代碼檢查
Linux也支持代碼檢查,使用命令“make C=1”使能代碼檢查,檢查那些需要重新編譯的文件。"make C=2”用于檢查所有的源碼文件,頂層Makefile中的代碼如下:
?
模塊編譯
Linux允許單獨編譯某個模塊,使用命令"make M=dir”即可,舊語法"make SUBDIRS=dir"也是支持的。頂層Makefile中的代碼如下:
?
?外部模塊編譯過程和uboot也一樣,最終導出sretree、objtree和VPATH這三個變量的值,其中srctree=.,也就是當前目錄, objtree同樣為".”。
?
設置目標架構(gòu)和交叉編譯器
同 uboot一樣,Linux編譯的時候需要設置目標板架構(gòu)ARCH 和交叉編譯器CROSS_COMPILE,在頂層Makefile中代碼如下:
?為了方便,一般直接修改頂層Makefile中的ARCH和CROSS_COMPILE,直接將其設置為對應的架構(gòu)和編譯器,將ARCH設置為為arm, CROSS-COMPILE設置為armlinux-gnueabihf-,如下所示:
?設置好以后我們就可以使用如下命令編譯Linux了:
?
調(diào)用scripts/Kbuild.include文件
同uboot一樣, Linux頂層Makefile也會調(diào)用文件scripts/Kbuild.include,頂層Makefile相應代碼如下:
?
交叉編譯工具變量設置
頂層Makefile中其他和交叉編譯器有關的變量設置如下:
?
?LA、LD、CC等這些都是交叉編譯器所使用的工具。
頭文件路徑變量
頂層Makefile定義了兩個變量保存頭文件路徑: USERINCLUDE和LINUXINCLUDE,相關代碼如下:
?
?第381~386行是USERINCLUDE是UAPI相關的頭文件路徑,第390~396行是LINUXINCLUDE是Linux內(nèi)核源碼的頭文件路徑。重點來看一下LINUXINCLUDE,其中srctree=., hdr-arch-arm, KBUILD_SRC 為空,因此, 將USERINCLUDE和LINUXINCLUDE展開以后為:
?
?
?
導出變量
頂層Makefile會導出很多變量給子Makefile使用,導出的這些變量如下:
?
?
make xxx_defconfig過程
第一次編譯Linux之前都要使用"make xxx_defconfig"先配置Linux內(nèi)核,在頂層Makefile中有"%config”這個目標,如下所示:
?
?
?
?第490-507行和uboot一樣,都是設置定義變量config-targets、mixed-targets和dot-config的值,最終這三個變量的值為:
因為config-targets=1,因此第534行~541行成立。第534行引用arch/arm/Makefile 這個文件,這個文件很重要,因為zlmage, ulmage等這些文件就是由arch/arm/Makefile來生成的。
第535行導出變量KBUILD_DEFCONFIG KBUILD_KCONFIG。
第537行,沒有目標與之匹配,因此不執(zhí)行。
第540行, "make xxx_defconfig"與目標"%config"匹配,因此執(zhí)行。"%config"依賴" scripts_basic,outputmakefile和FORCE,"%config"真正有意義的依賴就只有scripts_basic,scripts_basic的規(guī)則如下:
build定義在文件 scripts/Kbuild.include中
值為build :=-f S(srctree)/scripts/Makefile.buildobj,
因此將示例代碼展開就是:
?接著回到示例代碼35.5.1.1的目標“%config”處,內(nèi)容如下:
?
?將命令展開就是:
?
Makefile.build腳本分析
從可知,"make xxx_defconfig"配置Linux的時候如下兩行命令會執(zhí)行腳本scripts/Makefile.build:
?依次來分析一下:
scripts_basic目標對應的命令
scripts-basic目標對應的命令為: @make-f./scripts/Makefile.build obj-scripts/basic。打開文件scripts/Makefile.build,有如下代碼:
?將kbuild-dir展開后為:
將kbuild-file展開后為:?
?最后將44行展開,即:
?繼續(xù)分析scripts/Makefile.build,如下代碼:
build是默認目標,因為命令"@make-f /scripts/Makefile.build obj=scripts/basic"沒有指定目標,所以會使用到默認目標_build。在頂層 Makefile 中,KBUILD_BUILTIN為1,KBUILD MODULES為空,因此展開后目標build為:?
可以看出目標_build有5個依賴:?builtin-target、lib-target、extra-y、subdir-ym和always。這5個依賴的具體內(nèi)容如下:?
?只有always有效,因此_build最終為:
?
%config目標對應的命令
目標對應的命令為:@make -f ./scripts/Makefile.build obj=scripts/kconfig%configxxx defconfig,此命令會使用到的各個變量值如下:
?可以看出, Makefile.build會讀取scripts/kconfig/Makefile中的內(nèi)容,此文件有如下所示內(nèi)容:
?目標% defconfig與xxx_defconfig匹配,所以會執(zhí)行這條規(guī)則,將其展開就是:
?%-defconfig依賴scripts/kconfig/conf,所以會編譯scripts/kconfig/conf.c生成conf這個軟件。此軟件就會將%_defconfig 中的配置輸出到.config文件中,最終生成Linux kemel根目錄下的.config文件。
make過程
使用命令"make xxx_defconfig"配置好Linux內(nèi)核以后就可以使用"make"或者"make all"命令進行編譯。頂層Makefile有如下代碼:
?第126行,_all是默認目標,如果使用命令“make”編譯Linux的話此目標就會被匹配。
第193行,如果 KBUILD_EXTMOD為空的話194行的代碼成立。
第194行,默認目標_all依賴all。
第608行,目標all依賴vmlinux,所以接下來的重點就是vmlinux!
頂層Makefile中有如下代碼:
?從第920行可以看出目標vmlinux依賴scripts/link-vmlinux.sh $(vmlinux-deps) FORCE。第912 行定義了vmlinux-deps,值為:
第905行, KBUILD VMLINUX INIT-S(head-y) S(init-y)。
第906行, KBUILD_VMLINUX_MAIN=$(core-y) $(libs-y) $(drivers-y) $(net-y)。
第907行, KBUILD_LDS=arch/$(SRCARCH)/kernel/vmlinux.lds,其中SRCARCH=arm,因此 KBUILD LDS=arch/arm/kernel/vmlinux.lds。
綜上所述, vmlinux的依賴為: scripts/link-vmlinux.sh、S(head-y) 、S(init-y)、$(core-y)、$(libs-y)、$(drivers-y)、$(net-y)、arch/arm/kernel/vmlinux.lds和FORCE
第933行的命令用于鏈接生成vmlinux.量的值。
重點來看一下$(head-y)、S(init-y)、S(core-y) 、$(libs-y)、S(drivers-y)和$(net-y)這六個變
?
?
head-y
head-y定義在文件arch/arm/Makefile中,內(nèi)容如下:
?當不使能 MMU 的話 MMUEXT-nommu,如果使能MMU的話為空,因此head-y最終的值為:
?
init-y、drivers-y和net-y
在頂層Makefile中有如下代碼:
?從示例代碼35.5.3.4可知,init-y、libs-y、drivers-y和net-y最終的值為:
libs-y
libs-y基本和init-y一樣,在頂層Makefile中存在如下代碼:
?根據(jù)示例代碼35.5.3.5可知,libs-y應該等于“l(fā)ib.a built-in.o”,這個只正確了一部分!因為在arch/arm/Makefile中會向libs-y 中追加一些值,代碼如下:
?.arch/arm/Makefile將libs-y的值改為了: arch/arm/lib $(libs-y),展開以后為:
?因此根據(jù)示例代碼35.5.3.5的第900~902行可知,libs-y最終應該為:
?
core-y
core-y和init-y也一樣,在頂層Makefile中有如下代碼:
?但是在arch/arm/Makefile中會對core-y進行追加,代碼如下:
第269-274行根據(jù)不同的配置向core-y追加不同的值,比如使能VFP的話就會在.config中有 CONFIG_VFP=y這一行,那么core-y就會追加“arch/arm/vfp/”。第277-282行就是對core-y直接追加的值。
在頂層Makefile中有如下一行:
?經(jīng)過上述代碼的轉(zhuǎn)換,最終core-y的值為:
?
?關于head-y、init-y、core-y、libs-y、drivers-y和net-y這6個變量就講解到這里。這些變量都是一些built-in.o或a等文件,這個和uboot一樣,都是將相應目錄中的源碼文件進行編譯,然后在各自目錄下生成built-in.o文件,有些生成了.a庫文件。最終將這些built-in.o和.a文件進行鏈接即可形成ELF格式的可執(zhí)行文件,也就是vmlinux!但是鏈接是需要鏈接腳本的,vmlinux的依賴arch/arm/kernel/vmlinux.lds就是整個Linux的鏈接腳本。
示例代碼35.5.3.2第933行的命令
"+$(call if_changed,link-vmlinux)”表示將$(callif_changed,link-vmlinux)的結(jié)果作為最終生成vmlinux的命令,前面的“+”表示該命令結(jié)果不可忽略。$(call if_changed,link-vmlinux)是調(diào)用函數(shù) if_changed, link-vmlinux 是函數(shù) if_changed 的參數(shù),函數(shù)if-changed定義在文件scripts/Kbuild.include中,如下所示:
?any-prereq用于檢查依賴文件是否有變化,如果依賴文件有變化那么any-prereq就不為"空,否則就為空。arg-check用于檢查參數(shù)是否有變化,如果沒有變化那么arg-check就為空。
第248行, "@set-e”告訴bash,如果任何語句的執(zhí)行結(jié)果不為true(也就是執(zhí)行出錯)的話就直接退出。
第249行, $(echo-cmd)用于打印命令執(zhí)行過程,比如在鏈接vmlinux的時候就會輸出“LINK vmlinux”。S(cmd_$(1))中的$(1)表示參數(shù),也就是link-vmlinux,因此$(cmd_$(1)表示執(zhí)行cmd link-vmlinux的內(nèi)容。cmd link-vmlinux在頂層Makefile中有如下所示定義:
?第915行就是cmd_link-vmlinux的值,其中CONFIG_SHELL=/bin/bash,$<表示目標vmlinux的第一個依賴文件,根據(jù)示例代碼35.5.3.2可知,這個文件為 scripts/link-vmlinux.sh。LD= arm-linux-gnueabihf-ld-EL,LDFLAGS 為空。LDFLAGS_vmlinux 的值由頂層Makefile 和arch/arm/Makefile這兩個文件共同決定,最終LDFLAGS_vmlinux=-p --no-undefined -X--picveneer--build-id。因此 cmd_link-vmlinux最終的值為:
?cmd_link-vmlinux會調(diào)scripts/link-vmlinux.sh這個腳本來鏈接出 vmlinux!在linkvmlinux.sh 中有如下所示代碼:
?
vmliux_link就是最終鏈接出vmlinux的函數(shù),第55行判斷SRCARCH是否等于"um",如"果不相等的話就執(zhí)行56-58行的代碼。因為SRCARCH-arm,因此條件成立,執(zhí)行56-58行的代碼。這三行代碼就應該很熟悉了!就是普通的鏈接操作,連接腳本為Ids= /arch/arm/kernel/vmlinux.Ids,需要鏈接的文件由變量 KBUILD_VMLINUX_INIT 和KBUILD VMLINUX MAIN來決定,這兩個變量在示例代碼35.5.3.2中已經(jīng)講解過了。
第217行調(diào)用vmlinux_link函數(shù)來鏈接出vmlinux。
使用命令"make V=1”編譯Linux,會有如圖35.5.3.1所示的編譯信息:
?至此我們基本理清了make的過程,重點就是將各個子目錄下的built-in.o、.a 等文件鏈接在一起,最終生成vmlinux這個ELF格式的可執(zhí)行文件。鏈接腳本為arch/arm/kernel/vmlinux.lds,鏈接過程是由 shell 腳本 scripts/link-vmlinux.s 來完成的。接下來的問題就是這些子目錄下的 builtin.o、.a 等文件又是如何編譯出來的呢?
built-in.o文件編譯生成過程
根據(jù)示例代碼35.5.3.2第920行可知,vmliux依賴vmlinux-deps,而 vmlinux-deps=$(KBUILD_LDS)$(KBUILD_VMLINUX_INIT)$(KBUILD_VMLINUX_MAIN), KBUILD_LDS是鏈接腳本,這里不考慮,剩下的KBUILD-VMLINUX-INIT和KBUILD-VMLINUX-MAIN就是各個子目錄下的 built-in.o、a等文件。最終 vmlinux-deps 的值如下:
?除arch/arm/kernel/vmlinux.lds以外,其他都是要編譯鏈接生成的。在頂層Makefile中有如下代碼:
?sort是排序函數(shù),用于對vmlinux-deps的字符串列表進行排序,并且去掉重復的單詞??梢钥闯鰒mlinux-deps依賴vmlinux-dirs, vmlinux-dirs也定義在頂層Makefile中,定義如下:
?值如下:vmlinux-dirs看名字就知道和目錄有關,此變量保存著生成vmlinux所需源碼文件的目錄值如下:
?在頂層Makefile中有如下代碼:
?目標vmlinux-dirs依賴prepare和scripts,這兩個依賴不去浪費時間了,重點看一下第947行的命令。build前面已經(jīng)說了,值為"-f./scripts/Makefile.build obj”,因此將947行的命令展開就是:
$@表示目標文件,也就是vmlinux-dirs的值,"將vmlinux-dirs中的這些目錄全部帶入到命令中,結(jié)果如下:?
這些命令運行過程其實都是一樣的,我們就以“@ make-f Jscripts/Makefile.build obj-init”這個命令為例,講解一下詳細的運行過程。這里又要用到Makefile.build這個腳本了,此腳本默認目標為_build,再來看一下,_build目標對應的規(guī)則如下:?
當只編譯Linux內(nèi)核鏡像文件,也就是使用“make zImage”編譯的時候,.KBUILD_BUILTIN=1,KBUILD_MODULES為空?!癿ake”命令是會編譯所有的東西,包括Linux內(nèi)核鏡像文件和一些模塊文件。如果只編譯Linux內(nèi)核鏡像的話,_build目標簡化為:?
重點來看一下builtin-target這個依賴,builtin-target同樣定義在文件 scripts/Makefile.build,_中,定義如下:?
?第87行就是builtin-target變量的值,為“S(obj)/built-in.o”,這就是這些 built-in.o 的來源了。要生成 built-in.o,要求 obj-y、obj-m、obj-、subdir-m 和 lib-target 這些變量不能全部為空。最后一個問題:built-in.o是怎么生成的?在文件 scripts/Makefile.build中有如下代碼:
第336行的目標就是builtin-target,依賴為obj-y,命令為"$(call if_changed,link_o_target)",也就是調(diào)用函數(shù)if changed,參數(shù)為link o target,其返回值就是具體的命令。前面講過了.if_changed,它會調(diào)用 cmd_$(1)所對應的命令($(1)就是函數(shù)的第1個參數(shù)),在這里就是調(diào)用cmd_link_o_target 所對應的命令,也就是第331~334行的命令。cmd_link_o_target就是使用 LD將某個目錄下的所有.o文件鏈接在一起,最終形成built-in.o。
make zImage過程
vmlinux、Image, zImage、ulmage的區(qū)別
前面重點是講vmlinux是如何編譯出來的, vmlinux是ELF格式的文件,但是在實際中我們不會使用 vmlinux,而是使用zImage或ulmage這樣的Linux內(nèi)核鏡像文件。那么vmlinux、zImage、ulmage 他們之間有什么區(qū)別呢?
vmlinux是編譯出來的最原始的內(nèi)核文件,是未壓縮的,比如正點原子提供的Linux源碼編譯出來的vmlinux差不多有16MB,如圖所示:
?Image是Linux內(nèi)核鏡像文件,但是Image僅包含可執(zhí)行的二進制數(shù)據(jù)。Image就是使用objcopy取消掉vmlinux中的一些其他信息,比如符號表什么的。但是Image是沒有壓縮過的,Image保存在arch/arm/boot目錄下,其大小大概在12MB左右如圖所示:
相比vmlinux的16MB,Image縮小到了12MB。?
zImage是經(jīng)過gzip壓縮后的Image,經(jīng)過壓縮以后其大小大概在6MB左右,如圖所示:
ulmage是老版本uboot專用的鏡像文件,ulmag是在zImage前面加了一個長度為 64字節(jié)的“頭”,這個頭信息描述了該鏡像文件的類型、加載位置、生成時間、大小等信息。但是新的uboot已經(jīng)支持了zImage啟動!所以已經(jīng)很少用到ulmage了,除非你用的很古老的uboot。
使用"make"、"make all"、 "make zImage"這些命令就可以編譯出zImage鏡像,在arch/arm/Makefile中有如下代碼:
?
第310行,變量BOOT_TARGETS包含zImage, Image, xipImage等鏡像文件。
第315行,?BOOT_TARGETS依賴vmlinux,因此如果使用"make zImage"編譯的Linux內(nèi)核的話,首先肯定要先編譯出 vmlinux。
第316行,具體的命令,比如要編譯zImage,那么命令展開以后如下所示:
?看來又是使用scripts/Makefile.build文件來完成vmlinux到zImage的轉(zhuǎn)換。文章來源:http://www.zghlxwxcb.cn/news/detail-461545.html
基本和uboot的頂層Makefile一樣,重點在于vmlinux的生成。最后將vmlinux壓縮成我們最常用的zImage或ulmage等文件。文章來源地址http://www.zghlxwxcb.cn/news/detail-461545.html
到了這里,關于15_Linux工程目錄與頂層Makefile的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!