本文仅作为学习记录,非商业用途,侵删,如需转载需作者同意。
x86 有两种模式:
- 实模式:只能寻址1M,每个段最多64K
- 保护模式:32位系统能够寻址4G
一、BIOS时期
当按下开机键,主板就加上电了。
这个时候CPU就开始执行指令了,只是没有项目执行计划书(程序)还没有事情可干,CPU(外包公司老板)也不知道干啥。
所有BIOS 相当于是刚开始的指导手册,只读的,告诉你一开始的该做一些准备工作。
计算机系统也早有计划,在主板上有个叫ROM(read only Memory 只读存储器),这个和内存RAM(Random Access Memory 随机存取存储器)不同,RAM是可读可写的,才能保存计算结果。
ROM 是只读的,上面固化了一些初始化的程序,也就是BIOS(Basic Input and output system 基本输入输出系统)
就是电脑启动时候,按组合键进入,蓝色界面,可以调整启动顺序的 就是 BIOS。
x86系统内存结构图:
如上图:
1M空间最上面的 0xF0000 到0xFFFFF这64k 映射给ROM,也就是说到这部分地址访问的时候,会访问ROM。
当电脑加电的时候会做一些重置工作:
将CS(code segment register)代码段寄存器设置为0xFFFF,将IP(指令指针寄存器(Instruction Pointer Register) ) 设置为 0x0000,所以第一条指令就会指向0xFFFF0,正是在ROM的范围内。
在这里有个JMP 中跳到 ROM中做初始化的代码,然后BIOS 开始做初始化的工作。
创业指导手册内容
第一条:BIOS要检查下硬件是否都正常
第二条:要有个办事大厅,提供服务,这个时候自己就是办事员,提供的服务也很简单,也有零星的客户来提要求。
需要建立一个中断向量表和中断服务程序,因为还需要用鼠标和键盘,这些需要中断进行的。
这期间也需要给客户输出一些结果,做了什么事情,做到什么程度,要显示给客户。 就是内存空间映射显存的空间,在显示器上显示一些字符。
二、bootloader时期
BIOS(创业指导手册)只能保障公司成立起来,如何做大做强需要另外一个经营方法。
操作系统很重要,操作系统放在硬盘上,BIOS的界面上看到一个启动盘的选项。
启动盘:一般在第一个扇区,占512字节,而且以0xAA55结束。
这是一个约定,当满足这个条件就说明是个启动盘,在512字节以内会启动相关代码。
这些代码是谁放的呢?
Linux里面有一个工具,叫Grub2 ,全称是 Grand Unified Bootloader Version 2 就是搞系统启动的。
你可以通过grub2-mkconfig -o /boot/grub2/grub.cfg 来配置系统的启动项,
menuentry 'CentOS Linux (3.10.0-862.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-862.el7.x86_64-advanced-b1aceb95-6b9e-464a-a589-bed66220ebee' {
load_video
set gfxpayload=keep
insmod gzio
insmod part_msdos
insmod ext2
set root='hd0,msdos1'
if [ x$feature_platform_search_hint = xy ]; then
search --no-floppy --fs-uuid --set=root --hint='hd0,msdos1' b1aceb95-6b9e-464a-a589-bed66220ebee
else
search --no-floppy --fs-uuid --set=root b1aceb95-6b9e-464a-a589-bed66220ebee
fi
linux16 /boot/vmlinuz-3.10.0-862.el7.x86_64 root=UUID=b1aceb95-6b9e-464a-a589-bed66220ebee ro console=tty0 console=ttyS0,115200 crashkernel=auto net.ifnames=0 biosdevname=0 rhgb quiet
initrd16 /boot/initramfs-3.10.0-862.el7.x86_64.img
}
这里面的选项会在系统启动时候,成为一个列表,让你选择从哪个系统启动:
使用grub2-install /dev/sda
可以将启动程序安装到相应的位置。
grub2 第一个要安装的就是boot.img。它由boot.S编译而成,一共512字节,正式安装到启动盘的第一个扇区。这个扇区称为MBR(Master Boot Record 主引导记录扇区)
BIOS 完成任务后,会将boot.img从硬盘加载到内存中的0x7c00来运行。
由于512个字节实在有限,boot.img 做不了太多的事情。它能做的最重要的一个事情就是加载grub2的另一个镜像 core.img。
引导扇区就是你找的门卫,虽然它看着档案库的大门,但是知道的事情很少,它不知道你的宝典在哪里,它把你领导档案管理员哪里,档案管理员知道。
core.img 就是管理员,知道和能做的事情就多一些了。core.img 由lzma_decompress.img,di’s’kboot.img,kernel.imgh和一系列模块组成,功能比较丰富能做很多事情。
boot.img 先加载的是core.img 的第一个扇区,如果从硬盘启动的话,这个扇区里面是diskboot.img,对应的代码是diskboot.S。
boot.img 将控制权交给diskboot.img 后,diskboot.img 的任务就是将core.img 的其他部分加载进来:先是解压缩程序lzma_decompress.img 再往下是kernel.img,最后是各个模块module 对应的映像。 这个不是 Linux 内核,是grub的内核。
lzma_decompress.img 对应的代码是 startup_raw.S ,本来kernel.img 是压缩过的,现在执行的时候,需要解压缩。
在这之前,我们遇到的程序都非常小,在实模式下就可以运行了,随着加载的东西越来越大,实模式这1M的地址空间实在放不下了。
在真正解压缩之前,lzma_decompress.img 做了一个重要的决定,就是调用real_to_prot 切换到保护模式下,这样能在更大的寻址空间里面,加载更多的东西。
三、从实模式切换到保护模式
管理处听说要找宝典,知道你是要做老板的人,既然是老板,就要雇人干活,不是个体户小打小闹。所以需要切换到老板角色进入保护模式,把哪些是你的权限,哪些是可以授权给别人的都分得清清楚楚。
切换到保护模式要干很多工作,大多数和内存的访问方式有关。
第一项:启用分段
在内存里建立段描述符表,将寄存器里面的段寄存器变成段选择子,指向某个d段描述符,这样就能实现不同的进程的切换了。
第二项:启动分页
能够管理的内存变大了,就需要将内存分成相等大小的块。这些放在内存那一节详细再讲。
切换到老板角色,也是为了招聘很多人,同时接多个项目,这个时候就需要划清界限,懂的集权与授权。
当了老板,眼界就要宽多了,同理保护模式下需要做一项工作,打开Gate A20,就是第21根地址线的控制线。在实模式8086下面,一共就20个地址总线,可以访问1M的地址空间。
如果超过了怎么办,只能是绕回来。在保护模式下,第21根要起作用了,我们就需要打开Gate A20 。
切换保护模式的函数 DATA32 call real_to_prot 会打开Gate A20,也就是第21根地址线的控制线。
现在好了,有的是空间了,接下来要对压缩过的kernel.img 进行解压缩,然后跳转到kernel.img 开始运行。
切换到老板的角色,就可以正大光明的进入档案馆,寻找你的那本宝典。
kernel.img 对应的代码是 startup.S 以及一堆c文件,在startup.S 中会调用grub_main,这是grub kernel 的主函数。
在这个函数里面,grub_load_config() 开始解析,我们上面写的那个grub.conf 文件里面的配置信息。
如果是正常启动,grub_main 最后会调用grub_command_execute(“normal”,0,0),最终会调用grub_normal_execute()函数。 在这个函数里面,grub_show_menu() 会显示出让你选择的那个操作系统的列表。
同理,作为老板,你发现这类的宝典不止一本,经营企业的方式也有很多种,到底是人性化的还是强纪律的,需要选择一种。
一旦,你选定了某个宝典,启动某个操作系统,就要开始调用grub_menu_execute_entry() ,开始解析并执行你选择的那一项,接下来你的经营企业之路就此打开了。
例如里面的linux16命令,表示装载指定的内核文件,并传递内核启动参数,于是grub_cmd_linux() 函数会被调用,它会首先读取Linux内核镜像头部的一些数据结构,放到内存中的数据结构来,进行检查,如果检查通过,则会读取整个Linux内核镜像到内存中。
如果配置文件里面还有initrd命令,用于为即将启动的内核传递 init ramdisk 路径,于是 grub_cmd_initrd() 函数会被调用,将 initramfs 加载到内存中来。
当这些事情做完以后,grub_command_execute(“boot”,0,0) 才开始真正的启动内核。