Hi,你們好!我是CrazyCatJack。最近在學習Linux內核的配置、編譯及Makefile文件。今天總結一下學習成果,分享給你們^_^linux
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。ui
2.配置內核spa
如今補丁已經打好了,接下來就是配置內核了。這裏配置有3種方法:命令行
1>直接進行make menuconfig。這是最麻煩的一種方法,全部的配置都須要你來操做。code
2>在默認配置上本身修改,也就是修改defconfig文件。使用 find -name "*defconfig*"查找你的架構對應的默認配置文件。我是在arch/arm/configs找到本身板子的默認配置文件。執行defconfig文件: make XXX_defconfig。XXX是你具體使用的板子型號。執行這一操做後,結果保存在.config文件。而後再執行make menuconfig命令。這時的配置就是在默認配置上稍加修改就能夠了。blog
3>使用廠家的配置文件。若是你的硬件有廠家提供的config文件那是最輕鬆的。直接cp XXX .config。而後執行make menuconfig。ip
這裏詳細給你們講一下內核的配置。Linux的內核配置,就是爲了生成.config文件。由於在編譯時須要用.config文件生成其餘相關配置文件。咱們的配置項大可能是例如CONFIG_XXXDRIVER,這裏的XXXDRIVER指的是各類驅動。咱們須要告訴內核,這些驅動是編譯進內核,仍是編譯成模塊。經過查找CONFIG_XXXDRIVER,咱們能夠發現,它出如今四個地方:it
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內核的從配置到編譯的所有分析了^_^
CCJ
2016-12-01 11:32:45