linux kernel(一)編譯體驗
目錄
title: kernel(一)編譯體驗
tags: linux
date: 2018-11-06 17:27:22
---
打補丁
- 解壓
tar xjf linux-2.6.22.6.tar.bz2
- 打補丁,cat下補丁檔案知道需要忽略第一個/
patch -p1 < linux-2.6.22.6_jz2440.patch
- 打包下生成的檔案
tar cjvf linux2.6.22_ok.tar.bz2 linux-2.6.22.6
配置
總結
- .config通過make自動生成
autoconf.h
和auto.conf
- 原始碼中通過
autoconf.h
決定程式碼編譯,無論配置為m或者y都編譯,因為標頭檔案都定義為1 - 子目錄的makefile通過
auto.conf
中的y/m
決定編譯為模組.ko
還是編譯到核心.這個檔案是被頂層的Makefile包含
配置方式
配置有三種方法:
make menuconfig 每一項都配置,非常多的選項,圖形介面配置
使用預設配置後執行
make menuconfig
檢視下預設配置,使用
find -name "*defconfig*"
搜尋,找到很多配置檔案,比如在arch/arm
下有資料夾configs
儲存預設配置./arch/arm/configs/realview_defconfig ./arch/arm/configs/hackkit_defconfig ./arch/arm/configs/lpd270_defconfig ....... ./arch/arm/configs/s3c2410_defconfig
進入這個目錄檢視下相關的
config
,發現s3c2410_defconfig
與我們的單板最為接近cd arch/arm/configs/ ls make s3c2410_defconfig
使用對應的最接近的預設配置
make s3c2410_defconfig
執行
make menuconfig
.最後提示所有配置專案寫入.config
中[email protected]:~/stu/kernel/linux2.6/linux-2.6.22.6$ make s3c2410_defconfig drivers/serial/Kconfig:235:warning: multi-line strings not supported ....... # # configuration written to .config #
如果報錯,則是因為make版本過新
[email protected]:~/work/linux-2.6.22.6$ make s3c2410_defconfig Makefile:416: *** mixed implicit and normal rules: deprecated syntax Makefile:1449: *** mixed implicit and normal rules: deprecated syntax make: *** No rule to make target 's3c2410_defconfig'。 停止。 原因:是由於我的系統的make工具太新,make的舊版規則已經無法相容新版。 1在makefile中將416行程式碼 config %config: scripts_basic outputmakefile FORCE 改為 %config: scripts_basic outputmakefile FORCE 2在makefile中將1449行程式碼 / %/: prepare scripts FORCE 改為 %/: prepare scripts FORCE
廠家提供配置,直接複製為名為.config
cp config_ok .config
,再執行make menuconfig
實際上方式2最終也是產生這個.config
檔案
配置體驗
輸入Y編譯進核心
輸入N 不包含
輸入M編譯為模組,所謂模組就類似於驅動,可以後置載入
輸入?進入檢視具體的配置項
輸入 / 搜尋
比如我們配置網路DM9000,按下圖進入配置
配置詳解
配置的最終目的,是生成了.config
檔案,檢視下這個檔案,
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.22.6
# Wed Dec 30 18:30:33 2009
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
CONFIG_GENERIC_GPIO=y
# CONFIG_GENERIC_TIME is not set
# CONFIG_GENERIC_CLOCKEVENTS is not set
CONFIG_MMU=y
CONFIG_NO_IOPORT=y
CONFIG_GENERIC_HARDIRQS=y
.....
CONFIG_DM9000=y #來分析這個配置專案
取其中一行 CONFIG_DM9000=y
來分析,搜尋下檔案內容
grep "CONFIG_DM9000" * -nwR
- 忽略掉預設配置檔案(以defconfig命名的),可以看到程式碼中有以下.c檔案中的巨集肯定是源自於標頭檔案,也就是4中的
auto.conf
arch/arm/plat-s3c24xx/common-smdk.c:46:#if defined(CONFIG_DM9000) || defined(CONFIG_DM9000_MODULE)
arch/arm/plat-s3c24xx/common-smdk.c:162:#if defined(CONFIG_DM9000) || defined(CONFIG_DM9000_MODULE)
arch/arm/plat-s3c24xx/common-smdk.c:200:#endif /* CONFIG_DM9000 */
arch/arm/plat-s3c24xx/common-smdk.c:250:#if defined(CONFIG_DM9000) || defined(CONFIG_DM9000_MODULE)
- 子目錄下的Makefile中有以下,這裡的會被區分是編譯為模組還是編譯為核心,參考
子目錄的Makefile
drivers/net/Makefile:197:obj-$(CONFIG_DM9000) += dm9dev9000c.o
drivers/net/Makefile:198:#obj-$(CONFIG_DM9000) += dm9000.o
drivers/net/Makefile:199:#obj-$(CONFIG_DM9000) += dm9ks.o
- make之後還會在
include
下有以下,這個檔案很明顯就是.config
產生的,2中根據這個編譯模組還是核心.這個會被頂層的makefile包含
include/config/auto.conf:144:CONFIG_DM9000=y
- 還有標頭檔案自動產生的,這個給1中的原始碼使用.不論配置為Y或者為M模組,在這個標頭檔案都被定義為1
include/linux/autoconf.h:145:#define CONFIG_DM9000 1
//該檔案下的內容都是類似的如下
#define CONFIG_DM9000 1
#define CONFIG_SOLARIS_X86_PARTITION 1
#define CONFIG_SERIAL_NONSTANDARD 1
#define CONFIG_BLK_DEV_RAM_BLOCKSIZE 1024
至於是M與Y的區別,是在makefile中體現的
Makefile解析
詳細的解釋可以看/Documentation/kbuild/makefiles.txt
子目錄的Makefile
子目錄的makefile形式簡單,諸如下:
obj-$(CONFIG_DM9000) += dm9dev9000c.o
然後 CONFIG_DM9000
是在auto.conf
中定義,他是由.config
中定義為y(核心檔案)或者m(編譯為.ko模組),所以也就是形如
obj-y += xxx.o
obj-m += xxx.o
如果沒有被定義則是
obj - +=xxx.o 不被處理
架構下面的Makefile
arch/arm/Makefile
我們執行命令make uImage並不在頂層的makefile,而是在架構下面的makefile,所以它一定會被頂層的makefile包含
zImage Image xipImage bootpImage uImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/[email protected]
頂層Makefile
搜尋下arch
,可以下頂層的Makefile中找到包含了架構的Makefile
include $(srctree)/arch/$(ARCH)/Makefile
export KBUILD_DEFCONFIG
可以繼續搜發現同時定義了arm架構,這是在補丁檔案修改的
#ARCH ?= $(SUBARCH)
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-
同時搜尋下.config
檔案生成的auto.conf
,也在頂層包含
ifeq ($(dot-config),1)
# Read in config
-include include/config/auto.conf
Make解析
總結:最終在各個目錄下生成built-in.o
,根據arch/$(ARCH)/kernel/vmlinux.lds
的連結指令碼連結
uiamge
依賴於vmlinux
,uImage實際就是頭部資訊加上一個真正的核心,也就是vmlinux
就是真正的核心
zImage Image xipImage bootpImage uImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/[email protected]
可以發現在頂層makefile存在以下,也就是說這也是預設的目標檔案
all: vmlinux
繼續搜尋目標vmlinux
的依賴
# vmlinux image - including updated kernel symbols
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE
ifdef CONFIG_HEADERS_CHECK
$(Q)$(MAKE) -f $(srctree)/Makefile headers_check
endif
$(call if_changed_rule,vmlinux__)
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost [email protected]
$(Q)rm -f .old_version
其中相關變數繼續搜尋如下
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
# vmlinux-lds:連結指令碼。
# vmlinux-init:一些初始化程式碼。
# vmlinux-main:一些主要的程式碼(與核心核心相關的)。
vmlinux-init
#頂層 /makefile
init-y := init/
init-y := $(patsubst %/, %/built-in.o, $(init-y))
# 相當於 init-y = init/built-in.o
#架構 arch/arm/makefile
head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
#這裡MMUEXT 並沒有被定義,也就是最終就是 arch/arm/kernel/head.o arch/arm/kernel/init_task.o
patsubst分析
格式:$(patsubst <pattern>,<replacement>,<text> )
名稱:模式字串替換函式——patsubst。
功能:查詢<text>中的單詞(單詞以“空格”、“Tab”或“回車”“換行”分隔)是否符合模式<pattern>,如果匹配的話,則以<replacement>替換。
pattern=%/
在這裡應該是匹配所有的意思replacement=%/built-in.o
text=init/
也就是說在init/
下的所有檔名都被替換為+built-in.o
,視訊講的是最後會被編譯為built-in.o
,也就是相當於init-y = init/built-in.o
vmlinux-main
core-y 核心
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
# core-y
core-y := usr/
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
core-y := $(patsubst %/, %/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-y := lib/
# 在lib/中查詢 替換為lib.a
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
# 在lib/中查詢 替換為built-in.o
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
# 最終生成 built-in.o 和 lib.a
libs-y := $(libs-y1) $(libs-y2)
同樣的,也就是在最終生成built-in.o
和 替換為lib.a
drivers-y
驅動
drivers-y := drivers/ sound/ #(依賴了這兩個目錄)
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
同樣的,也就是在最終生成built-in.o
net-y
網路
net-y := net/
net-y := $(patsubst %/, %/built-in.o, $(net-y))
同樣的替換,生成built-in.o
編譯
uboot啟動的是uImage格式的linux kernel, 就是帶有頭部資訊的程式檔案
make uImage
從編譯流程分析下編譯過程
rm vmlinux #先刪除
make uImage V=1 # V=1 表示更加詳細顯示命令
摘錄下最後的日誌記錄
連結
檔案的順序在外部定義.第一個檔案為arch/arm/kernel/head.o
,具體的段由連結指令碼決定.這個連結命令實際也是存在.vmlinux.cmd
中的
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 arch/arm/common/built-in.o arch/arm/mach-s3c2410/built-in.o arch/arm/mach-s3c2400/built-in.o arch/arm/mach-s3c2412/built-in.o arch/arm/mach-s3c2440/built-in.o arch/arm/mach-s3c2442/built-in.o arch/arm/mach-s3c2443/built-in.o arch/arm/nwfpe/built-in.o arch/arm/plat-s3c24xx/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 arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o drivers/built-in.o sound/built-in.o net/built-in.o --end-group .tmp_kallsyms2.o
#-T arch/arm/kernel/vmlinux.lds
arch/arm/kernel/head.o
arch/arm/kernel/init_task.o
#這個與makefile中的是對應的
vmlinux-init := $(head-y) $(init-y)
head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
連結指令碼
arch/arm/kernel/
,檔案裡面的段按照順序,比如程式碼段是先放.text.head
,**一開始是放“ *”(指所有檔案)的 “ .text.head”段。**再放init
,檔案的順序由外部決定. 連結指令碼 vmlinux.lds
是由 vmlinux.lds.S
檔案生成的。
SECTIONS
{
. = (0xc0000000) + 0x00008000; #這個是虛擬地址
.text.head : { #先放所有檔案的 .text.head 段
_stext = .;
_sinittext = .;
*(.text.head)
}
.init : { /* Init code and data */ #再接著是放所有檔案的“ .init.text”段。
*(.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 = .;
*(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
__initcall_end = .;
__con_initcall_start = .;
*(.con_initcall.init)
__con_initcall_end = .;
__security_initcall_start = .;
*(.security_initcall.init)
__security_initcall_end = .;
. = ALIGN(32);
__initramfs_start = .;
usr/built-in.o(.init.ramfs)
__initramfs_end = .;
. = ALIGN(4096);
__per_cpu_start = .;
*(.data.percpu)
__per_cpu_end = .;
__init_begin = _stext;
*(.init.data)
. = ALIGN(4096);
__init_end = .;
}
燒寫核心
在uboot下,輸入k
進入燒寫,使用dnw燒寫程式,具體的k
實現了什麼命令,檢視下uboot程式碼,在cmd_menu.c
定義
strcpy(cmd_buf,
"usbslave 1 0x30000000; nand erase kernel;
nand write.jffs2 0x30000000 kernel $(filesize)");
run_command(cmd_buf, 0);
然後就可以通過b
啟動