現(xiàn)在我們就可以深入研究一下自舉過(guò)程中不可缺少的匯編語(yǔ)言函數(shù)了。
Setup()函數(shù)可以在/usr/src/linux-2.4.2/arch/i386/boot/setup.S文件中找到。
Setup()函數(shù)代碼是在完整的內(nèi)核自舉程序加載以后,才會(huì)跳到相應(yīng)的函數(shù)代碼處。在內(nèi)核文件中,其偏移地址是0x200。這使得自舉程序很容易找到這段代碼,并將其拷貝到起始物理地址為0x00090200的內(nèi)存中。
這個(gè)Setup()文函數(shù)到底是做什么用的?在計(jì)算機(jī)時(shí)里,內(nèi)核要正確地操作所有硬件就必需首先要檢測(cè)到它們,并且以一種有序的方式進(jìn)行初始化。Setup()函數(shù)初始化所有的硬件設(shè)備,從而為內(nèi)核操作它創(chuàng)造了一個(gè)環(huán)境。
但是,前面我們不是已經(jīng)提到過(guò)BIOS會(huì)檢測(cè)所有的硬件嗎?雖然BIOS初始化了所有的硬件,但是Linux內(nèi)核并不放心,它還要以自己的方式對(duì)所有的硬件進(jìn)行初始化。Linux內(nèi)核之所以要設(shè)計(jì)成這樣,是為了增強(qiáng)可移植性和穩(wěn)定性。這也是Linux內(nèi)核要優(yōu)于很多目前可用的Unix和類Unix內(nèi)核的原因之一,并且也使得它在很多方面表現(xiàn)的非常出眾。
Setup()函數(shù)主要完成以下任務(wù):
(1)首先是檢測(cè)系統(tǒng)可用內(nèi)存的總量,它是通過(guò)BIOS程序來(lái)完成檢測(cè)的;
(2)設(shè)置鍵盤重復(fù)延遲時(shí)間和重復(fù)速度;
(3)檢測(cè)視頻卡;
(4)重新初始化硬盤控制器和硬盤參數(shù);
(5)檢測(cè)一個(gè)MCA;
(6)檢測(cè)一個(gè)PS/2定點(diǎn)設(shè)備(鼠標(biāo)總線);
(7)檢測(cè)高級(jí)電源管理器(APM)BIOS支持;
(8)檢測(cè)內(nèi)核在內(nèi)存中的位置,如果在低地址0x00010000,就將其移到高地址0x00001000,如在高地址則不做任何移動(dòng);
(9)設(shè)置設(shè)備中斷描述表(IDT)和全局描述表(GDT);
(10)如已經(jīng)有了浮點(diǎn)單位(FPU),則重置之;
(11)重新調(diào)用程序中斷控制器;
(12)通過(guò)設(shè)置cr0狀態(tài)寄存器的PE位,把CPU從“實(shí)模式”切換到“保護(hù)模式”;
(13)跳轉(zhuǎn)到stratup_32( )匯編語(yǔ)言函數(shù)。
第一個(gè)stratup_32( )函數(shù)做什么
在啟動(dòng)過(guò)程中要用到兩個(gè)stratup_32( )函數(shù),雖然它們都是匯編語(yǔ)言函數(shù),但是卻是兩個(gè)完全不同的函數(shù)。我們這里所說(shuō)的函數(shù)包含在/usr/src/linux-2.4.2/arch/i386/boot/compressed/head.S文件里。
Setup()文件執(zhí)行后,這個(gè)函數(shù)就被加載到物理地址為0x00100000或者物理地址為0x00001000的內(nèi)存中(取決于內(nèi)核是載入高或者低內(nèi)存)。
當(dāng)執(zhí)行這個(gè)函數(shù)時(shí),會(huì)執(zhí)行以下的操作:
(1)初始化段寄存器和一個(gè)臨時(shí)棧。
(2)內(nèi)核中沒(méi)有初始化氖?荻加?填充。它是通過(guò)symbols _edata和 _end來(lái)識(shí)別的。
(3)執(zhí)行decompress_kernel( )函數(shù)。這個(gè)函數(shù)用于對(duì)Linux內(nèi)核解壓縮。這個(gè)時(shí)候,屏幕上將顯示“Uncompressing Linux……”信息。解壓縮完成后,就會(huì)顯示“OK, booting the kernel”信息。現(xiàn)在有一個(gè)問(wèn)題,就是解完壓縮的內(nèi)核被放置在什么位置?答案是如果Linux內(nèi)核被加載低地址,那么解壓縮的內(nèi)核將被置于物理地址為0x00100000的地方。如果在高地址,則內(nèi)核會(huì)被先解壓到一個(gè)臨時(shí)緩沖區(qū)中,待完成后再將其加載到物理地址為0x00100000的地方。
(4)最后,跳轉(zhuǎn)到物理地址為0x00100000的地方執(zhí)行。
到此為止,代碼執(zhí)行操作就由另外一個(gè)startup_32( )函數(shù)來(lái)接管。也就是說(shuō),第二個(gè)startup_32( )函數(shù)接管了啟動(dòng)過(guò)程。