學(xué)過C語言的人都知道,用C語言設(shè)計(jì)的程序都有一個(gè)main函數(shù),而且是從main函數(shù)開始執(zhí)行的。linux0.11的代碼是用C語言編寫的。奇怪的是,為什么在操作系統(tǒng)啟動(dòng)時(shí)先執(zhí)行的是三個(gè)由匯編語言寫成的程序,然后才開始執(zhí)行main函數(shù);為什么不是像我們熟知的C語言程序那樣,從main函數(shù)開始執(zhí)行呢?
通常,我們用C語言編寫的程序都是用戶應(yīng)用程序。這類程序的執(zhí)行有一個(gè)很重要的特征,就是必須在操作系統(tǒng)的平臺(tái)上執(zhí)行,也就是說,要由操作系統(tǒng)為應(yīng)用程序創(chuàng)建進(jìn)程,并把應(yīng)用程序的可執(zhí)行代碼從硬盤加載到內(nèi)存。現(xiàn)在我們討論的是操作系統(tǒng),不是普通的應(yīng)用程序,這樣就出現(xiàn)了一個(gè)問題:應(yīng)用程序是由操作系統(tǒng)加載的,操作系統(tǒng)該由誰加載呢?
從前面的只是中我們知道,加載操作系統(tǒng)的時(shí)候,計(jì)算機(jī)剛剛加電,只有BIOS程序在運(yùn)行,而且此時(shí)的計(jì)算機(jī)處在16位實(shí)模式狀態(tài),通過BIOS程序自身的代碼形成的16位的中斷向量表及相關(guān)的16位的中斷服務(wù)程序,將操作系統(tǒng)在軟盤上的第一扇區(qū)(512字節(jié))的代碼加載到內(nèi)存,BIOS能主動(dòng)操作的內(nèi)容也就到此為止了。準(zhǔn)確的說,這是一個(gè)約定。對(duì)于第一扇區(qū)代碼的加載,不論是什么操作系統(tǒng)都是一樣的;從第二扇區(qū)開始,就要由第一扇區(qū)中的代碼來完成后續(xù)的代碼加載工作。
當(dāng)加載工作完成后,好像仍然沒有立即執(zhí)行main函數(shù),而是打開A20,打開pe、pg,建立IDT、GDT......然后才開始執(zhí)行main函數(shù),這是什么道理?
原因是,Linux0.11是一個(gè)32位的實(shí)時(shí)多任務(wù)的現(xiàn)代操作系統(tǒng),main函數(shù)肯定要執(zhí)行的事32位的代碼。編譯操作系統(tǒng)代碼時(shí),是由16位和32位不同的編譯選項(xiàng)的。如果選擇了16位,C語言編譯出來的代碼是16位模式的,結(jié)果可能是一個(gè)int型變量,只有2字節(jié),而不是32位的4字節(jié)......這不是Linux0.11想要的。Linux0.11要的是32位的編譯結(jié)果。只有這樣才能成為32位的操作系統(tǒng)代碼。這樣的代碼才能用到32位總線(打開A20后的總線),才能用到保護(hù)模式和分頁,才能成為32位的實(shí)時(shí)多任務(wù)的現(xiàn)代操作系統(tǒng)。
開機(jī)時(shí)的16位實(shí)模式與main函數(shù)執(zhí)行需要的32位保護(hù)模式之間有很大的差距,這個(gè)差距誰來填補(bǔ)?head.s做的就是這項(xiàng)工作。這期間,head程序打開A20,打開pe、pg。廢棄舊的、16位的中斷響應(yīng)機(jī)制,建立新的32位的IDT......這些工作都做完了,計(jì)算機(jī)已經(jīng)處在32位的保護(hù)模式狀態(tài)了,調(diào)用32位main函數(shù)的一切條件已經(jīng)準(zhǔn)備完畢,這時(shí)順理成章地調(diào)用main函數(shù)。后面的操作系統(tǒng)就可以用32位編譯的main函數(shù)完成。