今天是臘八節,說好的女票要給我作的臘八粥就這樣泡湯了,好傷心,好心酸呀,看來代碼寫久了真的是惹人煩滴,因此告誡各位技術男敲醒警鐘,不要想我看齊,否則就只能和代碼爲伴了的~~話說沒了臘八粥但仍是有代碼,還有各位讀者的支持呀,因此得繼續寫下去,靜下心來,完成Linux內核的學習,堅持,加油~linux
到目前爲止,咱們已經認識了Linux內核子系統,也探究了系統的初始化過程,而且深刻探索了start_kernel()函數,一樣,瞭解內核映像的建立也是很是重要的,接下來將討論一下內核映像的編譯和連接過程,那麼這些固然須要工具鏈了,工具鏈包含編譯程序、彙編程序、連接程序,是建立Linux內核映像的一組程序集合,下圖說明了工具鏈的鏈式關係:shell
可執行ELF目標文件包括:ELF頭,程序頭表(用於加載的節),第1節,第2節。。。。節頭表(可選)編程
ELF頭文件架構
typedef struct elf32_hdr{
unsigned char e_ident[EI_NIDENT]; //標識該文件是否爲ELF文件
Elf32_Half e_type; //指定目標文件類型,例如可執行文件,重定位文件,共享的目標文件
Elf32_Half e_machine; //被編譯文件所在系統的體系結構
Elf32_Word e_version; //目標文件的版本
Elf32_Addr e_entry; /* Entry point */ //程序的起始地址
Elf32_Off e_phoff; //保存程序頭表在文件中的偏移量
Elf32_Off e_shoff; //保存節頭表在文件中的偏移量
Elf32_Word e_flags; //保存於特定與處理器的標誌
Elf32_Half e_ehsize; //字段保存ELF頭的大小
Elf32_Half e_phentsize; //保存程序頭表中的每一項的大小
Elf32_Half e_phnum; //程序頭中表項的個數
Elf32_Half e_shentsize; //節頭表中每一項的大小
Elf32_Half e_shnum; //保存節頭中項的數量,代表該文件中有多少節
Elf32_Half e_shstrndx; //保存節頭中節字符串的索引
} Elf32_Ehdr;
節頭表ide
typedef struct elf32_shdr {
Elf32_Word sh_name; //包含節名
Elf32_Word sh_type; //包含節的內容
Elf32_Word sh_flags; //各類屬性的內容
Elf32_Addr sh_addr; //節在內存映像中的地址
Elf32_Off sh_offset; //保存ELF文件中這一節中初始字節的偏移量
Elf32_Word sh_size; //包含節的大小
Elf32_Word sh_link; //表連接的索引
Elf32_Word sh_info; //包含附加信息
Elf32_Word sh_addralign; //包含地址對其的約束
Elf32_Word sh_entsize; //節中每項的大小
} Elf32_Shdr;
非可執行ELF文件節函數
節點 | 說明 |
.data | 已初始化的數據 |
bss | 爲初始化的數據 |
.hash | 符號散列表 |
.init | 初始化代碼 |
.symtab | 符號表 |
.text | 可執行的指令 |
.plt | 過程連接表 |
.rodata | 只讀數據 |
dynamic | 動態連接信息 |
程序頭表工具
typedef struct elf64_phdr {
Elf64_Word p_type; //描述該段的類型
Elf64_Word p_flags; //以p_type而定
Elf64_Off p_offset; //<span style="font-family: Arial, Helvetica, sans-serif;">該段的開始相對於文件開始的偏移量</span>
Elf64_Addr p_vaddr; //段虛擬地址
Elf64_Addr p_paddr; //段的虛擬地址
Elf64_Xword p_filesz; //文件映像中該段的字節數
Elf64_Xword p_memsz; //內存映像中該段的字節數
Elf64_Xword p_align; //描述要對齊的段在內存中如何對齊,該值是2的整數次冪
} Elf64_Phdr;
經過這些信息,系統函數exec()和連接程序合做,爲可執行程序在內存中建立進程映像,該過程以下:post
那麼內核是如何被編譯成二進制文件的呢,又是如何在執行前裝入內存。下面將開始介紹編譯內核源代碼。內存啓動始於執行arch/x86/boot/目錄中的實模式彙編代碼。查看arch/x86/kernel/setup_32.c文件能夠看出保護模式的內核怎樣獲取實模式內核收集的信息。第一條信息來自於init/main.c中的代碼,深刻挖掘init/calibrate.c能夠對BogoMIPS校準理解得更清楚,而include/asm-your-arch/bugs.h則包含體系架構相關的檢查。學習
內核中的時間服務由駐留於arch/your-arch/kernel/中的體系架構相關的部分和實現於kernel/timer.c中的通用部分組成。從include/linux/time*.h頭文件中能夠獲取相關的定義。
jiffies定義於linux/jiffies.h文件中。HZ的值與處理器相關,能夠從include/asm-your-arch/ param.h找到,內存管理源代碼存放在頂層mm/目錄中。
Linux的官方源代碼發佈網址是www.kernel.org。其源代碼目錄結構示意圖以下:
利用內核配置工具自動生成.config的內核配置文件,這是編譯的第一步,.config文件位於源代碼目錄下,其選項的位置根據它們在內核配置工具中的位置進行排序,咱們來看看一個.config文件的節選:
1 #
2 # Automatically generated make config: don't edit
3 # 4 CONFIG_X86=y 5 CONFIG_MMU=y 6 CONFIG_UID16=y 7 CONFIG_GENERIC_ISA_DMA=y //這4行位於頂層菜單中 8 9 # 10 # Code maturity level options 11 # 12 CONFIG_EXPERIMENTAL=y 13 CONFIG_CLEAN_COMPILE= 14 CONFIG_STANDALONE=y 15 CONFIG_BROKEN_ON_SMP=y //這4行位於代碼成熟度選項菜單中 16 17 # 18 # General setup 19 # 20 CONFIG_SWAP=y 21 CONFIG_SYSVIPC=y 22 #CONFIG_POSIX_MQUEUE is not set 23 CONFIG_BSD_PROCESS_ACCT=y //這4行位於通用設置選項菜單中
最後來粗略的介紹一下Linux內核的Makefile文件,也只能簡單的介紹一下啦,這個但是重難點,這裏我稍微說一下,之後會具體去學習。Linux內核是一種單體內核,可是經過動態加載模塊的方式,使它的開發很是靈活 方便。那麼,它是如何編譯內核的呢?咱們能夠經過分析它的Makefile入手。如下是 一個簡單的hello內核模塊的Makefile.
ifneq ($(KERNELRELEASE),)
obj-m:=hello.o
else
KERNELDIR:=/lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *.mod.c *.mod.o *.ko
endif
首先,因爲make 後面沒有目標,因此make會在Makefile中的第一個不是以.開頭的目標做爲默認的目標執行。因而default成爲make的目標。make會執行 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules shell是make內部的函數,make執行了兩次。
第一次執行時是讀hello模塊的源代碼所在目錄/home/s tudy/prog/mod/hello/下的Makefile。
第二次執行時是執行/usr/src/linux/下的Makefile時.
這其中很複雜,我也不知道怎麼講了。關於make modules的更詳細的過程能夠在scripts/Makefile.modpost文件的註釋 中找到。不過我找到了一個大牛寫的跟我一下學Makefile的博客,我把博客地址附在下面,供你們參考一下:http://blog.csdn.net/haoel/article/details/2886/
小結
本章探究了目標文件的編譯,連接過程,以及目標文件的結構,以便理解可執行代碼的最終形式,構建Linux內核涵蓋了內核編譯所須要的工具,最後還簡單的描述了Makefile,,這些都是難點,,得多加縮習啦,,儘管今天沒吃到臘八粥,可是轉轉鍋仍是很給力的,吃到如今還不餓,是一個難忘的一天 ~~
版權全部,轉載請註明轉載地址:http://www.cnblogs.com/lihuidashen/p/4253752.html