2021-05-12 14:32:11
Linux核心設定、編譯及Makefile簡述
最近在學習Linux核心的設定、編譯及Makefile檔案。今天總結一下學習成果,分享給大家。
1.解壓縮打修補程式
首先是解壓縮你獲取到的Linux核心。這裡我用到的是linux.2.22.6版本的核心。在Linux下命令列通過tar xjf linux.2.22.6.tar.bz2解壓核心。然後,如果你需要對這個核心打修補程式的話,用patch命令:patch -px <../linux.2.22.6.patch。這裡的px指的是忽略掉修補程式檔案中描述的第幾個斜槓。也就是忽略前x個目錄。
--- linux-2.6.22.6/arch/arm/configs/s3c2410_defconfig
+++ linux-2.6.22.6_jz2440/arch/arm/configs/s3c2410_defconfig
如果你此刻就在核心的根目錄下,即linux-2.6.22.6下,也就是說打修補程式需要忽略掉一個斜槓的目錄。那麼打修補程式的命令就是patch -p1 <../linux.2.22.6.patch。
2.設定核心
現在修補程式已經打好了,接下來就是設定核心了。這裡設定有3種方法:
1>直接進行make menuconfig。這是最麻煩的一種方法,所有的設定都需要你來操作。
2>在預設設定上自己修改,也就是修改defconfig檔案。使用 find -name "*defconfig*"查詢你的架構對應的預設組態檔。我是在arch/arm/configs找到自己板子的預設組態檔。執行defconfig檔案: make XXX_defconfig。XXX是你具體使用的板子型號。執行這一操作後,結果儲存在.config檔案。然後再執行make menuconfig命令。這時的設定就是在預設設定上稍加修改就可以了。
3>使用廠家的組態檔。如果你的硬體有廠家提供的config檔案那是最輕鬆的。直接cp XXX .config。然後執行make menuconfig。
這裡詳細給大家講一下核心的設定。Linux的核心設定,就是為了生成.config檔案。因為在編譯時需要用.config檔案生成其他相關組態檔。我們的設定項大多是例如CONFIG_XXXDRIVER,這裡的XXXDRIVER指的是各種驅動。我們需要告訴核心,這些驅動是編譯進核心,還是編譯成模組。通過查詢CONFIG_XXXDRIVER,我們可以發現,它出現在四個地方:
1>C原始碼
2>子目錄Makefile:drivers/XXX/Makefile
3>include/config/auto.conf
4>include/linux/autoconf.h
這裡首先說明:.config檔案在進行核心編譯時(make uImage)生成了include/config/auto.conf和include/linux/autoconf.h。通過檢視C原始碼我們發現CONFIG_XXXDRIVER是一個宏定義,等於一個常數。在include/linux/autoconf.h中宏定義CONFIG_XXXDRIVER為一個常數,可能是0或1。那麼現在有一個問題,就是CONFIG_XXXDRIVER到底被編譯進核心還是編譯成一個模組呢?這在C語言中是無法進行區分的,這種區分體現在哪裡呢?這種區分體現在子目錄的Makefile檔案中。在子目錄的Makefile中,若有 obj -y += XXX.o則表示XXX.c被編譯進核心;obj -m +=XXX.o則表示XXX被編譯成模組,為XXX.ko。include/config/auto.conf檔案則是對CONFIG_XXXDRIVER進行賦值,為y時表示編譯進核心,為m時表示編譯成獨立模組。
#這裡是include/config/auto.conf的部分內容
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.22.6
# Sun Nov 27 18:34:38 2016
#
CONFIG_CPU_S3C244X=y
CONFIG_CPU_COPY_V4WB=y
CONFIG_CRYPTO_CBC=y
CONFIG_CPU_S3C2410_DMA=y
CONFIG_CRYPTO_ECB=m
CONFIG_SMDK2440_CPU2440=y
#這裡是drivers/i2c/Makefile
# Makefile for the i2c core.
#
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
obj-y += busses/ chips/ algos/
ifeq ($(CONFIG_I2C_DEBUG_CORE),y)
EXTRA_CFLAGS += -DDEBUG
endif
3.編譯核心
通過上面的描述,我們可以知道,在每個driver下,都有一個Makefile檔案。來定義這個驅動是編譯進核心還是編譯成模組。這裡稍稍提一下。上面我們講到了在Makefile中單個檔案怎樣編譯進核心和編譯成模組。但是如果有兩個以上的檔案該如何書寫呢?這裡舉個例子:obj -y += a.o b.o就表示將a.c和b.c編譯進核心。
obj -m += ab.o
ab -objs := a.o b.o
就可以表示將a.c和b.c共同編譯成為一個模組。過程就是 a.c生成a.o , b.c生成b.o。a.o 和b.o共同生成ab.ko。在以往的對uboot啟動核心的程式碼分析中,我們提到過編譯核心生成的uImage是由兩部分組成的:頭部+Linux核心。這個頭部包含了很多初始化的引數資訊,例如核心的載入地址和入口地址。在編譯核心時,我們直接執行make uImage即可。那麼在檔案中是怎樣定義uImage的呢?又是怎樣生成uImage的呢?
首先,我們通過查詢Makefile,發現uImage在arch/arm/Makefile中,而這個架構目錄下的Makefile被包含進頂層目錄的Makefile中。這樣我們在執行 make uImage時,頂層目錄的Makefile就可以呼叫架構子目錄下的Makefile,實現對核心的編譯,生成uImage。
頂層目錄下Makefile中相關命令:
include $(srctree)/arch/$(ARCH)/Makefile
架構目錄下Makefile相關命令:
zImage Image xipImage bootpImage uImage: vmlinux
這就是剛剛所說的頂層Makefile呼叫架構目錄下Makefile,架構目錄下Makefile生成uImage,而且依賴於vmlinux檔案。下面我們就開始講解如何生成vmlinux檔案。在頂層Makefile中,我們找到了有關生成vmlinux的大部分命令。
頂層目錄Makefile:
init-y := init/
init-y := $(patsubst %/, %/built-in.o, $(init-y))
core-y := usr/
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
core-y := $(patsubst %/, %/built-in.o, $(core-y))
libs-y := lib/
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y := $(libs-y1) $(libs-y2)
drivers-y := drivers/ sound/
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
net-y := net/
net-y := $(patsubst %/, %/built-in.o, $(net-y)) = net/built-in.o
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE
vmlinux-init := $(head-y) $(init-y)
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux-all := $(vmlinux-init) $(vmlinux-main)
vmlinux-lds := arch/$(ARCH)/kernel/vmlinux.lds
export KBUILD_VMLINUX_OBJS := $(vmlinux-all)
架構目錄Makefile:
zImage Image xipImage bootpImage uImage: vmlinux
head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
我已經把頂層目錄和架構目錄下生成vmlinux的命令摘選出來。首先,我們看要想生成vmlinux,需要vmlinux-lds檔案、vmlinux-init檔案、vmlinux-main檔案。其中,vmlinux-lds是連結指令碼檔案,定義了程式碼段,資料段的存放位置。這裡我們接著往下看,vmlinux-init需要head-y和init-y,通過檢視兩個Makefile,我們可以得到經過轉換後的結果:
head-y :=
arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
init-y := $(patsubst %/, %/built-in.o, $(init-y)) = init/built-in.o
core-y := $(patsubst %/, %/built-in.o, $(core-y))
= usr/built-in.o kernel/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
libs-y := $(libs-y1) $(libs-y2) =lib/lib.a lib/built-in.o
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y)) = drivers/built-in.o sound/built-in.o
net-y := $(patsubst %/, %/built-in.o, $(net-y)) = net/built-in.o
現在已經分析了核心編譯的全部過程。那怎樣知道我們分析的到底對不對,通過實際執行make uImage我們就可以看到執行過程。這是執行make uImage過程中的部分相關命令:
arm-linux-ld -EL -p --no-undefined -X -o vmlinux
-T arch/arm/kernel/vmlinux.lds
arch/arm/kernel/head.o
arch/arm/kernel/init_task.o init/built-in.o --start-group usr/built-in.o arch/arm/kernel/built-in.o arch/arm/mm/built-in.o
可以看到,首先目標要生成vmlinux,然後是連結指令碼為vmlinux.lds。開始生成第一個檔案:head.o,第二個檔案:init_task.o。這和我們分析的完全一致。接下來以此類推,和我們分析的相同,也就是說我們分析的是正確的。
SECTIONS
{
. = (0xc0000000) + 0x00008000;
.text.head : {
_stext = .;
_sinittext = .;
*(.text.head)
}
.init : { /* Init code and data */
*(.init.text)
_einittext = .;
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;
. = ALIGN(16);
__setup_start = .;
*(.init.setup)
__setup_end = .;
__early_begin = .;
*(.early_param.init)
__early_end = .;
__initcall_start = .;
這是連結指令碼vmlinux.lds中的部分內容。首先定義了虛擬地址:(0xc0000000) + 0x00008000。 然後是首先執行頭部檔案,這與我們分析的完全一致。程式碼段,初始化程式碼段等等。
這就是Linux核心的從設定到編譯的全部分析了^_^
本文永久更新連結地址:http://www.linuxidc.com/Linux/2016-12/137841.htm
相關文章