声明:文章是个临时起意的小科普文,不是很严谨,建议多看文中提及的参考资料或者亲自扒Linux源码。
该部分参考资料:
王爽《汇编语言》
李忠、王晓波《x86汇编语言:从实模式到保护模式》
于渊《一个操作系统的实现》
刘超《趣谈Linux操作系统》
CPU加电复位,主要是预置处理器中寄存器的初始状态。
在8086CPU中其中除了CS全部置一之外,其他的13个寄存器全部置零。这样操作的目的是为了灵活的进行jmp转移执行系统ROM(不写死,换了不同的系统或体系,只需要jmp到不同的地址即可)。
执行系统ROM程序的目的有三个:
一是硬件的自检;二是初始化系统BIOS(这一部分主要是主板BIOS,除了系统BIOS之外,还有网卡和显卡的BIOS),准备好实模式中断服务;三是初始化了系统BIOS之后,具备了IO能力,然后将硬盘的0面0道1扇区(即主引导扇区MBR,共512字节)加载到0x07c00处并执行主引导记录MBR。
(图来自《汇编语言》1.15)
MBR主引导扇区由三个部分组成:MBR代码、DPT和主引导扇区标记0xAA55
MBR代码:主引导记录,即主引导扇区前446字节,存储的数据是引导代码,主要是计算操作系统在硬盘中的位置,并加载执行,将权限交给操作系统(后面80X86机器这里是加载boot程序,操作系统的磁盘寻址和加载交由boot程序处理)。DPT磁盘分区表disk partition table(现在几乎都是GPT——Guid partition table),分区表指明了各个分区的起始位置、大小、类型,最重要的是标记是否为活动分区。分区表中只允许设置一个分区为活动分区,表明该分区是“可以启动的”。0xAA55是主引导记录的标志。
主引导记录代码通过分区表知道了活动分区,然后从活动分区加载DOS操作系统,然后执行操作系统代码,完成内核初始化之后,OS才算启动完成。
以上是8086机器(实模式)的启动过程。
8086机器只有实模式,保护模式是80286机器开始的。所以在80286以后的系统,在加载操作系统之前,还需要做一些从实模式进入保护模式的准备,之后才会加载操作系统。
准备进入保护模式的主要步骤:
参考文章:https://blog.csdn.net/gatieme/article/details/50914250
刘超《趣谈Linux操作系统》
Linux的启动和上面8086的启动过程(80286以后的机器在启动过程中还需要做从实模式进入保护模式的准备)有很多相似的地方。但是Linux的启动过程不再只是MBR去加载DOS系统那么简单,现在的操作系统太大了。Linux的启动过程之前是GRUB引导程序来引导的,现在是GRUB2。
GRUB2工具是一个来自GNU项目的OS启动引导程序,可以通过修改其配置文件grub.cfg,对一些启动过程自定义。
比较Linux和8086的启动过程:在8086机器的四个步骤中,CPU加电复位和BIOS之后就是MBR和加载操作系统,但是在Linux启动过程中,在CPU加电复位和BIOS之后,就由GRUB2来负责了。
Linux启动的主要步骤:
CPU加电复位;系统BIOS(系统ROM);GRUB2过程(可以通过grubs-install来安装grub2启动程序);加载操作系统,初始化内核,Linux启动完成;
GRUB2过程中涉及的主要步骤如下图:
其中:
boog.img就相当于8086过程中的MBR(但是boot.img是镜像文件,grub2会解压成boot.s代码,之后跳转执行),也是512字节,结构、行为和MBR一样。所以boot.img所做的也很有限,要想加载core.img,需要boot.img跳转并将控制权交给diskboot.img。diskboot.img的主要功能就是加载core.img的其他部分;lzma_decompress.img会调用real_to_prot跳转到保护模式;kernel.img中的代码会选择并加载指定操作系统,然后启动并初始化Linux内核。
该部分参考资料:
刘超《趣谈Linux操作系统》
Daniel P.bovet、Marco Cesati《深入理解Linux内核》(主要是附录)
Wolfgang Mauerer《深入理解Linux内核架构》(主要是附录)
参考第一部分的内容。
start_kernel完成Linux内核的初始化工作。
init_task:初始化0号进程idle;trap_init:初始化系统中断和陷阱(系统调用相关的做准备);mm_init:初始化内存管理模块:sched_init:初始化进程调度模块;rest_init:初始化用户态所有进程的祖先进程init,init会监视其他用户线程;初始化内核态所有线程的祖先kthreadd进程,kthreadd负责调度和管理所有内核线程。
附注:本文如有错漏,烦请指正,谢谢。