LINUX加电启动流程

111 阅读3分钟

从计算机加电开始,PC值为0XFFFF0,执行BIOS的程序,在物理地址0处初始化中断向量,我们之后要用到BIOS的中断获取设备信息进行初始化工作。它将bootsect.S读到0X7C00(物理地址)处,BIOS执行完后会执行bootsect.S程序,这个程序将setup.S读到0X90200(物理地址)处,将内核的其它部分读到0x10000(物理地址)处,之后所执行的head.s就在这一块最开始部分也就是当前的0x10000处,但是它最后会被移到物理地址为0的地方工作(此时setup.S)已经用好了BIOS的中断,所以可以把它迁移到物理地址为0 的地方工作,最后执行head.S,然后进入到main()函数。一直是单执行流。知道fork()了第一个子进程后,才会出现进程调度的情况。0.12中第一个扇区是BOOT,第二到四个扇区是SETUP,之后的扇区就是system模块。

Bootsect.S主要工作(除上述部分外):建立驱动参数表(会用到物理地址0开始的BIOS中断,运行在实模式,只能寻址1MB。

Setup.s:在这个地方会由实模式到保护模式,读取机器的设置到之前bootsect.s的位置(执行完的程序就可以被覆盖掉,之后的head.s也会被页表覆盖),设置IDT和GDT和LDT(分别对应着中断描述符表,全局描述符表和局部描述符表),这三个表都有相应的寄存器来存放基地址,所以寻址的时候只需要给出偏移值。

Head.s:重新设置IDT,GDT,并设置页目录表和页表就在物理地址0开始出,也就是本程序所在的位置,所以会覆盖head.s,最后会将main()函数的地址入栈,ret后就会进入到main()函数执行。

首先进程0在内核态下进行一些初始化工作,然后move_to_user_mode()转移到用户态,(这部分首先在内核态堆栈压入cs ip等寄存器然后ret返回,在cs的低端的cpl保存了当前的特权级)然后pause()系统调用把自己切换成可中断状态,当没有除0进程之外的其它进程时候,进程0就会运行

期间进程0会创建进程1,调用init(),首先产生三个fd也就是我们所说的标准输入,标准输出和错误输出,然后创建进程2执行/etc/rc里面的命令,执行完后退出,此时的父进程也就是进程1一直在wait()进程2,wait()函数的作用是当子进程结束后会给父进程发消息,也就是子进程的进程号。

当返回的wait()读到的进程号等于子进程号,就会创建新的进程,此时都是(/bin/sh)作为执行程序,但是可以看到环境变量和参数不同,即此时的shell会当作登陆shell执行,创建会话绑定字符终端,等待用户登录(输入用户名和密码)