Linux如何被启动(三)

Linux如何被启动(二)

head程序将在自己所在的内存空间(内存开始的地方,即0x000000)创建内核分页机制:页目录表、页表、缓冲区、GDT全局描述符表、IDT中断描述符表,已执行过的head程序的代码将会被覆盖。

  1. head程序正式执行,先将寄存器DS、ES、FS、GS、SS等从实模式转到保护模式。在实模式下,CS本身是代码段基址;在保护模式下,它是代码段选择符。设置完后,DS、ES、FS、GS,SS的值都是0x10(0001 0000):最后两位00表示内核特权级,如果是11则是用户特权级,从后数第三位0,表示选择GDT,1则表示选择LDT,从后数第4、5位10表示GDT的2项(0项、1项、2项、3项…)就是说它们都用着同一个全局描述符。保护模式下,段基址要使用GDT产生。栈顶指针也会从实模式SP调整为保护模式下的ESP,栈顶的增长方向是从高地址向低地址的。
  2. head程序接下来就是对IDT进行设置。中断描述符为64位,其中包含了对应中断服务程序的段内偏移地址、所在段选择符、描述符特权级、段存在标志、段描述符类型等信息。CPU在找对应的中断服务程序时,中断描述符的第0~15位和第48~63位组成中断服务程序的段内偏移地址,第16~31位为段选择符,用于定位中断服务程序所在的段,第47位为段存在标志(P),用于标示此段是否在内存中,为虚拟存储提供支持,第45~46位为段描述符标志,中断描述符对应的类型标志为0111(0xE),即“386中断门“。head程序会先让所有中断描述符默认指向这个位置ignore_int,将来在main函数中再设置中断描述符指向具体的中断服务程序。现在先将中断机制的整体架构搭起来。IDT有256个表项、将来实际只会用到其中的几十个,基本则不会用到,如果误用也无所谓,因为那么都指向ignore_int,返回的信息会让开发人员注意到错误。
  3. head程序下一步就是先废除GDT(setup程序建立的),并在内核新的位置重新创建GDT。新创建的GDT第2、3项分别是内核代码段描述符和内核数据段描述符。为什么要废除原来的GDT呢?因为原来的GDT是在setup程序里的,将来这个setup程序所在的内存位置会被用来设计缓冲区,到时就会被覆盖掉。将来比较安全的位置就是head程序所在的位置。
  4. head程序接着会检查A20是否已打开。如果没有打开,超过0xFFFFF寻址必然回滚到0x00000
  5. head程序会检测是否有数学处理器的存在,有的话,就将其设置为保护模式工作状态,这个数学处理器是为了弥补x86系列在进行浮点运算时的不足,据说从486开始,以后的CPU一般都内置了协处理器。
  6. head程序将L6标号和main函数入口地址压栈,栈顶为main函数地址,目的是为使head程序执行完后,通过ret指令就可以直接执行main函数。main函数在正常情况下不会退出,也不应该退出。如果退出的话,就会返回这里的标号L6处继续执行,此时,还可以做一些系统调用。另外,即使main函数退出了,如果还有里程存在,仍然能够进行轮转。
  7. head程序做完这些压栈后,head程序开始创建分页机制。先将页目录表和4个页表放在物理内存的起始位置,从内存起始位置开始的5个页空间全部清零(每页4KB)。这一个页目录表和4个页表就已经覆盖了head程序自身所占内存空间。这一个页目录表和4个页表是Linux操作系统能够控制所有进程在内存中安全运行的重要基础。head程序会将页目录的前4项设置成分别指向这4个页表。设置完页目录表后,将第4个页表的最后一个页表项指向寻址范围的最后一个页面。然后开始从高地址向低地址方向填写4个页表,依次指向内存从高地址向低地址方向的各个页面(第4个页表的倒数第2个页表项指向寻址范围的倒数第2个页面,其他依次类推)。这4个页表都是内核专属的,将来每个用户进程都会有它们各自专属的页表。
  8. 到此,head程序已将页表设置完毕了。接着设置页目录表基址寄存器CR3,使它指向页目录表。CR3的高20位存放页目录表的基地址,当CR0的PG位置位时,CPU使用CR3指向的页目录表和页表进行虚拟地址到物理地址的映射。head程序再将CR0寄存器最高位(第31位)置为1,这是开启分页机制的控制位,它的第0位是控制是否进入保护模式。所以CR0的第0位是1(保护模式),才能设置它的第31位为1(PG位,即分页机制控制位)。否则会报错。内核的分页机制到此就构建完成了。用户进程也会有自己的分页机制。
  9. head程序执行最后一步:ret,跳入main函数执行。之前main函数的入口地址被压入了栈顶,执行ret,就会将压入的main函数的执行入口地址弹出给EIP。

setup 程序将system模块移动到内存开始的地方0x00000,然后system模块的head程序(在内存开始的地方)在内存起始的位置建立了页目录表和页表,并开启了分页机制,从而建立了内核分页机制,为内核控制所有用户程序打下了基础。将页目录表和4个页表放大内存开始的地方,是因为这个位置是能够实现线性地址等于物理地址的唯一起始位置。

你可能感兴趣的:(linux,运维,服务器)