计算机的内部结构和启动过程剖析

来源:互联网 发布:php中= 编辑:程序博客网 时间:2024/04/20 03:08

    我们在使用计算机时,当我们按下电源按钮,我们会发现计算机先进入BIOS,然后再进入操作系统,最后计算机成功启动。那么,计算机的启动过程是怎么样的呢?为什么要进入BIOS?另外,当计算机存在双系统时,我们还可以选择进入到哪个系统,这个又是怎么实现呢?想要比较详细地理解整个过程,我们先来看看计算机的硬件构成和主板芯片组的系统结构框图。

1 计算机芯片组和主板结构

    要想深入了解计算机的启动过程,我们先来看看目前计算机主板的系统结构:


1.1 系统总线

    总线分为数据总线、地址总线、控制总线,分别用来传输数据、数据地址和控制信号。它用于连接计算机的主要部件,如内存、硬盘、显卡等。总线在单位时间内传输的数据量用带宽来表示:总线的带宽=总线的工作频率*总线的位宽/8,单位是Mbps,即每秒能够传送多少兆字节的数据。

1.2 北桥芯片

    在主板芯片组中北桥芯片通过前端总线连接CPU,通过内存总线连接内存,通过图像总线连接显卡,通过内总线连接南桥芯片。北桥芯片是CPU与内存、显卡和其他外设进行通信的桥梁。

    CPU通过前端总线与北桥芯片连接,再通过系统总线实现CPU对其他部件的访问。前端总线的频率,以MHZ为单位,它是一个很重要的频率,因为它和时钟倍频器决定着CPU的工作频率,同时前端总线的频率为系统总线频率提供时钟源,此外,前端总线频率也决定着CPU与外设的访问速度,进而决定该计算机系统的运行效率,所以并不是搭载主频越高的CPU的计算机,它的运行效率越高。

1.3 南桥芯片

    南桥芯片主要负责IO,为CPU访问外设提供媒介,如硬盘、网卡、声卡以及其他各种外设接口的设备(键盘、鼠标、SD卡)等。

2 计算机系统内存地址分配

    CPU要想和不同的设备通讯,就必须知道该设备的地址范围,并通过地址总线选择具体的设备和具体的地址,再通过控制总线来控制数据的流向,数据总线来进行规定的数据传输,这就需要为每个设备分配一个CPU可以寻址的内存地址范围,事实上,内存映射表是主板提供的。对于32位X86架构的CPU来说,它的地址总线是32位的,因此最大寻址的空间是2^32=4G,在该体系下一个典型的内存映射如下图:


   0~4GB的地址范围属于物理地址,而对于CPU的保护模式来说,它使用的是逻辑地址,逻辑地址需要地址转换器转换为物理地址,进而实现对设备的访问。CPU的实模式就直接使用物理地址的,而且目前的CPU在上电后默认的模式都是实模式,这个是为了满足向下兼容的特性而设计的,因为80386以前的CPU只有实模式,而相应设计的操作系统就是工作在实模式的,因此为了使后续的CPU能够运行早期的操作系统,因此在设计上使CPU在启动时默认采用实模式。事实上,在80386即以后出现的CPU才支持保护模式。

    上图中,内存大概占了差不多3G的空间,大概1G的空间分配到支持PCI、APCI等总线的外设了。注意在实模式下,只有1M的地址空间分配到RAM。

3 启动过程


    
    当按下主机的电源按钮后,首先是主板芯片组进行初始化,并检查CPU是否接入且工作正常,当一切准备就绪后就需要CPU执行BIOS程序了,如果是多核CPU,则会随机选择一个CPU执行。此时的CPU是工作在实模式下的,内存分页机制是关闭的,因此它只能访问1M大小的内存空间。CPU在上电后,大部分的寄存器会有个初始值,这里我们要关注EIP,它的初始值为0xFFFFFFF0,使得程序执行的第一条指令地址为0xFFFFFFF0,它实际上是复位向量的地址(reset vector),而复位向量指向BIOS程序的入口,至此进入“BIOS Initialization”阶段。
    在“BIOS Initialization”阶段,BIOS将对硬件进行初始化和硬件上电自检,这个过程称作POST,在这个过程中,如果计算机没有接入显卡,则自检不通过,因为显卡最终会显示操作系统的启动界面和登陆界面等;POST此外会进行一系列的检查,并将相关信息打印在屏幕上,并根据标准“Advanced Configuration and Power Interface
”建立用于描述各个设备的数据表,这些数据表在后面会被OS的内核用到。
    POST过程之后,就需要启动操作系统了,如果是双系统,屏幕会提示选择需要启动的操作系统,这个过程是如何实现的?
    首先,操作系统的数据和代码会存在存储介质中,如硬盘、CD-ROM甚至是U盘中,BIOS会按照一定的顺序寻找可以启动的设备,就像我们用U盘启动WINPE系统,我们需要实现在BIOS设置里面将U盘设置为最先启动项,那么BIOS会首先在U盘中启动。一般来讲,BIOS会寻找可以启动的硬盘以启动操作系统,那么这个过程的实现原理是什么?
    我们先看硬盘的构成:

    硬盘的存储空间分布:

    可以看到,一个磁盘可分为多个扇形区域,也可以分为多个磁轨track,而扇形区域和磁轨重叠的部分就是扇区sector,每个sector的容量是固定512byte的。另外,一个硬盘会有多个磁盘面,每个盘面的同一个磁轨组成一个磁柱cylinder,每个磁盘面有一个读写头header,就是说一个磁盘有两个磁头(2个次盘面)。虽然内外磁轨的长度不一样,但是每条磁轨上的扇区数还是一样的,就是说内圈密度大,外圈密度小。

一个硬盘的存储空间大小的计算公式为:
容量=磁头数量*每个盘面的磁轨数量*每条磁轨的扇区数量*每个扇区的字节数(512字节)
    硬盘的第一个扇区sector0非常重要,它存储了OS的主引导记录表(Master Boot Record,MBR)和分区表,一共有512字节,分区表最多存储4个分区记录,每个分区记录占16个字节。如下表:

标准MBR结构

地址

描述

长度

Hex

Oct

Dec

0000

0000

0

代码区

440

(最大446)

01B8

0670

440

disk signature (optional)

4

01BC

0674

444

一般为空值; 0x0000

2

01BE

0676

446

Table of primary partitions

(四个16 byte的主分区表入口)

64

01FE

0776

510

55h

MBR 有效标志:

0x55AA

2

01FF

0777

511

AAh

MBR, 总大小: 446 + 64 + 2 =

512




单个分区表结构

偏移

长度(字节)

描述

00H

1

分区状态:0x00-->非活动(不可引导)分区;

0x80--> 活动(可引导)分区;

其它数值没有意义

01H

1

分区起始磁头号(HEAD),用到全部8位

02H

2

分区起始扇区号(SECTOR),占据02H的位0-5;

该分区的起始磁柱号(CYLINDER),占据

02H的位6-7和03H的全部8位

04H

1

分区类型

05H

1

分区结束磁头号(HEAD),用到全部8位

06H

2

分区结束扇区号(SECTOR),占据06H的位0-5;

该分区的起始磁柱号(CYLINDER),占据

06H的位6-7和07H的全部8位

08H

4

分区起始相对扇区号

0CH

4

分区总的扇区数



    BIOS会将MBR中的所有数据拷贝到物理地址0x7C00,然后再跳到该地址执行代码。此时的代码对于windows来说可以是MBR Loader,对于Linux来说可以是LILO或GRUB。
    以MBR Loader为例,MBR Loader会寻找MBR分区表中活动的分区,该活动的分区的第一个sector存放有OS的启动代码,称为boot sector,MBR Loader将执行boot sector中的程序从而完成Windows的启动。
    Linux的LILO或GRUB启动方式有所不同,它将进行如下几个阶段的启动过程:
  1. Linux在MBR中存放第一阶段的引导代码,称为阶段1;
  2. 因为阶段1的代码很小,只占MBR中的一小部分,因此阶段1的代码将存放在其它扇区的引导代码下载到MBR中,这些扇区一般为其他分区的boot sector,也可以被Linux在安装时将某个扇区直接写到MBR中,这样阶段1的代码可以直接根据该信息直接跳到目标扇区去下载代码,这里统称为boot sector;
  3. 当boot sector的代码都被下载到MBR后,该代码将执行,此时称为阶段2,引导代码将读取一个配置,该配置文件中存储有OS的相关信息,如果你安装了多系统,此时会让你选择启动哪个OS;
  4. 当引导代码知道要启动哪个操作系统后,引导代码会将boot sector所在分区中的内核文件拷贝到内存中,然后再跳到内存中执行内核启动代码,自此操作系统开始启动。

3.1 Linux LILO启动过程

    因为LILO可以事先存在MBR中或者每个分区的boot sector中,我们首先假定LILO存放在MBR。
    前文所述,MBR中只存放着第一阶段的代码,且此时MBR中的代码已经被拷贝到内存0x7C00并被执行了。
  1. 此时LILO第一阶段的代码会根据相关信息,到指定的扇区sector将阶段二的代码拷贝到0x00096c00,并且在0x000969ff~0x00098000范围建立实模式堆栈,并跳转到0x00096c00执行阶段二的代码。阶段二的代码将根据分区表寻找可以启动OS的分区,并通过BIOS的底层驱动打印信息提示用户选择启动哪个操作系统。若启动其他的OS,LILO将相应分区的boot sector中的启动代码拷贝到内存中并执行。
  2. 若启动的是Linux,则将内核镜像文件的前512字节存放到内存地址0x00090000,而镜像文件中的setup()函数存放到地址0x00090200。
  3. LILO再将镜像文件的剩下部分拷贝到相应的其他地址(如果使用指令"make zImage"则存放在地址0x00010000;如果使用指令"make bzImage"则存放在地址0x00100000)。
  4. 跳转执行setup()代码,该代码将执行以下工作:(1)调用BIOS底层接口建立一个描述系统物理内存布局的表;(2)设置键盘持续按下时判定“重复敲击”事件的阈值时间;(3)初始化显卡;(4)初始化硬盘控制器;(5)检查IBM Micro Channel Bus(MBA);(6)检查PS/2接口的设备;(7)检查BIOS是否支持Advanced Power Management(APM)功能;(6)

  参考链接:
  http://duartes.org/gustavo/blog/post/how-computers-boot-up/
  http://duartes.org/gustavo/blog/post/motherboard-chipsets-memory-map/










    

1 0
原创粉丝点击