終于跳轉(zhuǎn)到main.c文件中的cbegin()函數(shù)執(zhí)行了,這是從boot到loader再到內(nèi)核,第一次執(zhí)行c代碼的地方。我們先看main.c都做了什么。
1 #include <type.h>
2 #include <asm/system.h>
3 #include <winixj/clock.h>
4 #include <winixj/mm.h>
5 #include <winixj/hdd.h>
6 #include <winixj/sys_call.h>
7 #include <winixj/process.h>
8 #include <getpid.h>
9
10 //指向在loader.s中保存的系統(tǒng)參數(shù)表
11 //包括顯示卡參數(shù)、硬盤參數(shù)等等
12 void *sys_param = (void *)0xf0000;
13 volatile int cursor_pos = 0;
14
15
16 //這個(gè)函數(shù)是真正的第一個(gè)C函數(shù),從_start函數(shù)中跳入
17 //最好將cbegin函數(shù)用volatile關(guān)鍵字修飾,這樣做的好處
18 //是:用volatile修飾函數(shù)的話則是告訴gcc編譯器該函數(shù)
19 //不返回(可能是函數(shù)內(nèi)含有exit()或者死循環(huán)之類的),這樣
20 //gcc在函數(shù)優(yōu)化的時(shí)候就不會(huì)將返回值壓入堆棧,這樣,起到
21 //優(yōu)化的作用,在cbegin中start_proc0()啟動(dòng)了第一個(gè)init進(jìn)程
22 //下面的for循環(huán)永遠(yuǎn)不可能執(zhí)行到,就算執(zhí)行到那cbegin()也不會(huì)
23 //返回
24 void cbegin()
25 {
26 //初始化系統(tǒng)調(diào)用,包括將0x30號(hào)中斷與自陷框架sys_call函數(shù)掛鉤
27 //以及安裝對(duì)用系統(tǒng)調(diào)用號(hào)的中斷入口函數(shù)
28 init_sys_call();
29 //初始化proc_list進(jìn)程鏈表以及對(duì)init和sys進(jìn)程進(jìn)行初始化
30 init_proc_list();
31 //初始化心跳值為0,以及打開時(shí)鐘中斷、初始化開機(jī)時(shí)間等
32 init_clock();
33 init_mm();
34
35 //這里調(diào)試了很久!!!!!!!!!!
36 //注意這里一定要開中斷,因?yàn)楫?dāng)前中斷是關(guān)閉的
37 //init_hd()中有涉及到硬盤中斷的操作
38 //如果中斷關(guān)閉那么將響應(yīng)不到硬盤中斷
39 sti();
40 init_hd();
41 cli();
42
43 //啟動(dòng)第一個(gè)進(jìn)程也即0號(hào)init進(jìn)程
44 start_proc0();
45
46 //下面的代碼應(yīng)該永遠(yuǎn)不會(huì)被執(zhí)行,因?yàn)閟tart_proc0()函數(shù)不會(huì)返回,
47 //start_proc0()函數(shù)執(zhí)行完iretd命令后便啟動(dòng)了第一個(gè)init進(jìn)程,
48 //自此系統(tǒng)便開始了多進(jìn)程的運(yùn)行
49 for(;;){}
50 }
51
52 //proc0
53 //第一個(gè)進(jìn)程
54 void init()
55 {
56 int i;
57 uint8 *p = (uint8 *)(0xb8000);
58 *p = (uint8)getpid() + '0';
59
60 for(;;)
61 {
62 for ( i = 0; i < 1000000; ++i);
63
64 if (*p < '9')
65 {
66 *p = ++(*p);
67 }
68 };
69 }
70
71 //proc1
72 //第二個(gè)進(jìn)程
73 void sys()
74 {
75 int i;
76 //這里由sys進(jìn)程調(diào)用partition()系統(tǒng)調(diào)用來完成硬盤的初始化
77 //之所以在不在內(nèi)核中完成硬盤的初始化是因?yàn)檫@里需要讀取硬盤
78 //MBR的內(nèi)容,而讀取硬盤需要將當(dāng)前進(jìn)程睡眠,而內(nèi)核是不允許
79 //睡眠的,因此選擇在sys進(jìn)程中初始化硬盤
80 //這里的工作包括獲取硬盤柱面、柱頭、磁道、每磁道扇區(qū)數(shù)等的
81 //信息
82 uint8 *p = (uint8 *)(0xb8002);
83 *p = (uint8)getpid() + '0';
84
85 for(;;)
86 {
87 for ( i = 0; i < 1000000; ++i);
88
89 if (*p < '9')
90 {
91 *p = ++(*p);
92 }
93 };
94 }
95
代碼量并不多,但是完成的事情其實(shí)并不少,主要的函數(shù)以及功能如下:
1、init_sys_call 初始化系統(tǒng)調(diào)用
2、init_proc_list 初始化進(jìn)程控制塊數(shù)組
3、init_clock 初始化時(shí)鐘中斷
4、init_mm 初始化告訴緩沖區(qū)
5、init_hd 初始化硬盤及硬盤中斷
6、start_proc0 啟動(dòng)第一個(gè)進(jìn)程init進(jìn)程
其中每一項(xiàng)功能的實(shí)現(xiàn)都不簡(jiǎn)單,之后的章節(jié)再一一詳細(xì)論述。
這里有必要將內(nèi)存分配的情況展示一下:
在boot執(zhí)行過程中:
1、首先BIOS將自動(dòng)從軟盤第一扇區(qū)讀取boot代碼(共512B),然后將其加載到內(nèi)存物理地址0x7c00處,開始執(zhí)行第一條代碼,boot開始執(zhí)行。
2、我們的boot程序完成從軟盤的第二個(gè)扇區(qū)開始將loader和kernel Image加載到物理地址0x80000開始的地方,然后便急切的跳轉(zhuǎn)到0x80000地址處去執(zhí)行l(wèi)oader。
3、loader此時(shí)開始執(zhí)行,它首先分析kernel Image,將其各個(gè)段(包括若干代碼段、數(shù)據(jù)段等)復(fù)制到內(nèi)存合適的地址處(我們編譯內(nèi)核的時(shí)候指定了內(nèi)核起始虛擬地址為0x0,由于我們的分段機(jī)制使得虛擬地址等于線性地址,所以內(nèi)核開始執(zhí)行時(shí)的線性地址也是0x0,又由于此時(shí)分頁(yè)機(jī)制是地址對(duì)等映射,所以物理地址也同樣是0x0,所以我們是將內(nèi)核搬運(yùn)到內(nèi)存起始物理地址0x0開始運(yùn)行的);然后從BIOS ROM區(qū)獲取一些系統(tǒng)參數(shù),將參數(shù)保存在物理地址0xf0000開始的地方;之后準(zhǔn)備GDT,并填充適當(dāng)?shù)腉DT描述符項(xiàng),然后通過打開A20地址線和置cr0寄存器最后一位PE位為1來打開保護(hù)模式,然后跳轉(zhuǎn)到保護(hù)模式運(yùn)行;而進(jìn)入保護(hù)模式之后loader什么也不干,畢竟我們想盡快的到內(nèi)核世界去旅行,所以這里只簡(jiǎn)單的跳轉(zhuǎn)到kernel去執(zhí)行。
至此為止,boot運(yùn)行完成時(shí)的內(nèi)存分布如下:
存映像1.JPG)
而當(dāng)loader執(zhí)行完畢后的內(nèi)存分布如下:
存映像2.JPG)
之后內(nèi)存分布不會(huì)有太大的變化。
posted on 2012-02-14 13:01
myjfm 閱讀(620)
評(píng)論(0) 編輯 收藏 引用 所屬分類:
操作系統(tǒng)