终于跳转到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 阅读(578)
评论(0) 编辑 收藏 引用 所属分类:
操作系统