小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

深度探索Linux操作系統(tǒng):系統(tǒng)構(gòu)建和原理解析

 菌心說 2021-04-27

前言

對(duì)于編譯內(nèi)核而言,一條make命令就足夠了。構(gòu)建內(nèi)核最困難的地方不是編譯,而是編譯前的配置。配置內(nèi)核時(shí),通常我們都能找到一些參考。

比如,對(duì)于桌面系統(tǒng),可以參考主流發(fā)行版的內(nèi)核配置,比如,對(duì)于嵌入式系統(tǒng),BSP(Board Support Package)中通常也提供內(nèi)核,但他們通常也僅是個(gè)可以工作的內(nèi)核而已,如果要一個(gè)占用空間更小、運(yùn)行更快的內(nèi)核,就需要開發(fā)人員手動(dòng)配置內(nèi)核,也確實(shí)存在著在某些情況下,我們找不到任何合適的參考,這時(shí)我們只能以手動(dòng)方式從零開始配置。

構(gòu)建內(nèi)核
內(nèi)核的構(gòu)建系統(tǒng)kbuild基于GNUMake,是一套非常復(fù)雜的系統(tǒng)。我們本無意著太多筆墨來分析kbuild,因?yàn)樽鳛殚_發(fā)者可能永遠(yuǎn)不需要去改動(dòng)內(nèi)核映像的構(gòu)建過程,但是了解這一過程,無論是對(duì)學(xué)習(xí)內(nèi)核,還是進(jìn)行內(nèi)核開發(fā)都有諸多幫助。所以在構(gòu)建內(nèi)核之前,本章首先討論了內(nèi)核的構(gòu)建過程。

3.1 內(nèi)核映像的組成
在討論內(nèi)核構(gòu)建前,我們先來簡(jiǎn)單了解一下內(nèi)核映像的組成,如圖3-1所示。

深度探索Linux操作系統(tǒng):系統(tǒng)構(gòu)建和原理解析

如果將內(nèi)核的映像比作航天器,則setup.bin部分就類似于火箭的一級(jí)推進(jìn)子系統(tǒng),負(fù)責(zé)將內(nèi)核加載進(jìn)內(nèi)存,并為后面內(nèi)核保護(hù)模式的運(yùn)行建立基本的環(huán)境。加載內(nèi)核的功能被分離到Bootloader中,setup.bin則退化為輔助Bootloader將內(nèi)核加載到內(nèi)存,包圍在32位保護(hù)模式部分外的是非解壓縮部分??梢钥醋魇腔鸺亩?jí)推進(jìn)子系統(tǒng),將壓縮的內(nèi)核解壓到合適的位置,并進(jìn)行內(nèi)核重定位,在完成這個(gè)環(huán)節(jié)后,其從內(nèi)核映像脫離。
內(nèi)核的32位保護(hù)模式部分vmlinux。相當(dāng)于航天器的有效載荷,最后運(yùn)行的衛(wèi)星或者宇宙飛船,只有留在軌道內(nèi)(內(nèi)存中)運(yùn)行。內(nèi)核構(gòu)建時(shí),將對(duì)有效載荷vmlinux進(jìn)行壓縮,然后與二級(jí)推進(jìn)系統(tǒng)裝配為vmlinux.bin。
下面我們就來看看內(nèi)核映像的各個(gè)組成部分。

3.1.1 一級(jí)推進(jìn)系統(tǒng)-setup.bin
在進(jìn)行內(nèi)核初始化時(shí),需要一些信息,如顯示信息、內(nèi)存信息等。曾經(jīng),這些信息由工作在實(shí)模式下的setup.bin通過BIOS獲取,保存在內(nèi)核中的變量boot_params中,變量boot_params 是結(jié)構(gòu)體 boot params 的一個(gè)實(shí)例。如setup.bin 中收集顯示信息的代碼如下:


1inux-3.7.4/arch/x86/boot/video.c:
static void store_video_mode(void)
(
struct biosregs ireg, oreg;
initregs(&ireg);
ireg.ah=0x0f;
intcall(0x10,&ireg,&oreg);

boot_params.screen_info.orig_video_mode=oreg.al&0x7f;

boot_params.screen_info.orig_video_page=oreg.bh;
store_video_mode首先調(diào)用函數(shù)intcall獲取顯示方面的信息,并將其保存在boot_params的screen_info中。intcall是調(diào)用BIOS中斷的封裝,0x10是BIOS提供的顯示服務(wù)(Video Service)的中斷號(hào),代碼如下:

linux-3.7.4/arch/x86/boot/bioscall.s:
intcall:
/* Self-modify the INT instruction. Ugly, but works. */
cmpb
gal, 3f
je 1f
movb
gal, 3f
jmp 1f
/* Synchronize pipeline */
1:
...
.byte
Oxcd
/* INT opcode */
3: .byte
在代碼中我們并沒有看到熟悉的調(diào)用BIOS中斷的身影,如“int$0x10”,但是我們看到了一個(gè)特殊的字符——Oxcd。正如其后面的注釋所言,Oxcd就是x86匯編指令I(lǐng)NT的機(jī)器碼,如表3-1所示。

深度探索Linux操作系統(tǒng):系統(tǒng)構(gòu)建和原理解析

根據(jù)x86的INT指令說明,Oxcd后面跟著的1字節(jié)就是BIOS中斷號(hào),這就是上面代碼中標(biāo)號(hào)為3處分配1字節(jié)的目的。
0
函數(shù)intcall的開頭,比較寄存器al中的值與標(biāo)號(hào)3處占用的1字節(jié),若直接向前跳轉(zhuǎn)至標(biāo)號(hào)1處,否則將寄存器al中的值復(fù)制到標(biāo)號(hào)3處的1個(gè)字節(jié)空間。那么寄存器al中保存的是什么呢?
默認(rèn)情況下,GCC使用樹來傳遞參數(shù)??梢允褂谩癬attribute_(regparm(n)”修飾函數(shù),或者通過向GCC傳遞命令行參數(shù)“-mregparm=n”來指定GCC使用寄存器傳遞參數(shù),其中n表示使用寄存器傳遞參數(shù)的個(gè)數(shù)。在編譯setup.bin時(shí),kbuild使用了后者,編譯腳本如下所示:


linux-3.7.4/arch/x86/boot/Makefile:
KBUILD_CFLAGS
:=...-mregparm=3...
如此,函數(shù)的第一個(gè)參數(shù)通過寄存器eax/ax傳遞,第二個(gè)參數(shù)通過ebx/bx傳遞,等等,而不是通過樹傳遞了。因此,上面的寄存器al中保存的是函數(shù)intcall的第一個(gè)參數(shù),即BIOS中斷號(hào)。
在完成信息收集后,setup.bin將CPU切換到保護(hù)模式,并跳轉(zhuǎn)到內(nèi)核的保護(hù)模式部分執(zhí)行。如我們前面討論的,setup.bin作為一級(jí)推進(jìn)系統(tǒng),即將結(jié)束歷史使命,所以內(nèi)核將setup.bin收集的保存在setup.bin的數(shù)據(jù)段的變量boot_params復(fù)制到vmlinux的數(shù)據(jù)段中。

隨著BIOS標(biāo)準(zhǔn)的出現(xiàn),尤其是EFI的出現(xiàn),為了支持這些新標(biāo)準(zhǔn),開發(fā)者們制定了32位啟動(dòng)協(xié)議(32-bit boot protocol)。在32位啟動(dòng)協(xié)議下,由Bootloader實(shí)現(xiàn)收集這些信息的功能,內(nèi)核啟動(dòng)時(shí)不再需要首先運(yùn)行實(shí)模式部分(即setup.bin),而是直接跳轉(zhuǎn)到內(nèi)核的保護(hù)模式部分。因此,在32位啟動(dòng)協(xié)議下,不再需要setup.bin收集內(nèi)核初始化時(shí)需要的相關(guān)信息。但是這是否意味著可以徹底放棄setup.bin呢?

二級(jí)推進(jìn)系統(tǒng)-內(nèi)核非壓縮部分

內(nèi)核經(jīng)過壓縮,因此運(yùn)行前需要解壓縮,但是誰來負(fù)責(zé)內(nèi)核映像的解壓呢?解鈴還須系鈴人,既然內(nèi)核在構(gòu)建時(shí)自己壓縮了自己,當(dāng)然解壓縮也要由內(nèi)核映像自己完成。
除了解壓以外,非壓縮部分還負(fù)責(zé)內(nèi)核重定位。內(nèi)核可以配置為可重定位的(relocatable),所謂可重定位即內(nèi)核可以被Bootloader加載到內(nèi)存任何位置。但是在鏈接內(nèi)核時(shí),鏈接器需要假定一個(gè)加載地址,然后以這個(gè)假定地址為參考,為各個(gè)符號(hào)分配運(yùn)行時(shí)地址。顯然,如果加載地址和鏈接時(shí)假定的地址不同,那么需要對(duì)符號(hào)的地址進(jìn)行重新修訂,這就是內(nèi)核重定位。
內(nèi)核非壓縮部分工作在保護(hù)模式下,其占用的內(nèi)存在完成使命后將會(huì)被釋放。

有效載荷-vmlinux

kbuild分別構(gòu)建內(nèi)核各個(gè)子目錄中的目標(biāo)文件,然后將它們鏈接為vmlinux。為了縮小內(nèi)核體積,kbuild刪除了vmlinux中一些不必要的信息,并將其命名為vmlinux.bin,最后將vmlinux.bin壓縮為vmlinux.bin.gz。那么為什么內(nèi)核要進(jìn)行壓縮呢?

1.最初,因?yàn)樵谀承w系架構(gòu)上,特別是1386,系統(tǒng)啟動(dòng)時(shí)運(yùn)行于實(shí)模式狀態(tài),可以尋址空間只能在1MB以下,內(nèi)核尺寸過大,將無法正常加載,因此,對(duì)內(nèi)核進(jìn)行了壓縮。

2.另外一個(gè)原因是,2.4及更早版本的內(nèi)核,需要可以容納在一張軟盤上,所以內(nèi)核也要進(jìn)行壓縮。

映像的格式

Linux作為操作系統(tǒng)的hosted environment環(huán)境下,二進(jìn)制文件使用ELF格式,操作系統(tǒng)也提供ELF文件的加載器。但是,操作系統(tǒng)本身確是工作在freestanding environment 環(huán)境下。操作系統(tǒng)顯然不能強(qiáng)制要求 Bootloader 也提供ELF加載器。

但是,從Linux 2.6.26版本開始,內(nèi)核的壓縮部分,即有效載荷部分,采用了ELF格式。至于為什么采用ELF格式,Patch的提交者給出了原因:
This allows other boot loaders such as the Xen domain builder the opportunity to extract the ELF file.

當(dāng)內(nèi)核映像不是裸二進(jìn)制格式時(shí),我們需要有一個(gè)ELF加載器來將ELF格式的內(nèi)核映像轉(zhuǎn)化為裸二進(jìn)制格式。那么誰來充當(dāng)這個(gè)ELF加載器呢?
正所謂“蜘蜂捕蟬,黃雀在后”。內(nèi)核的非壓縮部分調(diào)用函數(shù)decompress解壓內(nèi)核后,緊接著就調(diào)用了函數(shù)parse_elf來處理ELF格式的內(nèi)核映像,代碼如下:

11nux-3.7.4/arch/x86/boot/compressed/misc.c:agml inkage void decompress kernel (...)decompress (input_data, input_len, ...);
parse_elf(output);
static void parse_elf (void *output)
for (i - 0; i < ehdr.e_phnum; i++) {
phdr = &phdrs [i];
switch (phdr->p_type) {
case PT_LOAD:
#ifdef CONFIG RELOCATABLE
dest = output;

dest += (phdr->p_paddr - LOAD_PHYSICAL_ADDR);
#else
dest = (void *) (phdr->p_paddr);
#endif
memcpy (dest, output + phdr->p_offset, phdr->p_filesz);
break;
default:/*Ignore other PT_**/break;
HZ BOOKS
free(phdrs);
在ELF文件中,存放代碼和數(shù)據(jù)的段的類型是PT_LOAD,因此,僅處理這個(gè)類型的段即可。在函數(shù)parse_elf中,對(duì)于類型是PT_LOAD的段,其按照Program Header Table中的信息,將它們移動(dòng)到鏈接時(shí)指定的物理地址處,即p_paddr。當(dāng)然,如果內(nèi)核是可重定位的,還要考慮內(nèi)核實(shí)際加載地址與編譯時(shí)指定的加載地址的差值。
如果Bootloader不是所謂的“the Xen domain builder”,我們完全沒有必要保留內(nèi)核的壓縮部分為ELF格式,并略去啟動(dòng)時(shí)進(jìn)行的“parse_elf”。具體方法如下:
(1)將壓縮部分鏈接為裸二進(jìn)制格式
將傳遞給命令objcopy的參數(shù)追加“-Obinary”,如下面使用黑體標(biāo)識(shí)的部分
:1inux-3.7.4/arch/x86/boot/compressed/Makefile:
OBJCOPYFLAGS_vmlinux.bin:=-R.comment-s-o binary
$(obj)/vmlinux.bin:vmlinux FORCE
$(call if_changed,objcopy)

總結(jié)

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多