首页 > 代码库 > 开机加电到系统打开究竟发生了什么?(1)

开机加电到系统打开究竟发生了什么?(1)

每天工作都少不了一件事情:打开电脑。。。

开机过程中究竟发生了什么事情?电脑内部各零件是怎么变化的?多年前,我记得我很好奇,可是现在却有点麻木了。只有保持好奇心才能保持年轻!

说到计算机,还是先回想一下计算机的构成吧,第一次接触电脑不是在大学,而是上初中在网吧,和同学一起玩游戏,还是那时候欢乐多一点,头一次玩了一夜电脑,还是很兴奋的,让我最高兴的莫过于游戏可以无限续命,最终通关了游戏技术分享

后来上了大学,专业是软件技术(.net),懵懵懂懂的上了几节计算机的课,知道了计算机有主机和外设构成,听说了冯诺依曼,自己买了电脑,看见了内部构造,一大块电路板,然后插着根内存条,还有一个带风扇的大家伙,知道那是CPU,最后就是感觉很贵的

显卡了,对了还有一个硬盘和电源,学校晚上都是断电,搞了几次把硬盘和风扇都给弄坏了。。。机箱里面说完了,外面是液晶显示器、键盘、耳机、鼠标。个人PC,基本上都是这样了,再后来了,参加了工作,接触了服务器还有一体机,现在都是云服务器了,我也买了一个,体验一把,可惜一直空闲着,没有 部署应用。比之还要火的是Docker,不过我也刚刚接触,不甚了解。

还是回到计算机构成吧,以下摘抄自百度:

计算机(computer)俗称电脑,是现代一种用于高速计算的电子计算机器,可以进行数值计算,又可以进行逻辑计算,还具有存储记忆功能。是能够按照程序运行,自动、高速处理海量数据的现代化智能电子设备。
由硬件系统和软件系统所组成,没有安装任何软件的计算机称为裸机。可分为超级计算机、工业控制计算机、网络计算机、个人计算机、嵌入式计算机五类,较先进的计算机有生物计算机、光子计算机、量子计算机等。
计算机发明者约翰·冯·诺依曼。计算机是20世纪最先进的科学技术发明之一,对人类的生产活动和社会活动产生了极其重要的影响,并以强大的生命力飞速发展。它的应用领域从最初的军事科研应用扩展到社会的各个领域,
已形成了规模巨大的计算机产业,带动了全球范围的技术进步,由此引发了深刻的社会变革,计算机已遍及一般学校、企事业单位,进入寻常百姓家,成为信息社会中必不可少的工具。

-----------------------

CPU是什么?

是一块超大规模的集成电路,是一台计算机的运算核心(Core)和控制核心( Control Unit)。它的功能主要是解释计算机指令以及处理计算机软件中的数据。

CPU从逻辑上可以划分成3个模块,分别是控制单元、运算单元和存储单元,这三部分由CPU内部总线连接起来。如下所示:

技术分享

控制单元:控制单元是整个CPU的指挥控制中心,由指令寄存器IR(Instruction Register)、指令译码器ID(Instruction Decoder)和操作控制器OC(Operation Controller)等,对协调整个电脑有序工作极为重要。它根据用户预先编好的程序,依次从存储器中取出各条指令,放在指令寄存器IR中,通过指令译码(分析)确定应该进行什么操作,然后通过操作控制器OC,按确定的时序,向相应的部件发出微操作控制信号。操作控制器OC中主要包括节拍脉冲发生器、控制矩阵、时钟脉冲发生器、复位电路和启停电路等控制逻辑。

运算单元:是运算器的核心。可以执行算术运算(包括加减乘数等基本运算及其附加运算)和逻辑运算(包括移位、逻辑测试或两个值比较)。相对控制单元而言,运算器接受控制单元的命令而进行动作,即运算单元所进行的全部操作都是由控制单元发出的控制信号来指挥的,所以它是执行部件。

存储单元:包括CPU片内缓存和寄存器组,是CPU中暂时存放数据的地方,里面保存着那些等待处理的数据,或已经处理过的数据,CPU访问寄存器所用的时间要比访问内存的时间短。采用寄存器,可以减少CPU访问内存的次数,从而提高了CPU的工作速度。但因为受到芯片面积和集成度所限,寄存器组的容量不可能很大。寄存器组可分为专用寄存器和通用寄存器。专用寄存器的作用是固定的,分别寄存相应的数据。而通用寄存器用途广泛并可由程序员规定其用途,通用寄存器的数目因微处理器而异。这个是我们以后要介绍这个重点,这里先提一下。

我们将上图细化一下,可以得出CPU的工作原理概括如下:技术分享

OK,总结一下,CPU的运行原理就是:控制单元在时序脉冲的作用下,将指令计数器里所指向的指令地址(这个地址是在内存里的)送到地址总线上去,然后CPU将这个地址里的指令读到指令寄存器进行译码。对于执行指令过程中所需要用到的数据,会将数据地址也送到地址总线,然后CPU把数据读到CPU的内部存储单元(就是内部寄存器)暂存起来,最后命令运算单元对数据进行处理加工。周而复始,一直这样执行下去,天荒地老,海枯枝烂,直到停电。

这个文章很好: http://blog.chinaunix.net/uid-23069658-id-3563960.html

内存是什么?

对于一根实际的、实实在在的、物理的、可看得见、摸得着的内存条而言,处理器把它当做8位一个字节的序列来管理和存取,每一个内存字节都有一个对应的地址,我们叫它物理地址,用地址可以表示的长度叫做寻址空间。而CPU是如何去访问内存单元里的数据的方式就叫做寻址。

为了支持分段机制,Intel在8086的CPU里新增了4个寄存器,分别是代码段CS,数据段DS,堆栈段SS和其他ES(以后深入介绍一下这几个兄弟伙,这涉及到进程的在内存的运行情况)。这样一来,一个物理地址就由两个部分组成,分别是“段地址”:“段内偏移量”。例如,ES=0x1000,DI=0xFFFF,那么这个数据ES:DI在内存里的绝对物理地址就是:

AD(Absolute Address)=(ES)*(0x10)+(DI)=0x1FFFF

技术分享

很明显,这种方式可以寻址的最高地址为0xFFFF:0xFFFF,其地址空间为0x00000~0x10FFEF,因为8086的地址总线是20位,最大只能访问到1MB的物理地址空间,即物理地址空间是0x00000~0xFFFFF。当程序访问0x100000~0x10FFEF这一段地址时,因为其逻辑上是正常的,CPU并不会认为其访问越界而产生异常,但这段地址确实没有实际的物理地址与其对应,怎么办?此时CPU采取的策略是,对于这部分超出1M地址空间的部分,自动将其从物理0地址处开始映射。也就是说,系统计算实际物理地址时是按照对1M求模运算的方式进行的,在有些技术文献里你会看到这种技术被称之为wrap-around。还是通过一幅图来描述一下吧:

 

根据前面的讲解我们可以发现段基址有个特征,其低4位全为0,也就是说每个段的起始地址一定是16的整数倍,这是分段的一个基本原则。这样每个段的最小长度是16字节,而最大长度只能是64KB。这里我们可以计算一下,1MB的物理地址空间能划分成多少个段。

如果每个段的长度为16字节,这样1MB物理地址空间最多可以划分成64K个段;

如果每个段的长度为64KB,那么1MB的物理地址空间最多能划分成16个段。

8086这种分段基址虽然实现了寻址空间的提升,但是也带来一些问题:

1、同一个物理地址可以有多种表示方法。例如0x01C0:0x0000和0x0000:0x1C00所表示的物理地址都是0x01C00。

2、地址空间缺乏保护机制。对于每一个由段寄存器的内容确定的“基地址”,一个进程总是能够访问从此开始64KB的连续地址空间,而无法加以限制。另一方面,可以用来改变段寄存器内容的指令也不是什么“特权指令”,也就是说,通过改变段寄存器的内容,一个进程可以随心所欲地访问内存中的任何一个单元,而丝毫不受限制。不能对一个进程的内存访问加以限制,也就谈不上对其他进程以及系统本身的保护。与此相应,一个CPU如果缺乏对内存访问的限制,或者说保护,就谈不上什么内存管理,也就谈不上是现代意义上的中央处理器。

总结一下:8086和后来的80186,这种只能访问1MB地址空间的工作模式,我们将其称之为“实模式”。我的理解就是“实际地址模式”,因为通过段基址和段偏移算出来的地址,经过模1MB之后得出来的地址都是实际内存的物理地址。

由于8086的上述问题,1982年,Intel在80286的CPU里,首次引入的地址保护的概念。也就是说80286的CPU能够对内存及一些其他外围设备做硬件级的保护设置(实质上就是屏蔽一些地址的访问)。自从最初的x86微处理器规格以后,它对程序开发完全向下兼容,80286芯片被制作成启动时继承了以前版本芯片的特性,工作在实模式下,在这种模式下实际上是关闭了新的保护功能特性,因此能使以往的软件继续工作在新的芯片下。后续的x86处理器都是在计算机加电启动时都是工作在实模式下。

也就是说,在保护模式下,程序不能再随意的访问物理内存了,有些内存地址CPU做了明确的保护限制。

1985年80386的问世,使Intel完成了从16位到32位CPU的飞跃,这中间80286毫无疑问的就成了这次飞跃的跳板。80286的地址线已经达到24位,可寻址空间是16MB,但Intel当初设计80286时提出的目标是向下兼容,这也是Intel一贯的作风,正是这种作风为Intel后面设计80386时增添了几根儿烦恼丝。所以,在“实模式”下,80286所表现的行为和8086所表现的完全一样。

80386是32位CPU,也就是说它的ALU数据总线是32位,地址总线的位宽和CPU内部数据总线的位宽是一致的,都是32位,其寻址范围可达4GB。如果重新设计80386的架构,其结构应该相当简洁才对。但是80386却很遗憾的无法做到这一点,作为一个产品系列中的成员分子,80386必须继续维持“前辈”们的那些段寄存器,必须支持实模式,同时还要支持保护模式。可以看得出来,80386其实也不容易。

 

所以,Intel决定在80386的段寄存器(CS,DS,SS,ES)的基础上构筑保护模式,并且继续保留段寄存器为16位,同时又增添了两个段寄存器FS和GS。显然,为了实现保护模式,光是用段寄存器来确定一个基地址是不够的,至少还要有一个地址段的长度,并且还需要一些诸如访问权限之类的其他信息。所以,这里需要的是一个数据结构(这个数据结构就叫做“段描述符”,以后会看到),而并非一个单纯的基地址。对此, Intel设计人员的基本思路是:

在保护模式下改变段寄存器的功能,使其从一个单纯的段基址变成指向一个“段描述符”的指针。因此,当一个访存指令发出一个内存地址时, CPU按照下面过程实现从指令中的32位逻辑地址到32位线性地址,再到物理地址的转换:

1、首先根据指令的性质来确定该使用哪一个段寄存器,例如操作指令中的地址在代码段CS里,而数据指令中的地址在数据段DS里。这一点与实地址模式相同。

2、根据段寄存器里的内容,找到相应的“段描述符”结构。

3、然后,从“段描述符”里得到的才是段基址。

4、将指令中的地址作为偏移量,然后和段描述符结构中规定的段长度进行比较,看齐是否越界。

5、根据指令的性质和段描述符中的访问权限来确定当前指令操作是否越权。

6、最后才将指令中的地址作为偏移量,与段基址相加得到线性地址,或者叫虚拟地址。

7、最后根据线性地址算出实际的物理地址。

所以,实模式就是80186及其之前的CPU只能寻址1MB物理地址空间,且寻到的就是实实在在的物理地址的模式,用户程序想干啥干啥,无法无天;而保护模式,就是说用户成的程序,某些地址你是不能访问的,或者说是有限制性的访问,且你访问到的地址不再是物理地址了,而是一个虚拟的地址。这个虚拟地址要经过一系列算法处理,最终映射到实际物理地址单元里去。

现在运行在X86CPU上的主流操作系统,如Linux,FreeBSD,Windows95以后的版本以及OS/2等都是工作在保护模式下。一般情况下,处理器只有在上电启动,引导阶段,初始化系统时才会进入实模式,当实模式阶段的任务完成后,它就切换到了保护模式。当切换到保护模式后就很难再回到实模式了,几乎不可能。(注意我的用词)

不得不说的:

字符与字节

ASCII码:一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。一个二进制数字序列,在计算机中作为一个数字单元,一般为8位二进制数,换算为十进制。最小值0,最大值255。如一个ASCII码就是一个字节。
UTF-8编码:一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。
Unicode编码:一个英文等于两个字节,一个中文(含繁体)等于两个字节。
符号:英文标点占一个字节,中文标点占两个字节。举例:英文句号“.”占1个字节的大小,中文句号“。”占2个字节的大小。

不同数量级间

数据存储是以10进制表示,数据传输是以2进制表示的,所以1KB不等于1000B。
1KB=1024B;1MB=1024KB=1024×1024B。其中1024=210。
1B(byte,字节)= 8 bit

易混概念辨析

在计算机中,一串数码作为一个整体来处理或运算的,称为一个计算机字,简称字。字通常分为若干个字节(每个字节一般是8位)。在存储器中,通常每个单元存储一个字,因此每个字都是可以寻址的。字的长度用位数来表示。
在计算机的运算器、控制器中,通常都是以字为单位进行传送的。字出现在不同的地址其含义是不相同。例如,送往控制器去的字是指令,而送往运算器去的字就是一个数。

字长

计算机的每个字所包含的位数称为字长。根据计算机的不同,字长有固定的和可变的两种。固定字长,即字长度不论什么情况都是固定不变的;可变字长,则在一定范围内,其长度是可变的。
计算的字长是指它一次可处理的二进制数字的数目。计算机处理数据的速率,自然和它一次能加工的位数以及进行运算的快慢有关。如果一台计算机的字长是另一台计算机的两倍,即使两台计算机的速度相同,在相同的时间内,前者能做的工作是后者的两倍。
一般地,大型计算机的字长为32-64位,小型计算机为12-32位,而微型计算机为4-16位。字长是衡量计算机性能的一个重要因素。平时所说的64位CPU就是指字长64位,64位CPU也能安装32位操作系统

字节

字节是指一小组相邻的二进制数码。通常是8位作为一个字节。它是构成信息的一个小单位,并作为一个整体来参加操作,比字小,是构成字的单位。
在微型计算机中,通常用多少字节来表示存储器的存储容量。
例如,在C++的数据类型表示中,通常char为1个字节,int为4个字节,double为8个字节。
理解编码的关键,是要把字符的概念和字节的概念理解准确。这两个概念容易混淆,我们在此做一下区分:
概念描述 举例
字符 人们使用的记号,抽象意义上的一个符号。 ‘1‘, ‘中‘, ‘a‘, ‘$‘, ‘¥‘ ……
字节计算机中存储数据的单元,一个8位的二进制数,是一个很具体的存储空间。0x01, 0x45, 0xFA……

字符串

在内存中,如果“字符”是以ANSI编码形式存在的,一个字符可能使用一个字节或多个字节来表示,那么我们称这种字符串为ANSI字符串或者多字节字符串。如,"中文123" (占8字节,包括一个隐藏的\0)。

字符集

对于ANSI编码方式,存在不同的字符集(Charset)。同样的字节序列,在不同的字符集下表示的字符不一样。要正确解析一个ANSI字符串,还要选择正确的字符集,否则就可能导致所谓的乱码现象。不同语言版本的操作系统,都有一个默认的字符集。在不指定字符集的情况下,系统会使用此字符集来解析ANSI字符串。也就是说,如果我们在简体中文版的Windows下打开了一个由日文操作系统保存的ANSI文本文件(仅包含ANSI字符串的文本文件),我们看到的将是乱码。但是,如果我们使用Visual Studio之类的带编码选择的文本编辑器打开此文件,并且选择正确的字符集,我们将可以看到它的原貌。注意:简体中文字符集中的繁体字和繁体中文字符集中的繁体字,编码不一定相同(事实证明,似乎是完全不同)。
每个字符集都有一个唯一的编号,称为代码页(Code Page)。简体中文(GB2312)的代码页为936,而系统默认字符集的代码页为0,它表示根据系统的语言设置来选择一个合适的字符集。

Unicode

字符串在内存中,如果“字符”是以在Unicode中的序号存在的,那么我们称这种字符串为Unicode字符串或者宽字节字符串。在Unicode中,每个字符都占两个字节。如,"中文123"(占10字节)。
Unicode和ANSI的区别就相当于输入法内的“全角”和“半角”的区别。
由于不同ANSI编码所规定的标准是不相同的(字符集不同),因此,对于一个给定的多字节字符串,我们必须知道它采用的是哪一种字符集则,才能够知道它包含了哪些“字符”。而对于Unicode字符串来说,不管在什么环境下,它所代表的“字符”内容总是不变的。Unicode 有着统一的标准,它定义了世界上绝大多数的字符的编码,使得拉丁文、数字、简体中文、繁体中文、日文等都能以同一种编码方式保存。

数据类型

数据类型在数据结构中的定义是一个值的集合以及定义在这个值集上的一组操作。
变量是用来存储值的所在处,它们有名字和数据类型。变量的数据类型决定了如何将代表这些值的位存储到计算机的内存中。在声明变量时也可指定它的数据类型。所有变量都具有数据类型,以决定能够存储哪种数据。
数据类型包括原始类型、多元组、记录单元、代数数据类型、抽象数据类型、参考类型以及函数类型
具体分为:

数字型 

字节型

文本型 

还有复杂类型一般就是编程语言中的class,其实也就是对基本类型的组合

一切都准备好了!

加电之后,CPU硬件瞬间将CS的值设置为0xF000,IP的值为0xFFF0,这是一个纯硬件完成的操作,而这个地址刚好就是BIOS的入口地址,也就是说BIOS的第一条指令就在这个位置上,

BIOS

BIOS芯片中主要存放:
●自诊断程序:通过读取CMOSRAM中的内容识别硬件配置,并对其进行自检和初始化;
● CMOS设置程序:引导过程中,用特殊热键启动,进行设置后,存入CMOS RAM中;
● 系统自举装载程序:在自检成功后将磁盘相对0道0扇区上的引导程序装入内存,让其运行以装入DOS系统;
● 主要I/O设备的驱动程序和中断服务:由于BIOS直接和系统硬件资源打交道,因此总是针对某一类型的硬件系统,而各种硬件系统又各有不同,所以存在各种不同种类的BIOS,随着硬件技术的发展,同一种BIOS也先后出现了不同的版本,新版本的BIOS比起老版本来说,功能更强。
其实,就是说BIOS开始执行,先自检,然后构建中断服务和中断向量,最后加载操作系统文件。
加载过程分为三步,第一部分代码--引导程序 bootsect
第二部分代码--setup,是由引导程序加载到内存的,继续加载第三部分程序--系统模块,
setup模块主要作用就是将加载后的系统模块复制到0x00000,而这个位置上面是head程序,作用就是用自身的代码在自身的空间上创建了分页机制,具体创建了页目录表、页表、缓冲区、GDT、IDT并将自己覆盖。
最后调用操作系统的main函数

 

开机加电到系统打开究竟发生了什么?(1)