在前面的启动流程分析中已经看到,boot monitor将控制权交给了minix3的内核,从内核的entry处开始
执行,本文就是继续这个流程。
内核中最先执行的代码是与体系结构相关的汇编代码,在386上,这部分代码包含在arch/i386/mpx.S中。
在这个文件中,定义了标签MINIX,这就是内核的入口点。我们看到,该标签后的第一条指令是jmp,它会跳过
后续的一些数据(这些数据就是给boot monitor用的,放在这里只是为了寻址方便)。
这部分汇编代码主要做的工作包括:
* 设置内核栈
为后面要调用的C代码(就是kernel任务的代码)设置好栈帧。boot monitor已经设置好了cs、ds,但是
并没有设置好栈,此时的栈仍然是monitor使用的16位的栈,ss也还指向monitor的数据段。
1. 判断boot monitor是否设置好了minix退出时的返回地址,如果设置好了,将C的全局变量mon_return
增加1(设置为真)。
2. 将此时,也就是boot monitor的栈指针值保存在C全局变量mon_sp中;
3. 将monitor的GDT表拷贝到内核空间的gdt表头部,后续内核保护模式初始化的prot_init()会使用。
4. 将gdtr切换到内核的GDT表,此时,ds设置为内核的数据段。
5. 保存monitor通过栈传递过来的参数:
a. 将monitor保存的image中进程头信息数组的地址放在aout中;
b. 内核的数据段的值设置给es, fs, gs, ss寄存器。
c. 设置esp,此时,才真正设置好了C的执行栈。
d. 将内核启动参数的地址、偏移量保存在C变量params_size,params_offset中,将monitor的数据
段基值保存在mon_ds中。
6. 调用C的初始化代码cstart(cs, ds, mds, params_offset, params_len)继续初始化环境;
cstart()函数定义在kernel/start.c文件中,它用于在调用main()之前完成一些系统初始化,例如
初始化kinfo中的信息,将启动参数拷贝到内核空间的缓冲区等,这些主要是把一些信息记录在全局
的C变量中,方便后续的C代码访问。
但是,该函数最重要的工作是初始化两个表:GDT和IDT,前者是i386保护模式的核心数据结构,后者
是中断处理的核心数据结构。
7. 重新加载GDT和IDT,并使用ljmp指令迫使新加载的这两个表其作用;
8. 使用DS_SELECTOR初始化所有的数据、堆栈段寄存器,不再使用TSS;
* 调用main()
如你所见,这确实是Minix3的真正的入口,定义在文件kernel/main.c中。
该汇编代码文件的后面的部分定义了32位保护模式下的硬件中断处理入口、IPC入口、内核调用入口
、异常(软中断)处理入口,启动/重启一个进程的代码。这是些工具性质的汇编代码。
汇编代码的最后是一些数据空间,此处就是预留的内核栈空间。
阅读(2110) | 评论(0) | 转发(0) |