这里写自定义目录标题
bios
初始化硬件,加载bootsect.s,第一个磁盘的512字节
bootsect.s
bootsect.s完成的工作首先是将引导扇区从内存地址0x07C00移动到0x90000处,然后读取共占四个扇区的setup模块,显示加载系统的信息,然后将system模块读入到内存地址0x10000处
setup.s
setup.s首先读取包括光标位置,扩展内存(超过1MB大小以外的物理内存)大小,显示模式,硬盘参数等系统参数,并将这些参数写入到空间地址0x90000~0x90200这512个字节的空间中,然后将system模块从地址0x10000处移动到地址0x00000处,覆盖掉了BIOS用到的数据和中断向量表,这也说明了此后Linux系统确实不再使用BIOS提供的服务了。然后加载已经提前设置好的全局描述符表GDT到GDTR寄存器,中断描述符表IDT到IDTR寄存器,开启A20地址线,重新设置两个中断控制芯片,最后设置CPU的控制寄存器CR0(也称机器状态字),从而进入32位保护模式,并跳转到物理内存0x00000地址处执行,这里是system模块最前面的head.s程序。
setup执行后内存分布图:
head.s
head.s程序在被编译生成目标文件后会与内核其他程序一起被链接成system模块,位于system模块的最前面开始部分,这也就是为什么称其为头部(head)程序的原因。system模块将被放置在磁盘上setup模块之后开始的扇区中,即从磁盘上第6个扇区开始放置。一般情况下Linux 0.11内核的system模块大约有120KB左右,因此在磁盘上大约占240个扇区。
如head.s执行中的内存使用情况图所示,head.s首先重新设置中断描述符表idt,共256项,并使各个表项均指向一个只报错误的哑中断子程序ignore_int,加载idt表到idtr寄存器。然后设置全局描述符表gdt,实际上新设置的GDT表最开始的3个表项与原来在setup.s程序中设置的GDT表除了在段限长上有些区别以外(原为8MB,现为16MB),其他内容完全一样。不同的是,setup.s中设置的GDT表是临时的,只设置了3个表项,而head.s中设置的GDT表在后面一直使用,这里除了设置最开始的3个表项外,还把其他表项全部请零,加载gdt表到gdtr寄存器。接着检测A20地址线是否真的开启,以及测试PC机是否含有数字协处理器芯片(80287,80387或其他兼容芯片),并在控制寄存器CR0中设置相应的标志位。接着将main函数地址压栈,并设置管理内存的分页处理机制,将页目录表放在绝对物理地址0开始处(也是本程序所处的物理内存位置,因此这段程序将被覆盖掉),紧随后面放置共可寻址16MB内存的4个页表,并分别设置它们的表项,设置好页目录表和4个页表后,开启分页机制。最后,利用返回指令将之前预先放置在栈顶的/init/main.c程序的入口地址弹出,去运行*main()*程序。
注意:32位启动代码是从绝对地址 0x00000000 开始的,这里也是也目录将存在的地方,因此这里的启动代码将被也目录完整覆盖掉,head.s执行过程中内存分配如下:
寻址方式区别
实模式:
寻址方式为:
segment<<4+offset
保护模式:
段寄存器是选择子,结构如下
15 3 2 1 0
+----------------------------+--+--+--+
| 描述符索引 |TI| RPL | 段寄存器/选择子
+----------------------------+--+--+--+
- RPL(Requestor’s Privilege Level),请求者特权级
- TI(Table Indicator),表指示器,用于指定选择符所引用的描述符表。TI=0,指定GDT表;TI=1,指定当前的LDT表
- 描述符索引用于选择指定描述符表中的其中一个。处理器将该索引值乘上8,并加上描述符表的基地址即可访问表中指定的段描述符。
段描述符放在描述符表(gdt)中。每一个段描述符占8个字节。
下面是代码段描述符的字节分布:
63 54 53 52 51 50 48 47 46 44 43 40 39 32
+-------------+--+--+--+--+--------+--+----+--+--------+----------------+
| BaseAddress |G |B |0 |A |Segment |P | D |S | TYPE | BaseAddress |
| 31...24 | | | |V | Limit | | P | | | 23...16 |
| | | | |L | 19...16| | L |1 | 1|C|R|A| |
+-------------+--+--+--+--+--------+--+----+--+--------+----------------+
31 17 16 0
+----------------------------------+------------------------------------+
| BaseAddress | Segment |
| 15...0 | Limit |
| | 15...0 |
+----------------------------------+------------------------------------+
段式寻址过程图
虚拟基址经过段式寻址方式转化为线性地址,转换过程如下:
#gdb 调试
通过 gdb 调试读取gdt 表和 idt 表在内存中的位置,可以得到相应的值