深入理解linux内核读书笔记1--系统启动

史前时代--BIOS阶段:

计算机在加电的后,一个特殊的硬件会在cpu的引脚处,产生RESET信号,cpu的部分寄存器(cs, eip)
就会被设定为固定的值,然后执行在oxfffffff0(位于CMOS)处的代码,即BIOS。BIOS是一个小型
的操作系统,所以linux内核在初始启动过程中也部分依赖BIOS来工作。此时,执行的BIOS,
是处于实模式(real mode)的。实模式下的地址,分为逻辑地址和线性地址(线性地址就是物理地址)。
逻辑地址就是段内地址。线性地址(物理地址)可以通过seg*16 + offset得到。seg*16相当于左移4位。
CPU此时寻址,不需要全局描述符表,局部描述符表或者页表把逻辑地址转换为物理地址。GDT,LDT和页表的
初始化代码必须在实模式下运行。

BISO启动过程执行的步骤:
1.POST(power-on self test)开机自检。检测那些有哪些设备,以及这些设备是否正常工作。

2.初始化硬件设备,这个在现代的基于PCI体系结构的PC非常重要,可以保证硬件设备操作不会引起
IRQ(中断请求)和I/O端口的冲突。完成后,会显示系统中安装的所有PCI设备的一个列表。LINUX内核
在后来会自己初始化硬件设备,不依赖BIOS的初始化。

3.扫描主引导记录(MBR)--第一块硬盘的第一个扇区(512字节),把扇区里的代码拷贝到RAM(内存)的Ox7c00处。然后跳转到这个地方,开始执行拷贝过来的代码,也即开始执行boot loader了。

远古时代:引导装入程序

boot loader用来把内核映像加载到RAM中。这个boot loader做的事情就是,在自己被BIOS移到0x7c00处
,并跳转到这儿执行后,就把自己移到0x96a00,建立实模式栈(0x98000-0x969ff),并且把并把boot loader第二部分装入到0x96c00处,第二个部分,从磁盘读取可用的操作系统,供用户读取(菜单模式),用户选取相应的
菜单以后,boot loader就把相应的内核模块加载到RAM(如果使用二级引导(chainloader)的话,就拷贝相应分区的引导扇区)。

步骤:
1.调用BIOS中断,显示Loading信息。
2.调用BIOS中断,从磁盘加载内核映像的初始部分:即将内核映像的第一个512字节从地址0x00090000开始存入RAM中,将setup()函数代码存入到从内存0x00090200处,setup()函数的指令代码存放在内核映像偏移地址0x200处。
3.调用BIOS中断,从磁盘装载其余的内核映像,并吧内核映像放入内存的0x00100000处.
4.跳转到setup()函数.

中世纪: setup()函数
setup()汇编函数,由链接器装入到内核映像的0x200偏移处。boot loader可以很容易的把它加载到内存的0x00090200处(见上2)。setup()函数必须初始化硬件中的大部分设备,我们说过linux不依赖BIOS来初始化硬件。
setup()执行下面的操作:
1.在ACPI兼容系统里,它调用BIOS中断,在RAM中建立物理内存的分布表(layout),在早期的linux系统中,它调用BIOS中断,返回系统的可用内存。
2.设置键盘重复延时和速率(repeat delay and rate)。
3.初始化视频适配器卡(vedio adapter card)。
4.重新初始化磁盘控制器并测定磁盘参数。
5.检查ibm的微通道总线(Micro Channel bus --MCA)
6.检查ps/2指针设备(总线鼠标)
7.检查高级电源管理(APM)BIOS支持。
8.如果BIOS支持增强磁盘驱动服务(EDD),它调用相应的BIOS中中断在内存中建立一个系统可用
硬盘表。(表中的信息,可用通过读取sysfs特殊文件系统中的firmware/edd目录看到。)
9.
10.设置8042键盘控制器的A20引脚,A20引脚是在80286引入的-能够使物理地址和早期的8088微处理器兼容的一项基本措施。不幸的是,在切换到保护模式之前,A20引脚必须被正确的设置。否则,每个物理地址的21位将被CPU认为是0。
11.建立一个临时的中断描述符表,(IDT),和一个临时的全局描述符表(GDT)。
12.如果有的话,重置浮点单元(FPU).
13.重新对可编程中断控制器(PIC)编程,来屏蔽所有的中断,但不包括IRQ2,因为它是两个可编程终端控制器的几级联中断。
14.把CPU从实模式切换到保护模式,通过设置CR0控制寄存器的PE位为0。CR0寄存器的PG位被清零,此时分页还没有激活。
15.跳转到startup_32()汇编函数处。


文艺复兴时代:startup_32函数
有两个不同的startup_32()函数,我们这里说的在arch/i386/boot/compressed/head.S。
setup()函数结束之后,startup_32()被移到0x00100000处。此函数执行下面的操作:

1.初始化段寄存器和一个临时战。
2.把eflags寄存器是所有位清零。
3.用0填充_edata和_end符号标识的未初始化内核数据。
4.调用decompress_kernel()函数解压内核映像。首先显示Uncompressing Linux...信息。
内核映像被解压后,显示OK,booting the kernel。解压的内核被放置在压缩的内核后面的临时缓冲区里。解压缩的内核映像接着被移到从0x00100000开始的位置(覆盖了原来压缩的内核)。
5.跳转到物理地址0x00100000处。

解压缩的内核从另一个startup_32()函数开始(rch/i386/kernel/head.S)。这个starup_32()函数为第一个linux进程(0号进程)建立可执行环境。函数执行下面的操作:
1.用最终值初始化段寄存器。
2.用0填充内核的bss段。
3.初始化包含在swapper_pg_dir里的临时内核页表 和pg0,把线性地址映射到同一个物理地址。
4.存储页全局目录地址到CR3控制寄存器里,并把CR0的PG位置1,激活分页。
5.为0号进程建立内核模式栈。
6.再次对eflags标志寄存器清零。
7.调用setup_idt()函数把中断描述符表设置为空。
8.把从BIOS获得的系统参数和传递给系统的参数放置在第一页框里。
9.识别处理器的型号。
10.用GDT和IDT填充gdtr和idtr寄存器。
11.跳转到start_kernel()函数。

现代: start_kernel()函数

start_kernel()函数完成linux内核的初始化工作,几乎每个内核组件都是由这个函数初始化的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值