本文主要介紹Linxu2.6的核心配置系統。
如果你瀏覽一下原始碼目錄,就可以發現源碼目錄及其子目錄中有很多的KConfig檔案和Makefile檔案。這些檔案什麼作用呢。正是這些檔案組成了Linux2.6的核心配置系統。
一、make menuconfig的背後------KConfig檔案的組織
有沒有想過,我們make menuconfig後,顯示的那個菜單列表是怎麼來的。
帶著這個疑問,我們先來簡單學一下Kconfig檔案的“文法”。
source 關鍵字:
用法:source <filename>
這個關鍵字相當於C語言裡的“include”關鍵字,source後面跟一個檔案名稱,相當於把該檔案的內容複寫到當前位置。下面是源碼目錄的arch/arm目錄下Kconfig檔案的部分內容。
通過這種source引用,可以引入很多其他子目錄中的Kconfig檔案,而且引入的Kconfig檔案中,還可以繼續通過source來引入下一級的Kconfig檔案。這樣的結構就可以將所有的Kconfig檔案包含進來。
一個功能表項目(或叫配置項)的基本組成:config、bool(tristate)、default、prompt、help
一個簡單的功能表項目:
其中,config關鍵字表示新定義一個功能表項目,後面跟的是這個功能表項目的名字(ARCH_IXP23XX)。bool標識這個功能表項目是bool類型,也就是這一項只能有兩個值Y和N,此外還有一種最常用的類型,tristate三態型,這種類型的可以有三個值,Y/M/N,這三個值的意義在(上)篇中已經說過了。後面的“IXP23XX-based”是這個功能表項目的描述,就是在make menuconfig時我們能看到的,如下圖:
在菜單列表裡我們並看不到一個功能表項目的名字,而只能看到它的描述,因為看它的描述更便於我們理解這個功能表項目的意義,方便我們配置。關於功能表項目的描述,還有一個prompt關鍵字,舉例說明其用法。比如下面兩段是等效的
----------------------------------
CONFIG MY_MENU
bool
prompt "this is my menu"
----------------------------------
CONFIG MY_MENU
bool "this is my menu"
----------------------------------
就是說,一個功能表項目的描述,可以直接跟在其類型(bool)的後面來進行聲明,也可以由prompt關鍵字聲明。
關於default關鍵字,截圖中並未出現,但也是很常用的,它表明一個功能表項目的預設值。如
default y
在進入菜單列表時,可以發現很多功能表項目都有預設值,這些預設值就是通過Kconfig檔案裡的default定義的。
還有一個help關鍵字,help關鍵字後面的內容是協助資訊,就是我們點擊右下角的heip時顯示的關於這個功能表項目的協助資訊。下面是關於上圖所示的功能表項目的協助資訊:
功能表項目間的依賴關係:select和depends on
還拿上面的例子來說明,第三行"depends on MMU"。這一行是說,現在定義的"ARCH_IXP23XX"這個功能表項目的值(Y/N)依賴於MMU這個功能表項目的值。當MMU這個功能表項目為N時,ARCH_IXP23XX只能為N。ARCH_IXP23XX的值必須“小於”MMU的值。(對於bool型,Y>N;對於tristate型,Y>M>N)。
select關鍵字的作用恰與depends on相反,它描述了一個反依賴的關係。以第五行"select PCI"為例,PCI的值依賴於ARCH_IXP23XX。在定義PCI這個功能表項目時,也要加上這樣一句:"depends on ARCH_IXP23XX"。
根據各功能表項目之間的依賴關係,在make menuconfig時,系統會自動將這些相關聯的功能表項目整理成功能表項目與子功能表項的形式,如下圖
第二張圖中的功能表項目都依賴於"Enable the block layer"對應的功能表項目,所以系統將它們整理成子功能表項。只有"Enable the block layer"對應的功能表項目不為N時,這些子功能表項才可以配置。
menu與endmenu關鍵字
這個關鍵字主要是為了給功能表項目分組,使菜單結構看起來更有條理。menu用來定義一個子功能表,這個子功能表裡包括一些相關的功能表項目,在menu和endmenu關鍵字之間定義的功能表項目都屬於這個子功能表。還以那上面兩張圖為例,"Enable the block layer----->"功能表項目下面的"system type--->"就是一個子功能表的名稱。將這個子功能表展開就可以看到這個子功能表包含的功能表項目了。
menu "System type"
config ……
…………
config……
…………
config………
…………
…………
endmenu
這裡再額外解釋一下,在上面的圖中,"Enable the block layer--->"和"system type-->",這兩個雖然看起來很像,都可以展開,但其性質是不同的。前者是根據各功能表項目間的依賴關係建立起來的,"Enable the block layer"本身就對應一個功能表項目或者說配置項,它也有自己的值(Y/M/N),而"system type"則只是一個子功能表的名稱,它下麵包含了一些相關的配置項,但他本身不對應某個配置項,因而沒有值(所以菜單列表中"system type--->"的前面沒有*或M這些符號)。
choices與endchoice關鍵字
跟menu與endmenu用法基本一樣,唯一的區別在於,choices定義的“子功能表”(應該叫選項表)中的多個功能表項目只能有一個被選中,相當於menu定義一個可多選的子功能表,choices定義一個單選的子功能表。篇幅限制,不再截圖詳述。
if與endif關鍵字
這兩個真心不用解釋,原諒我直接略過。
comment關鍵字
用來在菜單列表中插入一行文字,也是為了最佳化菜單結構。
如上圖中的第四、五行,就是通過 comment "Processor Type" 和 comment "Processor Features"插入的。這兩行既不能展開,也不能被配置,他們只是為菜單列表分段的一行文字。
終於把Kconfig中的關鍵字連圖帶字的解說完了,好累啊。(很多教材和部落格上介紹這些關鍵字的時候都是只有文字描述,而不結合make menuconfig後出現的菜單介面聯絡著說明,讀起來不夠直觀,現在我就把這些整理出來,供新手們快速掌握和理解這些關鍵字的作用)。大家現在可以試著去閱讀一個Kconfig檔案了。
好了,對這些關鍵字有了認識之後,我們來說一下這個菜單列表的形成過程。運行make menuconfig後,系統的組態工具先分析與體繫結構對應的/arch/ARCH/Kconfig
檔案(這裡出現的ARCH參數在本文最後會講到。它其實指的就是所用的<cpu>),/arch/ARCH/Kconfig檔案中定義了一個主菜單mainmenu,它除了包含一些配置項和配置菜單以外,還通過source語句引用了一系列其他子目錄中的Kconfig檔案,被引用進來的Kconfig 檔案內部可能再次通過source 引入下一級目錄中的Kconfig,系統就根據所有這些Kconfig檔案中包含的功能表項目(配置項)形成了菜單列表,然後根據使用者對各個功能表項目設定的值,最後產生.config檔案。
二、另一個重要角色------kbuild Makefile的介紹
Kconfig檔案協助使用者完成配置過程,而真正編譯核心則是在各個子目錄中的一系列Makefile共同完成的。Makefile中的重要文法就三個,比較好理解,這裡直接引用書中的內容。我把需要注意的地方用粗體標示。
引自 華清遠見嵌入式培訓中心 宋寶華 編著的《Linux裝置驅動開發詳解》(這本書真的很好,需要電子版的同仁可以直接聯絡我):
(1)目標定義。
目標定義用來定義哪些內容要作為模組編譯,哪些要編譯並串連進核心。
例如:
obj-y += foo.o
表示要由foo.c 或者foo.s 檔案編譯得到foo.o 並串連進核心,而obj-m 則表示該
檔案要作為模組編譯。除了y、m以外的obj-x形式的目標都不會被編譯。
而更常見的做法是根據.config 檔案的CONFIG_變數來決定檔案的編譯方式,如
下所示:
obj-$(CONFIG_ISDN) += isdn.o
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
除了obj-形式的目標以外,還有lib-y library庫、hostprogs-y 主機程式等目標,但
是基本都應用在特定的目錄和場合下。
(2)多檔案模組的定義。
如果一個模組由多個檔案組成,這時候應採用模組名加-objs尾碼或者-y尾碼的形
式來定義模組的組成檔案。如下面的例子所示:
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o bitmap.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
模組的名字為ext2,由balloc.o和bitmap.o兩個目標檔案最終串連產生ext2.o 直
至ext2.ko 檔案,是否包括xattr.o 取決於核心設定檔的配置情況。如果
CONFIG_EXT2_FS 的值是y 也沒有關係,在此過程中產生的 ext2.o 將被串連進
built-in.o最終串連進核心。這裡需要注意的一點是,該kbuild Makefile所在的目錄中
不能再包含和模組名相同的源檔案如ext2.c/ext2.s。
或者寫成如-objs的形式:
obj-$(CONFIG_ISDN) += isdn.o
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
(3)目錄層次的迭代。
樣本:
obj-$(CONFIG_EXT2_FS) += ext2/
當CONFIG_EXT2_FS 的值為y或m時,kbuild將會把ext2 目錄列入向下迭代的
目標中,具體ext2 目錄下的檔案是要作為模組編譯還是鏈入核心由ext2 目錄下的
Makefile檔案的內容決定。
引用結束。
上面所述的(3)特別關鍵,目錄層次的迭代使得整個Makefile系統呈現一個樹狀結構,條理清晰,載入新的編譯目錄也很方便。
三、牛刀初試
假如我們自己開發了一個新的模組,要如何才能把我們自己的模組加到配置系統中。上面已經介紹了Kconfig和Makefile檔案的基本文法,為了確保我們掌握了這兩種檔案的配置方法,我們最好做一下練習,。這裡我要再次偷懶,直接COPY書中內容了。
引用開始:
下面講解一個綜合執行個體,假設我們要在核心原始碼drivers目錄下為ARM體繫結
構新增如下用於test driver 的樹型目錄:
|--test
|-- cpu
| -- cpu.c
|-- test.c
|-- test_client.c
|-- test_ioctl.c
|-- test_proc.c
|-- test_queue.c
在核心中增加目錄和子目錄,我們需為相應的新增目錄建立Kconfig 和Makefile
檔案,而新增目錄的父目錄中的Kconfig 和Makefile 檔案也需要修改,以便新增的
Kconfig和Makefile檔案能被引用。
在新增的test目錄下,應該包含如下Kconfig檔案:
#
# TEST driver configuration
#
menu "TEST Driver "
comment " TEST Driver"
config CONFIG_TEST
bool "TEST support "
config CONFIG_TEST_USER
tristate "TEST user-space interface"
depends on CONFIG_TEST
endmenu
由於TEST driver 對於核心來說是新的功能,所以首先需要建立一個菜單TEST
Driver;然後顯示“TEST support”,等待使用者選擇;接下來判斷使用者是否選擇了TEST
Driver,如果是(CONFIG_TEST=y),則進一步顯示子功能:使用者介面與CPU功能支
持;由於使用者介面功能可以被編譯成核心模組,所以這裡的詢問語句使用了tristate。
為了使這個Kconfig檔案能起作用,需要修改arch/arm/Kconfig檔案,增加以下內
容:
source "drivers/test/Kconfig"
指令碼中的source意味著引用新的Kconfig檔案。
在新增的test目錄下,應該包含如下Makefile檔案:
# drivers/test/Makefile
#
# Makefile for the TEST.
#
obj-$(CONFIG_TEST) += test.o test_queue.o test_client.o
obj-$(CONFIG_TEST_USER) += test_ioctl.o
obj-$(CONFIG_PROC_FS) += test_proc.o
obj-$(CONFIG_TEST_CPU) += cpu/
該指令碼根據組態變數的取值構建obj-*列表。由於test目錄中包含一個子目錄cpu,
當CONFIG_ TEST_CPU=y時,需要將cpu目錄加入列表。
test目錄中的cpu子目錄也需包含如下的Makefile檔案:
# drivers/test/test/Makefile
#
# Makefile for the TEST CPU
#
obj-$(CONFIG_TEST_CPU) += cpu.o
為了使得整個test 目錄能夠被編譯命令作用到,test 目錄父目錄中的Makefile 文
件也需新增如下指令碼:
obj-$(CONFIG_TEST) += test/
在drivers/Makefile中加入obj-$(CONFIG_TEST) += test/,使得使用者在進行核心編
譯時能夠進入test目錄。
增加了Kconfig和Makefile檔案之後的新的test樹型目錄如下所示:
|--test
|-- cpu
| -- cpu.c
| -- Makefile
|-- test.c
|-- test_client.c
|-- test_ioctl.c
|-- test_proc.c
|-- test_queue.c
|-- Makefile
|-- Kconfig
附:(頂層Makefile中的ARCH參數、編譯ARM平台Linux核心的方法)
原始碼目錄頂層目錄中的Makefile檔案被稱作頂層Makefile,它裡面涉及到一個ARCH參數,還有一個CROSS_COMPILE參數,分別對應處理器核心架構和交叉編譯器。一般情況下,ARCH預設為x86,CROSS_COMPILE預設也是x86平台上的交叉編譯器。在執行make menuconfig,make bzImage,make modules等命令時,系統都會首先分析ARCH的值,根據選擇的平台來執行相應操作。因此,如果想編譯ARM平台的Linux核心,在輸入相應命令時要加上架構和交叉編譯器選項,如
make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux- bzImage
make ARCH=arm CROSS_COMPILE=arm-linux- modules
如果直接將
ARCH=arm
CROSS_COMPILE=arm-linux-
這兩行添加到頂層Makefile的開頭,就可以將ARCH和CROSS_COMPILE的預設值改成"arm"和"arm-linux-"了。以後再編譯ARM平台的Linux核心時就可以直接使用make menuconfig/bzImage/modules等命令,而不用再加"ARCH=arm CROSS_COMPILE=arm-linux-"。