天天看點

Linux核心中Makefile、Kconfig和.config的關系

我們在編譯Linux核心時,往往在Linux核心的頂層目錄會執行一些指令,這裡我以RK3288舉例,比如:make firefly-rk3288-linux_defconfig、make menuconfig、make firefly-rk3288.img、make zImage等等。先不管這具體的含義,首先提出幾個疑問:

本文引用位址:http://emb.hqyj.com/Column/7565.html

(1)Linux核心如此龐大(幾萬個檔案),目錄又分為很多層,它是如何将各層目錄下的檔案關聯起來的?

(2)Linux支援如此多的架構(X86、ARM、AVR、mips等等),為何我們在使用某一架構的晶片,比如RK3288時,其他架構的代碼不會被編譯?并且同為ARM架構下的其他系列SOC架構相關的代碼不被編譯?

(3)在編譯核心前,執行指令:make menuconfig的意義為何?

(4)編譯核心時,執行指令:make zImage的意義為何?

(5)Linux核心中各層目錄下的Makefile檔案和Kconfig檔案是如何編寫的。

個人認為,當了解了解決了以上的幾個問題,那麼本文題目的疑問就自然而然的解決了。以下筆者來一一回答這個問題:

問題1.Linux核心如此龐大(幾萬個檔案),目錄又分為很多層,它是如何将各層目錄下的檔案關聯起來的?

關于此問題,如果要準确的回答,估計隻有真正的大神才能回答了,這裡隻講自己的了解。Linux核心源碼的代碼管理是非常科學的,在Linux核心源碼的頂層目錄下,配置設定了相應的目錄,在對應的目錄下,代表這就一些功能或者是屬性的叢集,這樣就實作了子產品化,便于管理,比如arch目錄與平台架構相關、include目錄存放着大量的核心頭檔案、drivers目錄存放着各種驅動代碼,比如顯示卡、網卡、USB總線、PCI總線等等、kernel目錄存放着支援體系結構特有的諸如信号量處理和SMP之類特征的實作、mm目錄存放着體系結構特有的記憶體管理程式的實作等等;然而在各個子目錄下,又會進行細分,比如arch目錄下,就存在和x86架構相關的目錄x86、與ARM架構相關的目錄arm、與MIPS目錄相關的目錄mips等等,以此類推。是以這就構成了一顆樹形結構。

學習過資料結構的童鞋應該知道,對于一棵非标準樹,還是有辦法将其進行周遊的,隻是算法比較複雜而已。那麼在Linux核心源碼的這棵樹,就是通過Kconfig檔案建立各層子目錄之間的連接配接,通過Makefile檔案來選擇各個目錄下的對應的檔案是否被編譯,而.config檔案就像是作為總控制台吧,控制着Makefile檔案去編譯指定的程式代碼檔案(主要是C和彙編)。而這一切控制關系是由Kconfig檔案建立起來的。

在我了解而言,這其實就是在周遊樹形結構時,用的一種方法,即指路法。每當我們在某個陌生的地方問路時,假設從A地到E地,情景通常如下:

Linux核心中Makefile、Kconfig和.config的關系

通常我們也會按照路人的回答,很準确的就找到了目的地E,是以,在程式中或者代碼架構中也是一樣的。是以在Linux核心源碼中,.config檔案就相當于路人,而Kconfig就為問路的人,而Makefile就為兩隻腳了,聽指令幹苦力的貨!嘿嘿!

問題2.Linux支援如此多的架構(X86、ARM、AVR、mips等等),為何我們在使用某一架構的晶片,比如RK3288時,其他架構的代碼不會被編譯?并且同為ARM架構下的其他系列SOC架構相關的代碼不被編譯?

關于這個問題,其實和我們在編譯Linux核心時,執行的指令:make firefly-rk3288-linux_defconfig或者make menuconfig有關了,執行以上兩個指令的目的是為了生成.config檔案。往往我們執行指令make menuconfig隻是為了修改一些驅動子產品和要編譯的一些程式,往往我們是不會去選擇架構相關的,比如說,自行的去選擇RK3288這顆SOC,再去選擇基于Firefly平台的闆卡,其實這也是可以的,隻要你開心,都可以自行在菜單裡面選擇,但是通常我們不會這樣,我們通常是先執行make firefly-rk3288-linux_defconfig生成了一個基于Firefly平台的RK3288相關配置的.config檔案,然後再執行指令make menuconfig來選擇一些子產品代碼進行編譯,這樣做的好處就是減少了很多工作量。當然,存在一個問題是,假設需要編譯的核心不支援現有的開發闆怎麼辦?比如從官網下載下傳下來的原始Linux核心源碼,隻支援RK3288這顆SOC,但是并沒有急于Firefly開發的這款RK3288的開發闆,怎麼辦呢?這涉及到了Linux核心的移植,在此不作過多的叙述,提醒一點的是,可以找一塊基于RK3288這顆SOC的開發闆的配置(在官方釋出的初始Linux核心源碼中,每支援一款SOC,基本上都會對于的支援一款官方開發闆作為參考),進行移植和模拟,後生成一個類似于firefly-rk3288-linux_defconfig的配置檔案,用它來生成基本的.config檔案。

清楚了以上的關系,在根據問題1的原因,就不難了解了。沒錯,就是基于firefly-rk3288-linux_defconfig檔案生成了基礎的,預設的.config配置檔案,此檔案的内容就包含了架構相關的東西,是以在進行Linux核心源碼的編譯時,根據.config檔案的基本配置,尋找架構相關的代碼進行編譯和裝置相關的代碼進行編譯。總之一起而已.config這個控制台為準。

問題3.在編譯核心前,執行指令:make menuconfig的意義為何?

其實這個問題在前兩個問題中基本上都回答了,make menuconfig就是以菜單的形式打開核心源碼的樹形結構,然後程式員在預設配置的基礎上自行配置和選擇需要編譯的子產品代碼。

其實能做這個工作的指令有很多,比如:

•make config:基于文本的為傳統的配置界面,太複雜,不直覺,不推薦使用。

•#make xconfig:基于圖形視窗模式的配置界面,直覺明了,Xwindow界面下推薦使用。

•make oldconfig:如果隻想在原來核心配置的基礎上修改一些小地方,會省去不少麻煩,可以使用。

•make menuconfig:基于文本選單的配置界面,直覺明了,字元終端下推薦使用。

大概好像這幾種吧,其實還有一種就是,手動修改.config檔案,呵呵!相信基本上沒人會去幹這種事的。

問題4.編譯核心時,執行指令:make zImage的意義為何?

通常在編譯時,都是執行這個指令或者執行make uImage指令進行編譯的。意義為何呢?其實是生成名為zImage或uImage的核心鏡像。當然,有人在疑問了,在編譯Firefly的RK3288開發闆時,執行的指令是make firefly-rk3288.img ,而不是上面的任何一個,其實這是Firefly修改過的方法了,其實說白了也就是一個名字而已。這個不要太過糾結,想具體了解的話,答案就在Firefly提供的核心源碼内。

執行編譯指令後,通過.config檔案、Kconfig檔案和Makefile檔案,就可以有規律有選擇的去編譯源代碼了。

問題5.核心中各層目錄下的Makefile檔案和Kconfig檔案是如何編寫的。

其實前面的4個問題隻是基礎的鋪墊,這才是重點,但是這裡必須依賴于前面的原理。

首先我們先确定一點,在Linux核心源碼的各層目錄下。都存在一個Kconfig檔案和一個Makefile檔案,.config檔案存在頂層目錄。如下圖:

Linux核心中Makefile、Kconfig和.config的關系

上圖基本上可以證明一切了。

為了更好的诠釋,我在drivers目錄下建立了一個my_dr目錄,主要存放我自己編寫的核心驅動代碼,此目錄下的其他目錄都是我編寫的驅動代碼,現在需要将它們連接配接起來,當執行make menuconfig指令時,能夠找到我自己編寫的核心驅動代碼。在這裡以一個hello程式舉例。

那麼到底該如何做呢?•••••••••兩字:“模仿”!

如下圖:

Linux核心中Makefile、Kconfig和.config的關系

上圖中就是我要舉例的目錄層了,hello目錄裡為hello.c程式;my_dr目錄裡為我想把我自己寫的驅動程式存放的目錄;drivers目錄為Linux核心驅動目錄;firefly-rk3288-kernel目錄為Linux核心源碼頂層目錄。是以按照前文的推斷,在每一層目錄下都會存在一個Makefile檔案和一個Kconfig檔案。下面從底層hello到drivers目錄各層Makefile檔案和Kconfig檔案分析。

(1)hello目錄

Linux核心中Makefile、Kconfig和.config的關系

如上圖,左邊為Makefile檔案,右邊為Kconfig檔案。對于Makefile檔案,如果變量CONFIG_HELLO為真或假,則判斷這是否将目錄下的hello.c檔案編譯為hello.o檔案。CONFIG_HELLO變量的值來自于.config檔案的配置。.config的配置又來自于通過Kconfig檔案的顯式選擇(就是通過菜單選擇)。再看Kconfig檔案,如下圖:

Linux核心中Makefile、Kconfig和.config的關系

Config為配置關鍵字;HELLO為配置項;tristate為三态選擇器,此關鍵字在為上層提供菜單配置時,有三種選擇,分别是不編譯、編譯成核心鏡像和編譯成子產品驅動,除了這個關鍵字,還有另一個bool,這個不難了解,即為布爾類型,西遊兩種選擇,編譯或者不編譯;help為幫助提示。這基本上是比較經典的格式了。是以通常在進行編寫這樣的配置時,通常的做法是參考Linux核心源碼中存在的Kconfig檔案和Makefile檔案,然後模仿他們的格式進行編寫。

(2)my_dr目錄

Linux核心中Makefile、Kconfig和.config的關系

如上圖,即為mu_dr目錄下的Makefile檔案和Kconfig檔案的内容了。對于Makefile檔案,一句話指引找到hello目錄。而對于Kconfig檔案,因為my_dr目錄下可能要存在很多驅動程式,是以必須要建立好一個菜單,進行對各個驅動程式的選擇,是以第1行代碼就算建立名為my Drivers的菜單;第5行代表可以在相對路徑drivers/my_dr/hello/Kconfig找到Kconfig資源(注意,這裡的路徑是相對于Linux核心源碼的頂層目錄的)。後的第7行表示使用關鍵字endmenu結束檔案。

(3)drivers目錄

Linux核心中Makefile、Kconfig和.config的關系

如上圖,即為drivers目錄下的Makefile檔案和Kconfig檔案了。還是一樣的,在Makefile檔案中添加洗衣歌資源的菜單目錄路徑;而在Kconfig檔案中,還是添加此目錄下的資源的Kconfig檔案的路徑。就這樣配合使用。而且,基本上在這兩個文檔中,可以直接看到很多的參考示例,直接參考即可!其實有一個疑問是,在Makefile檔案中,比如第158行,如下圖:

Linux核心中Makefile、Kconfig和.config的關系

存在變量CONFIG_GATOR,這是為啥嘞?其實是這樣的,這表示如果想想在菜單中顯示選擇關于gator的目錄菜單,就必須依賴于變量CONFIG_GATOR的值為y,這樣才能在使用make menuconfig指令時,才能看到gator目錄下的菜單,而CONFIG_GATOR變量則是在gator目錄的上層,依賴于某些代碼,選擇後,其值為y。而筆者自己寫的直接使用了語句obj-y,表示變量值為y(其實就是yes),是以直接就可見了,不依賴于其他的代碼。

那麼整一個過程就如上三步了,現在執行make menuconfig指令檢視是否能看到前面建立的菜單。如下圖:

Linux核心中Makefile、Kconfig和.config的關系

如上圖可以看到我自己建立的名為my Dribers的菜單了。

Linux核心中Makefile、Kconfig和.config的關系
Linux核心中Makefile、Kconfig和.config的關系

如上兩圖即為所使用的tristate關鍵字所表現出的三态結果了,即就是三種狀态。可以按照需要進行合适的選擇即可。

總結:本文主要根據個人的了解,記錄了Linux核心源碼中的Makefile、Kconfig和.config檔案的編寫和使用,從某個角度來講,其貫穿了整一個Linux核心源碼,有以圖形菜單的形式展現給開發者,編譯開發和選擇。