目录
系统总体流程:
系统从boot开始动作,把内核从启动盘装到正确的位置,进行一些基本的初始化,如检测内存,保护模式相关,建立页目录和内存页表,GDT表,IDT表。然后进入main进行初始化设置,main完成系统各个模块要用到的所有数据结构和外部设备的初始化。使得系统可以正常的工作。然后才进入用户模式。执行第一个fork生成进程1执行init,运行shell,接受并执行用户命令.
这里整个系统建立起来了,OS就处于被动状态,靠中断和系统调用来完成每一项服务。
各个目录的阅读总结:
(一) boot
1.bootsect.s :
bootsect.s编译结果生成一个512BYTE(一个扇区)镜像。这个扇区的最后一个字是0xAA55,倒数第二个字是root_dev变量,值为ROOT_DEV(306),即根文件系统所在的设备号。这段代码必须写入到启动盘的启动扇区,也就第一个物理扇区上。这样机器启动后,BIOS自动把它加载到7C00H处并跳到那里开始执行。bootsect将自己移动到90000H(576K)处,并跳至那里相应位置执行。然后利用BIOS中断将setup直接加载到自己的后面(90200h)(576.5K处),并将system加载到地址10000h处。 跳到setup中执行。
2.setup.s:
利用BIOS中断把系统参数如显卡,硬盘参数保存到内存90000开始的位置,即覆盖原bootsect所在的内存位置。再把整个system 模块移动到00000 位置。加载GDTR和IDTR,这里的GDT表是临时的,保存了两个描述符,即内核代码、内核数据段描述符,其段基地址为0。而加载IDT除了进入保护模式需要加载IDTR之外,没有任何意义。开启A20 地址线开启扩展内存。重新设置8259中断码0x20~0x2f。进入保护模式(PE置1)跳转到system模块中的head.s中(0处)执行。Bootsect.s和setup.s执行时内存变化情况。
3.head.s:
前4KB的代码将被页目录覆盖掉。这些代码执行的操作包括:设置系统堆栈为_stack_start。重新设置GDTR和IDTR。gdt,idt表都定义在head.s的末端,长度均为256项(2KBYTE)。第2个页面到第5个页面是系统的4张页表。最后一个页表后面的代码执行分页操作,即填充的4个页目录和4张页表的内容,实现对等映射,即物理地址=线性地址。每个页表项属性为存在并且用户可读写。设置好后置CR3页目录地址即0。启动分页标志,CR0的PG标志置1。跳到main函数中执行。
跳到main之前,内存布局如下:从0到16M
页目录4K(0x0开始)
页表1 4K
页表2 4K
页表3 4K
页表4 4K
软盘缓冲区1K
head.s后半部分代码
IDT表2K
GDT表2K
main.o代码部分
内核其余部分(大约到512K,end值为结束地址)
setup保存的系统参数(90000H~900200)这个区间还保存着root_dev.
BIOS(640K-1M)
主内存区(1M-16M)
现在初始化好了内核工作依赖的主要的数据结构是GDT和IDT表,还有页表。
(二)内核初始化init
main.c将进行进一步初始化工作。主要方面:分配主内存功能,IDT表各中断描述符重新设定,对内核其它模块如mm,fs进行初始化,然后移到用户模式下生成进程1执行init,常驻进程0死循环执行pause。进程init加载根文件系统,设置终端标准IO,创建进程2以/etc/rc为标准输入文件执行shell.完成rc文件中的命令。
init等进程2退出,进入死循环:创建子进程,建立新会话,设置标准IO终端,以登录方式执行shell.
至此系统动作起来了。
所以整个系统的建立起来后除了两个死循环的进程idle和init,其它的动作都是由用户在shell下执行命令,产生系统调用来工作的。
通过执行move_to_usermdoe(),idle和init进程都属于用户态下的进程。而内核则完全是中断驱动的。也就是说只有通过中断才能进入系统,如时钟和系统调用等。
所以问题的重点就在于内核各部分数据结构的建立、初始化、操作是怎样进行的。这些初始化流程涉及到内核各个模块全部重要的数据结构。
现在从main执行的一系列初始化代码来浅窥一下:
1.根据内存的大小,设置高速缓冲的末端。16M内存把高速缓冲末端设为4M。缓冲末端到主存末端为主内存区。
2.mem_init(main_memory_start,memory_end);主内存区初始化
设置高端内存HIGH_MEMORY=memory_end,
设置内存映射字节图mem_map [ PAGING_PAGES ],将不可用的全部置为USED,可用的置为0。mem_map数组是系统mm模块核心数据结构,记载了每个内存页使用计数。
3.trap_init().硬件中断向量表设置。
向IDT中填充各个中断描述符,使其指向对应的中断处理程序。对于错误,基本是结束当前进程。其他如外设中断都是各个模块初始化的时候向IDT表中相应项进行设置。
4.blk_dev_init(); // 块设备初始化。
初始化请求数组request[],将所有请求项置为空闲项(dev = -1)。
5.chr_dev_init(); // 字符设备初始化。尚为空操作。
6.tty_init(); // tty 初始化。
/// tty 终端初始化函数。
// 初始化串口终端和控制台终端。
void tty_init (void)
{
rs_init (); // 初始化串行中断程序和串行接口1 和2。(serial.c, 37)
con_init ();