linux系统的启动过程
BIOS和启动区
CPU中的程序计数器寄PC中存放着CPU下一条执行指令的地址,在计算机通电后CPU会访问PC寄存器中的地址。
英特尔的芯片PC寄存器中的初始值为0xFFFF0
,这意味着在计算机通电后CPU会执行0xFFFF0这个地址空间中的地址。我们把这块地址空间中存放的程序叫做BIOS(basic input output system)
。
Bios程序是硬件厂商写好的,无法改变。BIOS中的程序做的事情是将硬盘0盘0到1扇区
的512字节的内容到内存0x7c00中
。硬盘的0盘0道1扇区
这块区域又叫做启动区MBR(Master Boot Record,主引导记录),启动区最后两个字节分别是0x55
和0xaa
,这是BIOS识别启动区的标志。
总结一下在计算机通电之后,CPU执行BIOS程序,BIOS搬运启动区代码到内存0x07c00
中。
bootsect.s
在linux0.11中,启动区的源码文件名为bootsect.s,它将被编译为二进制程序,存放在硬盘的0盘0道1扇区中,作为MBR。
当BIOS搬运完启动区的代码到内存0x7c00后,CPU将执行启动区的bootsect程序
让我们来看一下启动区的代码
复制自身到0x90000
1 | _start: |
这段代码的目的是将内存地址0x7c00
的512字节即启动区程序复制到0x9000
处,进行调转指令,跳转地址为[0x9000:go],执行位于0x9000的bootsect程序的go位置
加载setup.s程序到0x90200
bootssect
在复制自身数据到内存0x90000
后,会将setup
程序移动到内存当中
1 | load_setup: |
13号中断是BIOS提供的,用于读写磁盘。在这个程序中,我们使用了13号中断来将磁盘的setup
程序搬运到内存0x90200
处,共四个扇区大小,如果复制成功,则跳转到ok_load_setup
处,否则重置磁盘,重复执行之前的代码
加载system
bootsect
在执行完load_setup
后跳转到了ok_load_setup
1 | ok_load_setup: |
ok_load_setup
使用13号中断获取了磁盘信息,并在屏幕上显示了一些message,之后将head.s
和硬盘中的其他代码(system)的代码搬运到了0x10000
处
setup.s
获取硬件信息
setup函数首先使用bios的中断获取计算机的一些硬件信息
1 | start: |
上面代码使用10号中断读取光标位置,并存放在0x90000处,基地址为0x9000,偏移地址为0
1 | ; Get memory size (extended mem, kB) |
使用15号中断获取内存大小
1 | ; Get video-card data: |
使用10号中断获取显存信息
1 | ; check for EGA/VGA and some config parameters |
检查显示信息
1 | ; Get hd0 data |
获取第一块硬盘信息
1 | ; Get hd1 data |
获取第二块硬盘信息
1 | ;关闭中断 |
关闭中断
给system挪个位置
1 | ; first we move the system to it's rightful place |
setup
使用中断读取了硬件信息后将内存0x10000
到0x90000
的数据内容搬运到内存0开始的地方
linux系统启动的全过程
实模式与保护模式
实模式的寻址方式
8086CPU的寄存器为16位,地址线为20位,为了扩展CPU的寻址能力,有效利用20位的地址线,cpu采用基地址
和偏移地址
的方式来进行寻址。具体格式为物理地址=16位段地址*16+16位偏移地址
段地址*16也可以理解为段地址(2进制)左移4位后加上偏移地址。如何理解这种寻址方式呢?举个例子来说,180CM又可以表示为1米80CM,这里1可以理解为段地址,80CM对应着偏移地址.段地址和偏移地址拥有不同权重。
保护模式
在8086下,实模式寻址方式极不安全性,程序寻址不受限制,可以访问和修改内存中任意位置的数据。为了保证内存数据的安全,对权限进行控制,80386采用了一种新的方式来访问内存,我们称之为保护模式。当然80386也向下兼容88086实模式的寻址方式。
段选择子
在实模式下我们16位寄存器存放的是基地址,而在保护模式下,这16位有些位被赋予了不同的含义
段选择子就是原来的CS和DS的16位寄存器,它划分为了段描述符索引,TI和RPL三部分。段描述符索引是全局描述符的表项下标索引。当TI=0时,查全局描述符GDT表;TI=1时,查LDT表。RPL表示请求特权级别,在linux中0表示高特权,1表示低特权
段描述符
描述符表GDT存在于内存当中,CPU的GDTR寄存器保存着GDT在内存中的地址,描述符表由若干个段描述符组成。
段描述符主要由基地址,段界限和访问权限组成。
界限限制了基地址的最大访问地址,使得程序不能越界访问地址
访问权限:比较段选择子的RPL,来对内存的进行权限控制
段选择子中含有一个全局段描述符的索引。通过索引,可以获取到段描述符中的基地址。得到基地址之后,和偏移地址(偏移地址存放在esi中)进行合成,得到线性地址,如果不进行分页,这个线性地址就是物理地址。
分页机制
在进行分段后,如果不进行分页,那么线性地址就是逻辑地址。
使用分段机制后我们得提前划分内存空间,程序在内存中将被连续存储,这使得内存中一些碎片化的空间不能被有效利用起来,
于是我们引出了分页机制解决内存碎片化的问题。在分段后,我们得到线性地址。线性地址被划分为了三部分。
一本书可以怎么样定位到某一页呢?我们的书一般被划分若干的章节,而每一章又被划分为了若干节,在书的目录页记录了每一章的页码。所以我们如果要查找第一章第二节往后10页的内容,我们可以这样操作,我们根据目录页查找到第一章对应的页码,跳转到第一章,再根据节号,定位到第一章第二节,往后翻十页。通过这样的访问方式,我们定位到了我们想要访问的页。