在這篇文章中,我們將從零開始,動(dòng)手編寫一個(gè)可以用GRUB來引導(dǎo)的簡單x86內(nèi)核,該內(nèi)核會(huì)在屏幕上打印一條信息,然后——掛起!
one-does-not-kernel
一個(gè)人寫一個(gè)內(nèi)核是一件簡單的事情
X86機(jī)器是怎樣啟動(dòng)的?
在我們思考怎樣寫一個(gè)內(nèi)核之前,讓我們先看一下x86機(jī)器從啟動(dòng)到把控制權(quán)交給內(nèi)核的過程是怎樣的:
x86CPU在機(jī)器啟動(dòng)之后就會(huì)從地址[0xFFFFFFF0]處開始執(zhí)行,這個(gè)地址就是在32位尋址空間中的最后16個(gè)字節(jié)處,這里存放了一條跳轉(zhuǎn)指令,會(huì)跳轉(zhuǎn)到內(nèi)存中BIOS代碼起始處。
接著,cpu就開始開始執(zhí)行BIOS代碼塊了,BIOS首先會(huì)在我們配置好的啟動(dòng)設(shè)備序列中,通過檢查一個(gè)特定的魔數(shù),找到第一個(gè)可以引導(dǎo)的設(shè)備。
一旦BIOS找到一個(gè)可以引導(dǎo)的設(shè)備后,它就會(huì)把該設(shè)備第一個(gè)扇區(qū)的代碼復(fù)制到物理內(nèi)存的[0x7c00]的位置,然后跳轉(zhuǎn)到這個(gè)地址開始執(zhí)行這一段代碼,我們習(xí)慣把這一段代碼叫作bootloader。
Bootloader會(huì)將內(nèi)核代碼加載到物理內(nèi)存[0x100000]的位置,[0x100000]這個(gè)地址是所有x86機(jī)器宏內(nèi)核代碼的起始地址。
我們需要哪一些工具?
* 一個(gè)x86構(gòu)架的計(jì)算機(jī)
*GCC
*LD(GNU連接器)
*GRUB
源碼
源代碼可以在我的Github上找到:Githubrepository-mkernel
用匯編代碼來編寫內(nèi)核入口
我們喜歡用c來做所有的事情,但是我們無可避免地需要用到一點(diǎn)兒匯編,我們將會(huì)寫一小段x86的匯編代碼來作為內(nèi)核入口,這一段匯編代碼會(huì)在調(diào)用我們的c代碼后停止整個(gè)程序流程。
我們怎樣確認(rèn)匯編代碼會(huì)作為內(nèi)核的起始點(diǎn)呢?
我們將用一個(gè)連接器腳本將這些目標(biāo)文件鏈接成我們最終的內(nèi)核程序(稍后解釋更多),在連接器腳本里,我們指定了這段二進(jìn)制代碼會(huì)被加載到內(nèi)存[0x100000]處。這個(gè)地址就是我之前說過的,內(nèi)核所希望的起始地址。
匯編代碼如下:
1. ;;kernel.asm2. bits 32 ;nasm directive -32 bit3. section .text4.5. global start6. extern kmain ;kmain isdefinedin the c file7.8. start:9. cli ;block interrupts10. call kmain11. hlt ;halt the CPU
第一行指令bit32不是x86匯編指令,它是一條NASM指令,指定nasm匯編器產(chǎn)生32位的程序,這條語句并不是必不可少的,但加上它是一個(gè)好的編程習(xí)慣。
第二行是text段(代碼段)的開始,在這里存放著我們的代碼塊。
global是另外一個(gè)NASM指令,用將一個(gè)符號(hào)設(shè)置為全局符號(hào)。這樣做連接器才會(huì)知道符號(hào)start在哪兒開始,start是我們程序的入口地址。
kmain是我們定義在kernel.c文件中的函數(shù),extern關(guān)鍵字聲明了該函數(shù)定義在別的文件中。
到這里,我們的函數(shù)start調(diào)用kmian函數(shù)之后就會(huì)使用hlt指令將CPU掛起,中斷會(huì)cpu從hlt指令中喚醒,我們要在掛起之前用cli指令來關(guān)閉系統(tǒng)的中斷響應(yīng),cli指令是清除中斷(clear-interrupts)的縮寫。
用C實(shí)現(xiàn)的內(nèi)核
在kernle.asm中,我們調(diào)用了kmain()函數(shù),所以我們的c代碼將會(huì)在kmain()中開始運(yùn)行:
1. /*2.* kernel.c3.*/4.void kmain(void)5.{6. char*str ="my first kernel";7. char*vidptr =(char*)0xb8000; //video mem begins here.8. unsignedint i =0;9. unsignedint j =0;10. //clear all11. while(j <80*25*2){12.??????? //blank character13.???????????????? ????????????vidptr[j]=' ';14.??//attribute-byte: light grey on black screen???????15.???????????????? ??????????????vidptr[j+1]=0x07; ?????? ???????????16.???????????????? ??????????????? j = j +2;17.???????????????? ???? }18.???????????????? ???? j =0;19.?????????????? ? while(str[j]!=' 北京百家乐网上投注| 百家乐官网压钱技巧| 百家乐牌九| 百家乐官网打法分析| 载大发888软件| 墨尔本百家乐的玩法技巧和规则| 百家乐玩法守则| 金花百家乐官网的玩法技巧和规则 | 盈禾娱乐场| 大发888更名网址62| 太阳城二手房| 澳门百家乐官方网站| 任你博百家乐的玩法技巧和规则| 网络百家乐怎样出千| 百家乐官网官网站| 真人百家乐官网是真的吗| 百家乐官网赌场信息| 百家乐官网赌博现金网| 太子百家乐的玩法技巧和规则| 威斯汀百家乐的玩法技巧和规则| 香港百家乐玩| 娱乐百家乐下载| 百家乐统计工具| 百家乐14克粘土筹码| 大发888游戏免费下载| 世界德州扑克大赛| 钻石国际娱乐| 隆德县| 百家乐官网是怎样的| 百家乐官网真人游戏开户| 百家乐官网做庄家必赢诀窍| 杭州百家乐官网西园| 百家乐打庄技巧| 金盾百家乐网址| 百家乐娱乐优惠| 威尼斯人娱乐信誉| 365足球备用| 波音百家乐官网游戏| 玩百家乐官网678娱乐城| 无锡百家乐官网的玩法技巧和规则| 做生意房门挂啥招财|