久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Linux 2.4 內(nèi)核說明文檔(引導(dǎo)篇)

 guitarhua 2011-12-31
Linux >> 查看資訊

字號(hào): | 推薦給好友 上一篇 | 下一篇

Linux 2.4 內(nèi)核說明文檔(引導(dǎo)篇)

發(fā)布: 2007-5-26 13:14 | 作者: 未知 | 來源: ChinaUnix.net | 查看: 88次 | 進(jìn)入軟件測(cè)試論壇討論

領(lǐng)測(cè)軟件測(cè)試網(wǎng)

[list=]
本文檔是《Linux2.4 內(nèi)核說明文檔》中的第一部分,。以下是整個(gè)文檔大致目錄: 
1,啟動(dòng) 
2,進(jìn)程和中斷管理 ([url=http://bbs./forum/viewtopic.php?t=571760]http://bbs./forum/viewtopic.php?t=571760[/url])
3,,虛擬文件系統(tǒng) 
4,,Linux 頁緩沖 
5,,IPC機(jī)制 

本篇文檔目錄:
1.1. 創(chuàng)建Linux內(nèi)核鏡像
1.2. 引導(dǎo):概述
1.3. 引導(dǎo):BOIS POST
1.4. 引導(dǎo):bootsector和setup
1.5. 采用LILO引導(dǎo)器
1.6. 高級(jí)初始化
1.7. SMP機(jī)在x86系統(tǒng)上引導(dǎo)
1.8. 釋放初始化數(shù)據(jù)和代碼
1.9. 處理內(nèi)核命令

以下是正文:

1. 引導(dǎo)
1.1. 創(chuàng)建Linux內(nèi)核鏡像
本部分解釋了編譯內(nèi)核時(shí)每個(gè)步驟以及每個(gè)步驟的輸出,。這個(gè)創(chuàng)建過程依賴于不同的體系結(jié)構(gòu),,這里強(qiáng)調(diào)一下我們僅考慮創(chuàng)建一個(gè)Linux x86的內(nèi)核。當(dāng)用戶輸入“make zImage”或者“make bzImage”時(shí),,輸出的可啟動(dòng)內(nèi)核鏡像就分別存放為arch/i386/boot/zImage或者arch/i386/boot/bzImage,。下面來看看這個(gè)鏡像是怎么創(chuàng)建的:
1) 首先C和匯編源文件被編譯成ELF中間文件(.o),其中一部分按照邏輯分組打包成壓縮文件(.a),。
2) 調(diào)用ld指令將以上的.o和.a文件被鏈接成一個(gè)靜態(tài)的80386可執(zhí)行文件vmlinux,。
3) 接著調(diào)用nm vmlinux 指令剔除不相關(guān)和不感興趣的符號(hào)并創(chuàng)建系統(tǒng)關(guān)系圖。
4) 進(jìn)入arch/i386/boot目錄,。
5) Bootsect.S文件按照目標(biāo)是bzImage(zImage)在定義(不定義) –D_BIG_KERBEL_ 宏 下進(jìn)行預(yù)處理,,結(jié)果分別存為bbootsect.s(bootsect.s)。
6) Bbootsect.s文件被編譯并轉(zhuǎn)換成“raw binary”格式的bbootsect文件(bootsect.s 被轉(zhuǎn)換成“raw”格式文件bootsect),。
7) setup.s(setup.s 包含了video.s文件)被預(yù)處理成bzImage需要的bsetup.s或者zImage需要的setup.s文件,。這個(gè)過程和bootsector一樣,bzImage鏡像需要定義.D__BIG_KERNEL__宏,,結(jié)果被轉(zhuǎn)換成“raw binary”格式的bsetup,,zImage鏡像則被轉(zhuǎn)換成“raw”格式的setup。
8) 進(jìn)入arch/i386/boot/compressed目錄,,移出/usr/src/linux/vmlinux文件中ELF標(biāo)識(shí)節(jié).note和.comment ,,并將其轉(zhuǎn)換成raw binary格式存放到臨時(shí)文件$tmppiggy。
9) 將$tmppiggy壓縮成$tmppiggy.gz
10) .將$tmppiggy.gz鏈接成重定向文件piggy.o
11) 編譯壓縮程序head.S 和misc.c文件
12) 將head.o,、misc.o和piggy.o鏈接成bvmlinux(或者vmlinux),,注意vmlinux的標(biāo)號(hào).Ttext 0x1000和bvmlinux的標(biāo)號(hào).Ttext 0x100000的不同,這是由于bzImage壓縮裝載器是從高位裝載的,。
13) 將bvmlinux轉(zhuǎn)換成“raw binary”文件bvmlinux.out,,移出ELF節(jié).note 和.comment。
14) 回到arch/i386/boot目錄并調(diào)用tools/build將bbootsect,、bsetup和壓縮后的bvmlinux鏈接成bzImage,。這個(gè)過程將向bootsector末尾添加重要的變量例如setup_sects和root_dev。
bootsector的大小總是512字節(jié),,setup的大小必須大于4個(gè)分節(jié)且受限于12K,,這個(gè)規(guī)則如下:
0x4000 bytes >= 512 + setup_sects*512 + 運(yùn)行bootsector/setup所需堆棧空間
在后面將說明是那個(gè)部分造成了這種限制,。
BzImage文件大小的上限采用LILO啟動(dòng)時(shí)為2.5M,,采用冷啟動(dòng)如軟盤或者光盤等則為1048560字節(jié)。
注意,,tools/build工具檢驗(yàn)了boot段的大小,、內(nèi)核鏡像的大小和setup的低范圍地址,,并沒有檢測(cè)setup的高范圍地址。因此,,在setup.S文件末尾的“.space”節(jié)增加一個(gè)大的地址數(shù)值就會(huì)很容易創(chuàng)建一個(gè)無法使用的內(nèi)核,。

1.2. 引導(dǎo):概述
啟動(dòng)過程是和體系結(jié)構(gòu)相關(guān)的,這里我們僅關(guān)注IBM PC/IA32體系,。由于舊有設(shè)計(jì)以及向前兼容,,PC機(jī)采用了以前流行的風(fēng)格啟動(dòng)操作系統(tǒng)。這個(gè)過程可以被分為一下六個(gè)邏輯步驟:
1) BOIS選擇啟動(dòng)設(shè)備,。
2) 從啟動(dòng)設(shè)備裝載bootsector。
3) Bootsector裝載setup,,解壓縮程序和內(nèi)核鏡像,。
4) 在保護(hù)模式下解壓內(nèi)核。
5) 匯編代碼執(zhí)行低級(jí)初始化,。
6) 執(zhí)行上層初始化,。

1.3. 引導(dǎo):BOIS POST
1) 電源啟動(dòng)時(shí)鐘發(fā)生器并在總線上產(chǎn)生一個(gè)#POWERGOOD的中斷。
2) 產(chǎn)生CPU的RESET中斷(此時(shí)CPU處于8086工作模式),。
3) %ds=%es=%fs=%gs=%ss=0, %cs=0xFFFF0000,%eip = 0x0000FFF0 (ROM BIOS POST code). 
4) 在中斷無效狀態(tài)下執(zhí)行所有POST檢查,。
5) 在地址0初始化中斷向量表IVT。
6) 0x19中斷以啟動(dòng)設(shè)備號(hào)為參數(shù)調(diào)用BIOS啟動(dòng)裝載程序,。這個(gè)程序從0扇面1扇區(qū)物理位置0x7C00開始裝載,。

1.4. 引導(dǎo):bootsector和setup
用來啟動(dòng)內(nèi)核的bootsector可以是以下幾種:
Linux bootsector
LILO等bootsector
無bootsector
以下詳細(xì)解釋linux bootsector。下面一些代碼,,負(fù)責(zé)初始化用作段變量的宏定義,。
29 SETUPSECS = 4 /* default nr of setup.sectors */ 
30 BOOTSEG = 0x07C0 /* original address of boot.sector */ 
31 INITSEG = DEF_INITSEG /* we move boot here . out of the way */ 
32 SETUPSEG = DEF_SETUPSEG /* setup starts here */ 
33 SYSSEG = DEF_SYSSEG /* system loaded at 0x10000 (65536) */ 
34 SYSSIZE = DEF_SYSSIZE /* system size: # of 16.byte clicks */ 
(左邊的數(shù)值是代碼的行號(hào)) 在文件include/asm/boot.h中定義了DEF_INITSEG, DEF_SETUPSEG, DEF_SYSSEG和DEF_SYSSIZE的值。
/* Don't touch these, unless you really know what you're doing. */ 
#define DEF_INITSEG 0x9000 
#define DEF_SYSSEG 0x1000 
#define DEF_SETUPSEG 0x9020 
#define DEF_SYSSIZE 0x7F00 
以下來看看bootsect.S的源代碼:
54 movw $BOOTSEG, %ax 
55 movw %ax, %ds 
56 movw $INITSEG, %ax 
57 movw %ax, %es 
58 movw $256, %cx 
59 subw %si, %si 
60 subw %di, %di 
61 cld 
62 rep 
63 movsw 
64 ljmp $INITSEG, $go 
65 # bde . changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We 
66 # wouldn't have to worry about this if we checked the top of memory. Also 
67 # my BIOS can be configured to put the wini drive tables in high memory 
68 # instead of in the vector table. The old stack might have clobbered the 
69 # drive table. 
70 go: movw $0x4000.12, %di # 0x4000 is an arbitrary value >= 
71 # length of bootsect + length of 
72 # setup + room for stack; 
73 # 12 is disk parm size. 
74 movw %ax, %ds # ax and es already contain INITSEG 
75 movw %ax, %ss 
76 movw %di, %sp # put stack at INITSEG:0x4000.12. 
代碼54行~63行將bootsector從地址0x7C00移動(dòng)到0x90000,,由以下過程完成:
1) set %ds:%si to $BOOTSEG:0 (0x7C0:0 = 0x7C00) 
2) set %es:%di to $INITSEG:0 (0x9000:0 = 0x90000) 
3) set the number of 16bit words in %cx (256 words = 512 bytes = 1 sector) 
4) clear DF (direction) flag in EFLAGS to auto.increment addresses (cld) 
5) go ahead and copy 512 bytes (rep movsw) 
The reason this code does not use rep movsd is intentional (hint . .code16). 
64行跳轉(zhuǎn)到標(biāo)號(hào)go:(一個(gè)最新創(chuàng)建的bootsector的拷貝,,也就是在0x9000段)。這和接下來的三段指令(64~76行)在$INITSEG:0x4000.0xC 段初始化一個(gè)堆棧,,也就是指令%ss = $INITSEG (0x9000) 和 %sp = 0x3FF4 (0x4000.0xC),。這就是我們前面提到的setup大小限制的來歷(見1.1節(jié))。
77~103行代碼建立了第一個(gè)磁盤的參數(shù)表,,以便允許多扇區(qū)讀操作,。
77 # Many BIOS's default disk parameter tables will not recognise 
78 # multi.sector reads beyond the maximum sector number specified 
79 # in the default diskette parameter tables . this may mean 7 
80 # sectors in some cases. 
81 # 
82 # Since single sector reads are slow and out of the question, 
83 # we must take care of this by creating new parameter tables 
84 # (for the first disk) in RAM. We will set the maximum sector 
85 # count to 36 . the most we will encounter on an ED 2.88. 
86 # 
87 # High doesn't hurt. Low does. 
88 # 
89 # Segments are as follows: ds = es = ss = cs . INITSEG, fs = 0, 
90 # and gs is unused. 
91 movw %cx, %fs # set fs to 0 
92 movw $0x78, %bx # fs:bx is parameter table address 
93 pushw %ds 
94 ldsw %fs:(%bx), %si # ds:si is source 
95 movb $6, %cl # copy 12 bytes 
96 pushw %di # di = 0x4000.12. 
97 rep # don't need cld .> done on line 66 
98 movsw 
99 popw %di 
100 popw %ds 
101 movb $36, 0x4(%di) # patch sector count 
102 movw %di, %fs:(%bx) 
103 movw %es, %fs:2(%bx) 
通過0x13BOIS服務(wù)0號(hào)函數(shù)重置軟盤管理器,并且在bootsector完成后立即載入setup部分,。也就是說在物理地址0x90200 ($INITSEG:0x200)處再次調(diào)用0x13BOIS服務(wù)2號(hào)函數(shù),。這個(gè)過程發(fā)生在107~124行。
107 load_setup: 
108 xorb %ah, %ah # reset FDC 
109 xorb %dl, %dl 
110 int $0x13 
111 xorw %dx, %dx # drive 0, head 0 
112 movb $0x02, %cl # sector 2, track 0 
113 movw $0x0200, %bx # address = 512, in INITSEG 
114 movb $0x02, %ah # service 2, "read sector(s)" 
115 movb setup_sects, %al # (assume all on head 0, track 0) 
116 int $0x13 # read it 
117 jnc ok_load_setup # ok . continue 
118 pushw %ax # dump error code 
119 call print_nl 
120 movw %sp, %bp 
121 call print_hex 
122 popw %ax 
123 jmp load_setup 
124 ok_load_setup: 
如果由于某些原因出錯(cuò),,例如無法使用的軟盤或者在運(yùn)行過程中有人彈出了磁盤等,,裝載過程將輸出錯(cuò)誤代碼并且無限循環(huán)嘗試本過程,。除非重試成功(這通常不會(huì)發(fā)生,如果出現(xiàn)其他錯(cuò)誤后果將更嚴(yán)重),,唯一退出這個(gè)循環(huán)的辦法就是重新啟動(dòng)機(jī)器,。
如果成功裝載配置代碼部分,流程將跳轉(zhuǎn)到ok_load_setup標(biāo)簽,。
緊接著,,啟動(dòng)程序就在物理地址0x10000裝載壓縮后的內(nèi)核。這樣做是為了保護(hù)低位(0~64K)內(nèi)存的固件數(shù)據(jù)區(qū),。在內(nèi)核裝載后,,啟動(dòng)程序跳轉(zhuǎn)到地址$SETUPSEG:0。一旦這些固件數(shù)據(jù)不再需要的時(shí)候,,它們會(huì)被從0x10000移動(dòng)到0x1000地址的完整內(nèi)核鏡像覆蓋,。這個(gè)過程由setup.S完成,它主要設(shè)置保護(hù)模式下的狀態(tài),,并跳轉(zhuǎn)到壓縮內(nèi)核的起始物理地址0x1000,,也就是arch/386/boot/compressed/{head.S,misc.c}文件。它設(shè)置堆棧,,調(diào)用decompress_kernel()解壓縮內(nèi)核到0x100000并跳轉(zhuǎn)到該地址,。
注意以前的裝載器(如老版本的LILO)僅能裝載setup的前4節(jié),這就是setup里面有必要時(shí)候裝載自身重置的代碼的原因,。同樣,,setup代碼必須注意多種不同類型版本的裝載器與zImage/bzImage的結(jié)合,它也因此非常復(fù)雜,。
讓我們分析一下bootsector代碼里允許裝載大內(nèi)核(即bzImage)的組裝部分(kludge),。首先setup部分像往常一樣裝載到地址0x90200,但是采用調(diào)用BIOS服務(wù)將數(shù)據(jù)從低位內(nèi)存移動(dòng)到高位內(nèi)存的輔助程序,,內(nèi)核一次可裝載64K,。這個(gè)輔助程序在bootsect.S中的bootsect_kludge曾提到,并在setup.S中定義為bootsect_helper,。Setup.S中的bootsect_kludge標(biāo)簽段包含了setup段的代碼以及其中bootsect_helper代碼的偏移量,,這樣bootsector可以調(diào)用lcall指令跳轉(zhuǎn)到bootsect_helper。bootsect_helper包含在setup.S文件里的原因很簡(jiǎn)單,,因?yàn)閎ootsect.S沒有剩余的空間了,。這個(gè)程序調(diào)用0x15號(hào)BIOS服務(wù)以便移動(dòng)到高位內(nèi)存并復(fù)位%es,使其總是指向0x10000,。這保證了bootsect.S里的代碼在從磁盤拷貝數(shù)據(jù)時(shí)不會(huì)溢出,。

1.5. 采用LILO引導(dǎo)器
在原始的linux bootsector上采用專門的引導(dǎo)器(LILO)有很多好處:
1) 可在多個(gè)操作系統(tǒng)或多個(gè)內(nèi)核之間選擇。
2) 可以輸入內(nèi)核指令參數(shù),。
3) 可以裝載更大(2.5M甚至1M)的內(nèi)核文件,。
老版本的LILO不能裝載bzImage內(nèi)核,,新版本采用了bootsect+setup這樣的技術(shù),通過BIOS服務(wù)將數(shù)據(jù)從低位內(nèi)存拷貝到高位,。一些人在為是否移出對(duì)zImage支持而爭(zhēng)論,,這個(gè)主要原因是在已損壞的BIOS服務(wù)時(shí),zImage可以正常裝載而bzImage卻不能,。
LILO所做的最后一件事情就是跳轉(zhuǎn)到setup.S,,然后就是正常的處理過程。

1.6. 高級(jí)初始化
對(duì)于“高級(jí)初始化”我們認(rèn)為這不是直接和引導(dǎo)過程相關(guān),,即使它的部分實(shí)現(xiàn)代碼也是用匯編語言編寫的,。也就是arch/i386/kernel/head.S文件,它是未壓縮內(nèi)核的最初部分,。整個(gè)過程如以下部分:
1) 初始化段數(shù)值(%ds = %es = %fs = %gs = __KERNEL_DS = 0x18),。
2) 初始化內(nèi)存頁表。
3) 設(shè)置%cr0的PG位,,使內(nèi)存分頁機(jī)制有效。
4) 將BSS清零(在SMP機(jī)上,,僅第一個(gè)CPU會(huì)執(zhí)行此操作),。
5) 拷貝內(nèi)核引導(dǎo)指令的前2k。
6) 利用EFLAGS檢測(cè)CPU類型,,如果可能,,還有cpuid,以便探測(cè)386或者更高型號(hào),。
7) 第一個(gè)CPU調(diào)用start_kernel函數(shù),,如果ready等于1,其他CPU則調(diào)用arch/i386/kernel/smpboot.c文件的:initialize_secondary()函數(shù),,這個(gè)函數(shù)重新裝載esp/eip且不再返回,。
init/main.c文件的start_kernel函數(shù)是用c語言編寫的,并完成以下工作:
1) 獲得一個(gè)全局的內(nèi)核鎖(這在單CPU完成初始化的時(shí)候需要),。
2) 執(zhí)行細(xì)節(jié)配置(內(nèi)存頁布局分析,,再次拷貝引導(dǎo)命令等)。
3) 輸出帶有版本號(hào)的內(nèi)核標(biāo)語,,編譯器通常將這些創(chuàng)建到內(nèi)核保存消息的ring緩沖中,。版本號(hào)來自于init/version.c的linux_banner變量,在使用cat /proc/version時(shí)也會(huì)顯示同樣的字符串,。
4) 初始化trap,。
5) 初始化IRQ。
6) 初始化調(diào)度所需數(shù)據(jù),。
7) 初始化時(shí)鐘數(shù)據(jù),。
8) 初始化軟中斷子系統(tǒng),。
9) 處理引導(dǎo)命令。
10) 初始化控制臺(tái),。
11) 如果模塊支持功能被編譯到內(nèi)核,,初始化動(dòng)態(tài)模塊裝載器。
12) 如果支持“profile=”命令,,初始化配置文件緩沖區(qū),。
13) 調(diào)用kmem_cache_init()函數(shù),初始化大多數(shù)的塊分配算法,。
14) 中斷有效,。
15) 為當(dāng)前CPU計(jì)算BogoMips值。
16) 調(diào)用mem_init()函數(shù)計(jì)算max_mapnr,、totalram_pages和high_memory,,并輸出“Memory……”。
17) 調(diào)用kmem_cache_sizes_init()函數(shù)完成塊分配算法初始化,。
18) 初始化procfs使用的數(shù)據(jù)結(jié)構(gòu),。
19) 調(diào)用for_init(),創(chuàng)建uid_cache進(jìn)程,,基于內(nèi)存可用總量初始化max_threads,,并將init_task 結(jié)構(gòu)的RLIMIT_NPROC設(shè)置為max_threads的1/2。
20) 創(chuàng)建VFS,、VM,、高速緩沖等所需的多種塊緩沖器。
21) 如果IPC支持功能被編譯到內(nèi)核,,初始化IPC子系統(tǒng),,注意系統(tǒng)V的shm,它包含了掛載一個(gè)shmfs文件系統(tǒng)的內(nèi)部實(shí)例,。
22) 如果配額功能被編譯到內(nèi)核,,則為其創(chuàng)建并初始化一個(gè)特殊的塊緩沖。
23) 執(zhí)行bug檢查,,可能在任何時(shí)候激活processor/bus/etc 工作區(qū)下的bugs,,比較各種結(jié)構(gòu)以發(fā)現(xiàn)“ia64沒有bug”或者“ia32有相當(dāng)多的bug”。一個(gè)較好的例子就是“f00f bug”,,它僅在內(nèi)核編譯為686以前版本并再此環(huán)境下工作時(shí)產(chǎn)生,。
24) 設(shè)置一個(gè)標(biāo)記,標(biāo)識(shí)調(diào)度表在“next opportunity”時(shí)調(diào)用,,如果提供了“init=”引導(dǎo)命令,,則執(zhí)行execute_command函數(shù)以創(chuàng)建一個(gè)內(nèi)核線程init,否則嘗試執(zhí)行/sbin/init, /etc/init, /bin/init, /bin/sh, 如果所有的過程都失敗了,,則提示使用“init=”參數(shù),。
25) .進(jìn)入到idle循環(huán),,這是一個(gè)pid為0的idle進(jìn)程。
注意,,init內(nèi)核進(jìn)程調(diào)用了do_basic_setup 函數(shù),,這個(gè)函數(shù)依次調(diào)用do_initcalls函數(shù)遍歷由_initcall或者module_init宏注冊(cè)的函數(shù)列表,并調(diào)用他們,。這些函數(shù)不是相互倚賴,,就是他們的依賴關(guān)系已經(jīng)在Makefile文件里面固定了。這意味著依靠目錄樹的位置和Makefile的結(jié)構(gòu),,調(diào)用初始化函數(shù)的指令是可以更改的,。有時(shí),這很重要,。因?yàn)槟憧梢韵胂駜蓚€(gè)子系統(tǒng)A和B,,其中B依賴于A的初始化晚完成。如果A被靜態(tài)編譯,,并且B是一個(gè)模塊,,則在A準(zhǔn)備號(hào)所有必須環(huán)境后調(diào)用B實(shí)體的入口;如果A是一個(gè)模塊,,B也同樣必須是一個(gè)模塊,,這樣才沒有問題。但如果A和B都被靜態(tài)編譯到內(nèi)核時(shí)呢,?調(diào)用他們的指令依賴于內(nèi)核的.initcall.init節(jié)的相對(duì)入口偏移量。Rogier Wolff 提議引進(jìn)一種分等級(jí)(“優(yōu)先權(quán)”)的基礎(chǔ)下部組織,,模塊通過它讓連接器知道在何種關(guān)聯(lián)指令下這些模塊可以被鏈接,,但迄今為止都沒有一個(gè)內(nèi)核可接收的具備一流風(fēng)格的可用的實(shí)現(xiàn)補(bǔ)丁。因此,,確定你的連接指令是正確的,。如果像上面的例子,倘若在同一個(gè)Makefile中相繼列出A和B,,一旦在靜態(tài)編譯時(shí)他們能夠正常工作,,他們將會(huì)一直如此。如果他們不能工作,,更改列舉有他們目標(biāo)文件的指令,。
另外一件事是linux的通過“init=”引導(dǎo)命令的含義執(zhí)行多選擇性的初始化程序的能力。這個(gè)在意外覆蓋/sbin/init或者手工調(diào)試初始化腳本和/etc/inittab,,并在某時(shí)執(zhí)行其中一個(gè)時(shí)非常有用,。

1.7. SMP機(jī)在x86系統(tǒng)上引導(dǎo)
SMP機(jī)的引導(dǎo)過程在到達(dá)start_kernel入口前和普通順序一樣執(zhí)行bootsector,setup等等,,然后調(diào)用smp_init和smp_boot_cpus(位于src/i386/kernel/smpboot.c)函數(shù),。smp_boot_cpus函數(shù)依次遍歷每個(gè)apicid直到NR_CPUS數(shù),,并針對(duì)每個(gè)apicid調(diào)用do_boot_cpu函數(shù)。do_boot_cpu函數(shù)的功能就是為目標(biāo)CPU創(chuàng)建一個(gè)空閑任務(wù)并記錄到一個(gè)眾所周知的位置,,該位置由trampoline.S文件提供的Intel MP規(guī)范EIP代碼定義,;然后,它為該CPU產(chǎn)生讓引導(dǎo)程序執(zhí)行trampoline.S 代碼的STARTUP IPI,。
引導(dǎo)CPU在低位內(nèi)存為每個(gè)CPU創(chuàng)建一份trampoline代碼的拷貝,,這個(gè)啟動(dòng)程序在自身寫入一個(gè)可供引導(dǎo)程序判斷是否正在執(zhí)行trampoline代碼的隨機(jī)數(shù)。Intel MP 標(biāo)準(zhǔn)限定了trampoline代碼必須在低位內(nèi)存,。Trampoline代碼僅僅將%bx登記為1,,進(jìn)入到保護(hù)模式,并跳轉(zhuǎn)到arch/i386/kernel/head.S的主入口startup_32,。
現(xiàn)在,,引導(dǎo)程序開始執(zhí)行head.S并發(fā)覺這并不是引導(dǎo)過程,于是它跳過清楚BSS的代碼,,進(jìn)入到當(dāng)前CPU的空任務(wù)的入口函數(shù)initialize_secondary()并重新調(diào)用引導(dǎo)過程已經(jīng)初始化成功的init_tasks[cpu],。
注意,雖然init_task可以被共享但是每個(gè)空任務(wù)都有自己的時(shí)間分配系統(tǒng),,這就是為何init_tss[NR_CPUS]是一個(gè)隊(duì)列,。

1.8. 釋放初始化數(shù)據(jù)和代碼
當(dāng)操作系統(tǒng)完成對(duì)自身的初始化,大多數(shù)的代碼和數(shù)據(jù)結(jié)構(gòu)將不再需要,。多數(shù)的操作系統(tǒng)(BSD,FreeBSD等)不會(huì)釋放這些不需要的數(shù)據(jù),,因而浪費(fèi)了寶貴的物理內(nèi)核內(nèi)存。他們的理由是“相關(guān)代碼在多種子系統(tǒng)中都有關(guān)聯(lián),,所以釋放不是切實(shí)可行的”,。當(dāng)然,Linux系統(tǒng)不能以這些為托詞,,因?yàn)樵趌inux下,,如果某事在理論上是可行的,那么它多半已經(jīng)被實(shí)現(xiàn)了或者某人正在為之而努力,。
那么,,像我在前面所說的Linux內(nèi)核僅能被編譯成ELF二進(jìn)制格式,現(xiàn)在我們來找出這個(gè)原因(或者是其中之一的原因),。這個(gè)原因和linux提供的兩個(gè)用于清楚初始化代碼和數(shù)據(jù)的宏有關(guān)系:
初始化代碼的__init宏
初始化數(shù)據(jù)的__initdata宏
在include/linux/init.h中,,gcc特殊分類符定義了他們的含義。
#ifndef MODULE 
#define __init   __attribute__ ((__section__ (".text.init"))) 
#define __initdata  __attribute__ ((__section__ (".data.init"))) 
#else 
#define __init 
#define __initdata 
#endif 
這意味著如果代碼是靜態(tài)編譯到內(nèi)核的,,那么它就放在執(zhí)行的ELF節(jié).text 和.init,,這兩個(gè)節(jié)都在arch/i386/vmlinux.lds文件的連接關(guān)聯(lián)圖中都有定義;否則這兩個(gè)宏就沒有任何含義。
引導(dǎo)時(shí)將出現(xiàn) init內(nèi)核任務(wù)調(diào)用free_initmem函數(shù)釋放從地址__init_begin到地址__init_end的所有頁,。在一個(gè)典型系統(tǒng)上(如工作終端),,這個(gè)結(jié)果將釋放260k左右的內(nèi)存。
靜態(tài)編譯情況時(shí),,所有經(jīng)由module_init注冊(cè)的函數(shù)被放置到同樣會(huì)被釋放的.initcall.init這里,。當(dāng)設(shè)計(jì)一個(gè)子系統(tǒng)時(shí),當(dāng)今Linux的趨勢(shì)是從舊有版本提供一個(gè)init/exit入口點(diǎn),,這樣將來有爭(zhēng)議的子系統(tǒng)在需要時(shí)可以組件化,。以pipefs為例,見fs/pipe.c文件,。即使給定的子系統(tǒng)不會(huì)是一個(gè)模塊(如fs/buffer.c 中的bdflush),,通過module_init宏它依然正常精簡(jiǎn)地實(shí)現(xiàn)了初始化功能,并在函數(shù)正確調(diào)用時(shí)正常運(yùn)行,。
還有兩個(gè)宏__exit 和__exitdata也以類似的風(fēng)格工作,,但他們和模塊支持有更加直接的關(guān)聯(lián),因此在稍后將詳加討論,。

1.9. 處理內(nèi)核命令
讓我們回想在引導(dǎo)期間向內(nèi)核傳遞控制命令會(huì)發(fā)生什么:
1) LILO通過BIOS鍵盤服務(wù)接收命令,,保存到物理內(nèi)存明顯的位置,這就像發(fā)出一個(gè)信號(hào)說這里有一條可用的命令,。
2) arch/i386/kernel/head.S拷貝命令的前2k到內(nèi)存頁,。
3) arch/i386/kernel/setup.c文件的parse_mem_cmdline函數(shù)(start_kernel-> setup_arch-> parse_mem_cmdline)從內(nèi)存頁拷貝256字節(jié)到saved_command_line,這將顯示到/proc/cmdline,。如果出現(xiàn)“mem=”項(xiàng)也以同樣流程處理,,并為VM參數(shù)做適量調(diào)整。
4) 我們回到parse_options函數(shù)(由start_kernel調(diào)用),,它處理一些內(nèi)核參數(shù)(當(dāng)前的“init=”以及init所需的環(huán)境變量)并將每個(gè)命令提交到checksetup函數(shù),。
5) Checksetup函數(shù)在.setup.init節(jié)中搜索并調(diào)用對(duì)應(yīng)的函數(shù),傳遞命令,。注意經(jīng)由__setup注冊(cè)的函數(shù)返回值0的使用,,這有可能出現(xiàn)提交“變量=值”命令,,而不是一個(gè)函數(shù)在“值”時(shí)無效另一個(gè)無效,。Jeff Garzik評(píng)論說“這樣做的黑客在快馬加鞭?”為什么?因?yàn)檫@明顯就是ld舊有的細(xì)節(jié),。也就是說一個(gè)命令連接內(nèi)核時(shí)讓函數(shù)A先于B調(diào)用,,而另一個(gè)則是相反的命令,結(jié)果就依賴于這條命令,。
那么怎樣寫處理引導(dǎo)命令的程序呢,?我們使用定義在include/linux/init.h里的__setup()宏:
/* 
* Used for kernel command line parameter setup 
*/ 
struct kernel_param { 
const char *str; 

int (*setup_func)(char *); 
}; 
extern struct kernel_param __setup_start, __setup_end; 
#ifndef MODULE 
#define __setup(str, fn) \ 
static char __setup_str_##fn[] __initdata = str; \ 
static struct kernel_param __setup_##fn __initsetup = \ 
{ __setup_str_##fn, fn } 
#else 
#define __setup(str,func) /* nothing */ 
endif 
你可以在自己的代碼里仿效這個(gè)做法(下面是邏輯總線HBA驅(qū)動(dòng)的示例)。
static int __init 
BusLogic_Setup(char *str) 

int ints[3]; 
(void)get_options(str, ARRAY_SIZE(ints), ints); 
if (ints[0] != 0) { 
BusLogic_Error("BusLogic: Obsolete Command Line Entry " "Format Ignored\n", NULL); 
return 0; 

if (str == NULL || *str == '\0') 
return 0; 
return BusLogic_ParseDriverOptions(str); 
}
__setup("BusLogic=", BusLogic_Setup); 
注意__setup()不會(huì)對(duì)模塊進(jìn)行任何操作,所以這段代碼希望處理引導(dǎo)命令并在模塊初始化過程中,,模塊化連接或者靜態(tài)連接時(shí)都能調(diào)用自身的處理函數(shù),。這也同樣意味著可以編譯一個(gè)僅處理以模塊化編譯的內(nèi)核的參數(shù),而不處理靜態(tài)編譯或其他情況,。

[/list]

 xuediao 回復(fù)于:2005-06-07 16:35:09
關(guān)于linux 2.6.11內(nèi)核文件IO的系統(tǒng)調(diào)用實(shí)現(xiàn)的帖子:
1. [url=http://bbs./forum/viewtopic.php?t=544401]open & creat [/url] 描述了open函數(shù)和creat函數(shù)的實(shí)現(xiàn)
2. [url=http://bbs./forum/viewtopic.php?t=546490]read & write [/url] 描述了read 函數(shù)和write函數(shù)的實(shí)現(xiàn)
3. [url=http://bbs./forum/viewtopic.php?t=549408]flseek & close [/url] 描述了fleek函數(shù)和close函數(shù)的實(shí)現(xiàn)

 xuediao 回復(fù)于:2005-06-08 14:12:32
這是zImage內(nèi)核裝載器通用的內(nèi)存示意圖:

+----------------0A0000----------------+
|
|  BIOS預(yù)留空間,,不能使用, 
|  為BIOS EBDA保留空間.
|
+----------------09A000----------------+
|
|  棧/堆/命令行,,內(nèi)核實(shí)模式代碼使用.
|
+----------------098000----------------+
|
|  內(nèi)核 setup,,內(nèi)核實(shí)模式代碼使用.
|
+----------------090200----------------+
|
|  內(nèi)核bootsector,
|  內(nèi)核boot sector代碼使用.
|
+----------------090000----------------+
|
|  保護(hù)模式內(nèi)核,,
|  大小為內(nèi)核文件體積.
|
+----------------010000----------------+
|
|  引導(dǎo)裝載器,,
|  Bootsector 入口點(diǎn)0000:7C00
|
+----------------001000----------------+
|
|  MBR/BIOS預(yù)留空間
|
+----------------000800----------------+
|
|  MBR常用空間 
|
+----------------000600----------------+ 
|
|  BIOS使用空間  
|
+----------------000000----------------+

 xuediao 回復(fù)于:2005-06-13 09:31:51
前面的正文說明了引導(dǎo)過程,這里附錄一下引導(dǎo)過程所需的協(xié)議,。

當(dāng)前存在4個(gè) Linux/i386的引導(dǎo)協(xié)議:

舊版本內(nèi)核: 僅支持zImage/Image. 一些非常老的內(nèi)核甚至不會(huì)支持引導(dǎo)命令,。

2.00協(xié)議: (內(nèi)核 1.3.73)增加了對(duì)bzImage和initrd的支持, 并在引導(dǎo)裝載器和內(nèi)核之間提供了標(biāo)準(zhǔn)通信方式。雖然傳統(tǒng)的setup內(nèi)存區(qū)域依然假定是可寫的,,但 setup.S 在裝載時(shí)確是可重定位的,。

2.01協(xié)議: (內(nèi)核1.3.76) 增加了堆溢出警告。

2.02協(xié)議: (內(nèi)核2.4.0-test3-pre3) 新增命令行協(xié)議,。降低了常規(guī)內(nèi)存的上限,,并規(guī)定setup內(nèi)存區(qū)域不能被覆蓋,這使得使用EBDA從SMM或者32位BIOS入口引導(dǎo)系統(tǒng)更加安全,。 zImage同樣支持,,但不再重視。

2.03協(xié)議: (內(nèi)核 2.4.18-pre1) 明確定義引導(dǎo)裝載器中可用的initrd地址上限,。

 slackwaresz 回復(fù)于:2005-06-13 11:37:44
3q~

 xuediao 回復(fù)于:2005-06-14 09:42:19
關(guān)于內(nèi)核頭的內(nèi)容還有較多的詳細(xì)解說,,由于時(shí)間太緊,只能一天來點(diǎn),,各位見諒  :lol:

 xuediao 回復(fù)于:2005-06-14 09:42:19
實(shí)模式的內(nèi)核頭

   裝載內(nèi)核首先需要裝載實(shí)模式代碼(即是bootsector和setup代碼),,然后在0x01f1處檢查內(nèi)核頭。盡管裝載器可能僅選擇裝載前兩個(gè)代碼節(jié)(大約1k),,然而實(shí)模式代碼可以達(dá)到32k的長(zhǎng)度,。

這個(gè)內(nèi)核頭如下:

偏移量/長(zhǎng)度 支持協(xié)議 名字 含義


01F1/1 ALL setup_sects setup內(nèi)部節(jié)長(zhǎng)度,為了向后兼容,,如果值為0,,則代表真是值為4。
01F2/2 ALL root_flags 如果設(shè)置此標(biāo)識(shí),,則以只讀方式掛載root,。
01F4/2 ALL syssize 不能設(shè)置,僅供bootsect.S使用。
01F6/2 ALL swap_dev 不能設(shè)置,,已經(jīng)廢棄的項(xiàng),。
01F8/2 ALL ram_size 不能設(shè)置,僅供bootsect.S使用,。
01FA/2 ALL vid_mode 視頻模式選現(xiàn),。
01FC/2 ALL root_dev 默認(rèn)根設(shè)備號(hào)。
01FE/2 ALL boot_flag 0xAA55唯一號(hào),。
0200/2 2.00+ jump Jump指令,。
0202/4 2.00+ header 唯一簽名"HdrS"。
0206/2 2.00+ version 支持的引導(dǎo)協(xié)議版本號(hào),。
0208/4 2.00+ realmode_swtch 引導(dǎo)裝載器鉤子程序標(biāo)識(shí),。
020C/2 2.00+ start_sys 裝載的首段地址(0x1000) (已經(jīng)廢棄)。
020E/2 2.00+ kernel_version 內(nèi)核版本字符串指針,。
0210/1 2.00+ type_of_loader 引導(dǎo)裝載器標(biāo)識(shí),。
0211/1 2.00+ loadflags 引導(dǎo)協(xié)議選項(xiàng)。
0212/2 2.00+ setup_move_size 移動(dòng)到高位內(nèi)存的偏移量,。
0214/4 2.00+ code32_start 引導(dǎo)裝載器鉤子,。
0218/4 2.00+ ramdisk_image initrd裝載地址
021C/4 2.00+ ramdisk_size initrd長(zhǎng)度
0220/4 2.00+ bootsect_kludge 不能設(shè)置,僅供bootsect.S使用,。
0224/2 2.01+ heap_end_ptr setup后的空余內(nèi)存首指針,。
0226/2 N/A pad1 沒有使用。
0228/4 2.02+ cmd_line_ptr 內(nèi)核命令行32位指針,。
022C/4 2.03+ initrd_addr_max initrd地址上限

 amthe 回復(fù)于:2005-06-14 14:57:35
先頂在看,。

 xuediao 回復(fù)于:2005-06-15 14:20:22
type_of_loader特別說明:

type_of_loader:
如果引導(dǎo)裝載器被指定了一個(gè)id,則進(jìn)入到0xTV地址,,這里T代表引導(dǎo)裝載器的標(biāo)號(hào),,V代表版本號(hào)。否則,,跳轉(zhuǎn)到0xFF,。

id號(hào)對(duì)應(yīng)得引導(dǎo)裝載器類型列表:
0  LILO
1  Loadlin
2  bootsect-loader
3  SYSLINUX
4  EtherBoot
5  ELILO
7  GRuB
8  U-BOOT

 xuediao 回復(fù)于:2005-06-15 14:22:41
回帖的兄弟太少,不知道大伙有啥意見,,唉 BBS 啊

 st690714 回復(fù)于:2005-06-15 16:55:49
這么看太累,,最好有個(gè)連接能下載到文檔

 xuediao 回復(fù)于:2005-06-15 17:26:58
由于暫時(shí)找不到地方放這個(gè)文檔,所以暫時(shí)不能提供地方下載,。

 xuediao 回復(fù)于:2005-06-16 10:30:27
loadflags, heap_end_ptr:
如果協(xié)議版本是2.01或者更高,,則條裝到heap_end_ptr 地址,,設(shè)置0x80位裝載標(biāo)識(shí)CAN_USE_HEAP,。

  setup_move_size: 
如果采用2.00或者2.01協(xié)議,如果內(nèi)核沒有裝載到0x90000,則稍后將內(nèi)核移動(dòng)到該地址,。如果你需要在內(nèi)核中增加更多的東西,,則設(shè)置這個(gè)字段。

 xuediao 回復(fù)于:2005-06-20 10:53:52
cmd_line_ptr 特別說明
 

 cmd_line_ptr:
如果協(xié)議版本是2.02或者更高,,這就是一個(gè)32位的指向內(nèi)核命令行的指針地址.內(nèi)核命令行可能存放到setup后到0xA0000地址之間的任何位置,。當(dāng)引導(dǎo)裝載器不支持命令行時(shí),這個(gè)字段可指向一個(gè)空串,。如果這個(gè)字段為零,,則內(nèi)核會(huì)人定引導(dǎo)裝載器不支持2.02以上協(xié)議。

 popmouse 回復(fù)于:2005-06-21 10:41:07
有沒有更多的,?繼續(xù)?。。,?!

 xuediao 回復(fù)于:2005-06-22 11:10:47
ramdisk_max 說明:


 ramdisk_max:
initrd內(nèi)容可能占用得最大地址。對(duì)于引導(dǎo)協(xié)議2.02或者更早版本,,這個(gè)字段并沒有提供,,最大得地址為0x37FFFFFF。這個(gè)地址是安全可用的最高可用地址,,所以當(dāng)ramdisk數(shù)值表示了131072字節(jié)長(zhǎng),,那么這個(gè)字段就是0x37FFFFFF,這樣就可以在地址0x37FFFFFF開始ramdisk ,。

 lifengliu 回復(fù)于:2005-06-24 14:08:34
非常好,,一直都想看到這樣的內(nèi)核相關(guān)的文檔。 那位朋友提供一個(gè)地方下載吧,,這樣更能推廣

 xuediao 回復(fù)于:2005-06-29 09:47:58
關(guān)于第一章 引導(dǎo) 部分的內(nèi)容就基本發(fā)完了,,下面是第二章 進(jìn)程和中斷管理,請(qǐng)大家繼續(xù)支持,!

 SybaseLU 回復(fù)于:2005-06-29 18:05:12
很奇怪為什么這么多的人討論這些沒有實(shí)際有意義的東西,,而且一模一樣。如果這里放到嵌入式來講,,有點(diǎn)意義,。真正的對(duì)于不同體系結(jié)構(gòu)的CPU的bootloader并不需要中國(guó)人來寫,網(wǎng)上有許多針對(duì)不同的MCU的現(xiàn)成的bootloader,。不要隨便看到一片文章,,就放到這里討論,最好是自己去做,,去實(shí)踐,,把自己實(shí)際用到的東西和遇到的困難拿來討論,。提點(diǎn)意見。

 UNIX-COW 回復(fù)于:2005-06-29 22:52:51
[quote:54d15f9995="SybaseLU"]很奇怪為什么這么多的人討論這些沒有實(shí)際有意義的東西,,而且一模一樣,。如果這里放到嵌入式來講,有點(diǎn)意義,。真正的對(duì)于不同體系結(jié)構(gòu)的CPU的bootloader并不需要中國(guó)人來寫,,網(wǎng)上有許多針對(duì)不同的MCU的現(xiàn)成的bootloader..........[/quote:54d15f9995]


ni de kou qi hao da a  :em03:

 xuediao 回復(fù)于:2005-07-01 10:55:53
[quote:26ba1903b2="SybaseLU"]很奇怪為什么這么多的人討論這些沒有實(shí)際有意義的東西,而且一模一樣,。如果這里放到嵌入式來講,,有點(diǎn)意義。真正的對(duì)于不同體系結(jié)構(gòu)的CPU的bootloader并不需要中國(guó)人來寫,,網(wǎng)上有許多針對(duì)不同的MCU的現(xiàn)成的bootloader..........[/quote:26ba1903b2]

也許你是一個(gè)牛人,,不過坦白的講,我還不是,,我相信現(xiàn)在中國(guó)有很多程序員都不是,。發(fā)這樣的東西,僅是為了與大家探討學(xué)習(xí),,積累大家的經(jīng)驗(yàn)看法,,共同進(jìn)步,以便能夠從一個(gè)不牛的人快一點(diǎn)變成一個(gè)牛人,;發(fā)這些東西,,還有個(gè)想法,就是和大家共享這么一點(diǎn)微不足道的經(jīng)驗(yàn),,以使更多的于我后來之人無須從零開始,,少走彎路。

一花獨(dú)放不是春,,百花齊放春滿園,。如果你也有心,也和大家一道來分享,,我想會(huì)有學(xué)多獲益的人在心里感謝你的,。

另:對(duì)于一模一樣的描繪,我不敢茍同,。我的帖子,,都是我自己一字一句敲出來的。

 megavolt 回復(fù)于:2005-07-02 08:16:49
[quote:18a991104b="SybaseLU"]很奇怪為什么這么多的人討論這些沒有實(shí)際有意義的東西,,而且一模一樣,。如果這里放到嵌入式來講,有點(diǎn)意義,。真正的對(duì)于不同體系結(jié)構(gòu)的CPU的bootloader并不需要中國(guó)人來寫,,網(wǎng)上有許多針對(duì)不同的MCU的現(xiàn)成的bootloader..........[/quote:18a991104b]

竊以為了解學(xué)習(xí)一下還是很有好處的,,就像計(jì)算機(jī)的一些理論課程,看似無用其實(shí)很有用,,個(gè)中滋味各位看官應(yīng)該是深有體會(huì)的罷。

    本站是提供個(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

    來自: guitarhua > 《理學(xué)》

    0條評(píng)論

    發(fā)表

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

    類似文章 更多
    喜歡該文的人也喜歡 更多
    熱門閱讀 換一換