《Linux内核设计艺术》——1.BIOS

1. 上电时发生了什么


CS寄存器和IP寄存器被置位,让cpu运行BIOS程序。这个步骤是硬件实现。
CS寄存器为 code segment register,ip/eip : instrucation pointer
这时BIOS被执行,BIOS之所以能被执行,还因为他被写入ROM中,ROM是掉电不丢失数据的内存。

2. BIOS做了什么

BIOS检查并初始化了硬件,并构建运行环境(排布数据段,程序段,在内存的分布),
并构建了中断向量表。

当开启中断后,BIOS能接受硬件中断,实现和用户交互(键盘中断),读取磁盘(磁盘中断)等。
完成上面工作后,BIOS开始加载kernel。
BIOS从约定的位置读取磁盘,然后运行读取的程序。
若被加载的程序时kernel,则BIOS加载kernel的bootsect模块。
BIOS运行bootsect模块。

3. bootsect模块做了什么

显然BIOS不能将kernel所有部分加载到内存,所以,bootsect需要完成kernel加载工作。
bootsect首先对内存的使用进行规划,这里是实模式

.global begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext
.data
begdata
.bss
begbss
.text
SETUPLEN=4
BOOTSEG=0x07c0
INITSEG=0x9000
SETUPSEG=0x9020
SYSSEG=0x1000
ENDSEG=SYSSEG+SYSSIZE
ROOT_DEV=0x306


然后bootsect将自己复制到INITSEG,并执行新段处的代码。
然后设置栈

有了栈,意味可以执行更复杂的代码。

最后借助BIOS的中断(读磁盘的中断),加载setup模块到SETUPSEG段,位于 bootsect的尾端。

再加载system模块到SYSSEG段后120KB的位置

最后跳转到setup模块执行。

4. setup模块做了什么

setup首先关闭中断(因为他要设置新的中断服务)。
把system模块复制到0x000000 ,这样BIOS中断占用的空间就被回收了,且kernel占据了最好的内存位置。

setup使用自身提供的数据构造IDT,GDT,并设置IDTR,GDTR
IDT(Interrupt Descriptor Table),保护模式下所有中断服务程序的入口地址
GDT(Global Descriptor Table), 存放每个任务的LDT,和TSS
LDT:记录进程的地址映射表
TSS:用于保存进程上下文


setup让GDTR和IDTR指向内存对应位置,并初始化了kernel的LDT

打开A20,实现32位寻址

这样Linux0.11的物理寻址虽然只有16MB,但线性地址已经有4GB

之后对中断器进行设置,
然后开启保护模式

实模式时,寻址只使用CS做基地址,IP做偏移,
保护模式时,根据task_struct找到gdt中的ldt,使用ldt和cs的16位组合,得到64位,得到基地址,并加上EIP做偏移。

cs加ldt的64位组合,包含了基地址,保护信息等。

setup跳到head模块

5. head模块做了什么

head模块和kernel模块一起被链接到system模块,

head模块创建了内核分页机制,即在0x00000000创建了 页目录表,页表,缓存区,GDT,IDT。

gdt[2]就是内核的数据段,gdt[1]是内核的代码段。

构造IDT

head构造页目录和页表

构造4个页表,并页表项指向对应的页面,每个页面4KB,

这4个页表和对应页面就是内核的地址空间。
之后每个进程都有自己的页表。

最后head设置好栈,执行main程序

posted on 2022-08-09 09:37  开心种树  阅读(424)  评论(0编辑  收藏  举报