終于跳轉到main.c文件中的cbegin()函數執行了,這是從boot到loader再到內核,第一次執行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中保存的系統參數表
11 //包括顯示卡參數、硬盤參數等等
12 void *sys_param = (void *)0xf0000;
13 volatile int cursor_pos = 0;
14
15
16 //這個函數是真正的第一個C函數,從_start函數中跳入
17 //最好將cbegin函數用volatile關鍵字修飾,這樣做的好處
18 //是:用volatile修飾函數的話則是告訴gcc編譯器該函數
19 //不返回(可能是函數內含有exit()或者死循環之類的),這樣
20 //gcc在函數優化的時候就不會將返回值壓入堆棧,這樣,起到
21 //優化的作用,在cbegin中start_proc0()啟動了第一個init進程
22 //下面的for循環永遠不可能執行到,就算執行到那cbegin()也不會
23 //返回
24 void cbegin()
25 {
26 //初始化系統調用,包括將0x30號中斷與自陷框架sys_call函數掛鉤
27 //以及安裝對用系統調用號的中斷入口函數
28 init_sys_call();
29 //初始化proc_list進程鏈表以及對init和sys進程進行初始化
30 init_proc_list();
31 //初始化心跳值為0,以及打開時鐘中斷、初始化開機時間等
32 init_clock();
33 init_mm();
34
35 //這里調試了很久!?。。。。。。。?!
36 //注意這里一定要開中斷,因為當前中斷是關閉的
37 //init_hd()中有涉及到硬盤中斷的操作
38 //如果中斷關閉那么將響應不到硬盤中斷
39 sti();
40 init_hd();
41 cli();
42
43 //啟動第一個進程也即0號init進程
44 start_proc0();
45
46 //下面的代碼應該永遠不會被執行,因為start_proc0()函數不會返回,
47 //start_proc0()函數執行完iretd命令后便啟動了第一個init進程,
48 //自此系統便開始了多進程的運行
49 for(;;){}
50 }
51
52 //proc0
53 //第一個進程
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 //第二個進程
73 void sys()
74 {
75 int i;
76 //這里由sys進程調用partition()系統調用來完成硬盤的初始化
77 //之所以在不在內核中完成硬盤的初始化是因為這里需要讀取硬盤
78 //MBR的內容,而讀取硬盤需要將當前進程睡眠,而內核是不允許
79 //睡眠的,因此選擇在sys進程中初始化硬盤
80 //這里的工作包括獲取硬盤柱面、柱頭、磁道、每磁道扇區數等的
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
代碼量并不多,但是完成的事情其實并不少,主要的函數以及功能如下:
1、init_sys_call 初始化系統調用
2、init_proc_list 初始化進程控制塊數組
3、init_clock 初始化時鐘中斷
4、init_mm 初始化告訴緩沖區
5、init_hd 初始化硬盤及硬盤中斷
6、start_proc0 啟動第一個進程init進程
其中每一項功能的實現都不簡單,之后的章節再一一詳細論述。
這里有必要將內存分配的情況展示一下:
在boot執行過程中:
1、首先BIOS將自動從軟盤第一扇區讀取boot代碼(共512B),然后將其加載到內存物理地址0x7c00處,開始執行第一條代碼,boot開始執行。
2、我們的boot程序完成從軟盤的第二個扇區開始將loader和kernel Image加載到物理地址0x80000開始的地方,然后便急切的跳轉到0x80000地址處去執行loader。
3、loader此時開始執行,它首先分析kernel Image,將其各個段(包括若干代碼段、數據段等)復制到內存合適的地址處(我們編譯內核的時候指定了內核起始虛擬地址為0x0,由于我們的分段機制使得虛擬地址等于線性地址,所以內核開始執行時的線性地址也是0x0,又由于此時分頁機制是地址對等映射,所以物理地址也同樣是0x0,所以我們是將內核搬運到內存起始物理地址0x0開始運行的);然后從BIOS ROM區獲取一些系統參數,將參數保存在物理地址0xf0000開始的地方;之后準備GDT,并填充適當的GDT描述符項,然后通過打開A20地址線和置cr0寄存器最后一位PE位為1來打開保護模式,然后跳轉到保護模式運行;而進入保護模式之后loader什么也不干,畢竟我們想盡快的到內核世界去旅行,所以這里只簡單的跳轉到kernel去執行。
至此為止,boot運行完成時的內存分布如下:

而當loader執行完畢后的內存分布如下:

之后內存分布不會有太大的變化。
posted on 2012-02-14 13:01
myjfm 閱讀(601)
評論(0) 編輯 收藏 引用 所屬分類:
操作系統