linux內核源碼編譯過程分析之Kconfig,Makefile

關注”技術簡說“,帶你由淺入深學習linux內核源碼。linux內核開發100講免費教程,每天晚上九點準時更新,敬請收看。進我主頁點”視頻“欄目即可觀看。

在之前的課程裡,當我們在編譯linux內核源碼的時候,不知道大家會不會有一些疑問:

1.linux內核源碼那麼多(大概800M),編譯的時候它(編譯系統)怎麼知道應該要編譯哪些文件呢?

2.怎樣保證源碼的編譯順序?比如,先編譯A模塊,再編譯B模塊?

3.怎麼樣把這些編譯出來的一個一個的目標文件,最終形成一個內核鏡像文件?

所有這些,都是通過Makefile來完成的。

本文所用內核源碼為 linux-4.9.229,ARCH=x86

linux內核源碼裡的Makefile分為三層:

  • 頂層Makefile(源碼根目錄下)
  • 體系相關的Makefile(arch/$(ARCH)/Makefile)
  • 各級子目錄下面的Makefile。
linux內核源碼編譯過程分析之Kconfig,Makefile

那我們就先來看頂層Makefile,先摘幾段關鍵代碼:

<code> 262 SRCARCH     := 

$(ARCH)

... 546

include

arch/

$(SRCARCH)

/Makefile/<code>

這樣體系相關的Makefile就被include進來了。

而下面的代碼則間接的把各級子目錄下面的Makefile包含進來了。

<code> 

570

init-y := init/

571

drivers-y := drivers/ sound/ firmware/

572

net-y := net/

573

libs-y := lib/

574

core-y := usr/

575

virt-y := virt//<code>

這樣頂層的Makefile就把體系相關的Makefile和各個子目錄的Makefile都包含進來了。

那接下來我們深入到某個具體的子目錄的Makefile,看它又是如何編譯這個子目錄下面的源代碼的呢?

linux內核源碼編譯過程分析之Kconfig,Makefile

具體到某個源碼目錄中,編譯系統是通過obj-y, obj-m,obj-n和lib-m來組織的。

  • obj-y用來定義哪些源文件被編進內核。在當前目錄,這些被編譯到內核裡的.o文件,會被鏈接成一個built.o文件。大家可以看看,等內核編譯完成後,是不是每個子目錄下,都有一個built-in.o文件呢?
  • obj-m用來定義哪些文件被編譯成可加載模塊,也就是驅動文件。
  • obj-n表示當前源文件既不被編譯到內核,也不會編譯成驅動,也就是不做處理。
  • lib-y用來定義哪些文件被編譯成庫文件。
  • obj-y, obj-m,lib-y,lib-m還可以指定要包含的下一級目錄。這樣層層包含,保證能夠編譯到所有的內核源代碼。

我以drivers/tty子目錄為例,其Makefile的摘要如下:

<code>obj-$(CONFIG_TTY)       += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
                   tty_buffer.o tty_port.o tty_mutex.o tty_ldsem.o
obj-$(CONFIG_HVC_DRIVER)    += hvc//<code>

以上表示:

如果通過make menuconfig配置了CONFIG_TTY為y,那麼 tty_io.c n_tty.c tty_ioctl.c tty_ldisc.c

tty_buffer.c tty_port.c tty_mutex.c tty_ldsem.c 這些c文件就會被編譯到內核鏡像裡;

如果通過make menuconfig配置了CONFIG_HVC_DRIVER,那麼還會進入到hvc子目錄進一步執行它的Makefile。

所以,當某個子目錄下的makefile執行一遍以後,所有的需要編譯到內核裡的.o文件就都羅列在obj-y這個變量裡了,而所有需要編譯成驅動的.o文件就都羅列在obj-m裡了。

另外,某個子目錄下obj-y變量裡的所有 .o文件會被鏈接成built-in.o文件,並最終被鏈接到內核鏡像文件。

下面代碼是drivers/tty/built-in.o 生成的過程。

<code>ld -m elf_x86_64  -z 

max

-page-size=

0x200000

-r -o drivers/tty/built-

in

.o drivers/tty/tty_io.o drivers/tty/n_tty.o drivers/tty/tty_ioctl.o drivers/tty/tty_ldisc.o drivers/tty/tty_buffer.o drivers/tty/tty_port.o drivers/tty/tty_mutex.o drivers/tty/tty_ldsem.o drivers/tty/pty.o drivers/tty/tty_audit.o drivers/tty/sysrq.o drivers/tty/vt/built-

in

.o drivers/tty/serial/built-

in

.o drivers/tty/ipwireless/built-

in

.o /<code>
linux內核源碼編譯過程分析之Kconfig,Makefile

我們來看看內核鏡像是如何生成的。

還是看頂層的Makefile:

<code>core-y      += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/

vmlinux-dirs    

:

= $(patsubst

%/,%,$(filter %/

, $(init-y) $(init-m) \ $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ $(net-y) $(net-m) $(libs-y) $(libs-m) $(virt-y))) vmlinux-alldirs

:

= $(sort $(vmlinux-dirs) $(patsubst

%/,%,$(filter %/

, \ $(init-) $(core-) $(drivers-) $(net-) $(libs-) $(virt-)))) init-y

:

= $(patsubst

%/, %/

built-

in

.o, $(init-y)) core-y

:

= $(patsubst

%/, %/

built-

in

.o, $(core-y)) drivers-y

:

= $(patsubst

%/, %/

built-

in

.o, $(drivers-y)) net-y

:

= $(patsubst

%/, %/

built-

in

.o, $(net-y)) libs-y1

:

= $(patsubst

%/, %/

lib.a, $(libs-y)) libs-y2

:

= $(patsubst

%/, %/

built-

in

.o, $(libs-y)) libs-y

:

= $(libs-y1) $(libs-y2) virt-y

:

= $(patsubst

%/, %/

built-

in

.o, $(virt-y)) export KBUILD_ALLDIRS

:

= $(sort $(filter-out arch/%,$(vmlinux-alldirs)) arch Documentation

include

samples>

patsubst和filter是Makefile的內置函數,說明如下:

$(patsubst %/, %/built-in.o, $(init-y))

尋找$(init-y)中符合格式“%/”的字符串,用“%/built-in.o”替換它們。在之前我們有定義init-y的值為init/,所以,經過這麼轉換,init-y的值就變成了init/built-init.o了。其他同理。

而 $(filter pattern..., text)表示返回在“text”中由空格隔開且匹配格式“pattern...”的字符串,去掉不符合格式“pattern...”的字符串。例如:

<code>$(

filter

%.

c

%.s, foo.

c

bar.

c

jin.s xin.h) 結果為:foo.

c

bar.

c

jin.s/<code>

根據以上的分析,最終vmlinux-dirs的值為:

<code>

init

usr arch/x86 kernel certs mm fs ipc security crypto block drivers sound firmware \ arch/x86/pci arch/x86/power arch/x86/video arch/x86/ras net lib arch/x86/lib virt/<code>

最終vmlinux-alldirs的值為:

<code>  

arch/x86/lib arch/x86/math-emu arch/x86/oprofile arch/x86/pci \ arch/x86/power arch/x86/ras arch/x86/video block certs crypto drivers firmware \ fs init ipc kernel lib mm net security sound usr virt

/<code>

linux內核代碼的鏈接腳本為:arch/$(SRCARCH)/kernel/vmlinux.lds。 對於x86,則位於
arch/x86/kernel/vmlinux.lds, 它是由
arch/x86/kernel/vmlinux.lds.S文件生成的。

在編譯內核的時候指定:make V=1,則可以打印出具體的編譯細節,最終編譯出linux內核鏡像的編譯過程為:

<code>

ld

-m elf_x86_64 -z max-page-size=0x200000 --build-id -o vmlinux \ -T ./arch/x86/kernel/vmlinux.lds arch/x86/kernel/head_64.o \

arch/x86/kernel/ebda.o \ arch/x86/kernel/platform-quirks.o init/built-in.o \ --start-group usr/built-in.o arch/x86/built-in.o \ kernel/built-in.o certs/built-in.o mm/built-in.o \ fs/built-in.o ipc/built-in.o security/built-in.o \ crypto/built-in.o block/built-in.o lib/lib.a \ arch/x86/lib/lib.a lib/built-in.o arch/x86/lib/built-in.o \ drivers/built-in.o sound/built-in.o firmware/built-in.o \ arch/x86/pci/built-in.o arch/x86/power/built-in.o \ arch/x86/video/built-in.o arch/x86/ras/built-in.o \ net/built-in.o virt/built-in.o --end-group .tmp_kallsyms2.o

/<code>
linux內核源碼編譯過程分析之Kconfig,Makefile

linux內核源碼編譯過程分析之Kconfig,Makefile

總結一下,linux內核源碼的編譯過程為:

  • 頂層Makefile決定了linux內核源碼各個目錄和子目錄的編譯順序
  • make menuconfig通過對內核配置,生成了一系列CONFIG_XXX的配置
  • 各層級的Makefile結合這些CONFIG_XXX配置來決定哪些文件被編譯到內核、哪些文件被編譯成驅動
  • 頂層Makefile按照鏈接腳本鏈接所有的.o文件並最終打包成一個完整的內核鏡像文件vmlinux。

關注”技術簡說“,帶你由淺入深學習linux內核源碼。linux內核開發100講免費教程,每天晚上九點準時更新,敬請收看。進我主頁點”視頻“欄目即可觀看。


分享到:


相關文章: