一、计算机的前世今生
1801年,法国人杰卡德设计的自动提花编织机,通过打孔卡片上存储的信息织就漂亮的花纹。这种机械装置是将“思想”存储在“介质”上的最初体现。
1836年,英国科学家巴贝奇研究构造了差分机。差分机是一个机械式的通过齿轮运转来求值多项式的机器。英国人Ada曾论证了如何通过差分机运算伯努利数,而被认为是世界上第一个程序员。差分机拥有存储区、运算区以及控制区,能够读取指令,是计算机的雏形。
19世纪80年代,美国人霍列瑞斯发明了穿孔卡片机。穿孔卡片及主要用于数据的收集和整理,它能通过卡片的位置对卡片进行分组,成功帮助当时美国人口普查减少了大量工作时间。穿孔卡片通过在一张卡片上不同位置进行打孔来实现字符编码。比如在第一行第一列打一个孔可能代表代表数字1,在第零行第一列打一个孔,同时在第一行第一列打一个孔,可能代表字母A。霍列瑞斯建立的公司后来被收购,收购者在穿孔卡片机的生意上十分成功,后来发展成IBM。
1937年诞生了世界上第一台电子计算机ABC(阿塔纳索夫·贝瑞计算机),主要用来求解方程。ABC的缺陷是不可编程,因为它没有实现存储结构。但是ABC提出了很多具有重大意义的设计,比如二进制(之前的都是十进制的)表示法,使用电子器件运算(之前都是机械式)等。
1946年诞生了世界第一台通用电子计算机ENIAC(电子数值积分计算机,Electronic Numerical Integrator And Computer)。它是图灵完全的,可编程的。
与ENIAC采用十进制不同,EDVAC(离散变量自动电子计算机,Electronic Discrete Variable Automatic Computer)采用二进制,是第一台冯·诺依曼结构计算机。
冯·诺依曼结构将计算机分成五个组成部分:输入设备、输出设备、存储器、控制器、运算器。这一结构沿用至今。在此之前,计算机都是存储一组固定的程序,只能实现特定功能,就好像一个计算器就不能用来看视频。而冯·诺依曼结构的电脑是存储程序型电脑,借由创建一组指令集结构,并将所谓运算转化成一系列指令的运行,计算机能够动态决定其运算的内容。指令本身也是一种静态数据。
早期人们曾使用汇编语言编程,一种汇编语言对应某一套机器指令集。汇编语言程序的不仅难于编写,而且难于移植。显然为每个平台都编写一套程序的开销要远大于编写一个源程序,然后实现针对目标平台的编译器的开销。编译器的编译过程就是将高级语言(对应汇编语言等低级语言)编译成计算机能执行的机器语言。有些编译过程不是输出机器语言,如C#源程序被编译成中间语言IL(Intermediate Language),java源程序被编译成字节码,最终由CLR或JVM去执行,从而实现跨平台。
常用计算机介绍
个人:
平板、手机、笔记本、PC等
服务器:
PC服务器:基于Intel ×86架构,Linux或Windows Server,开放、通用,较便宜
小型机:高可靠,高可用,高服务性,价格较昂贵
大型机:银行、金融等关键领域核心数据,非常贵
二、计算机的组成和工作原理
计算机的硬件组成部分有主板、CPU、内存、显卡、声卡、网卡和键盘鼠标等。
IO设备不直接与CPU打交道,每个IO设备都与总线连接,所有设备之间的通信通过总线来完成。
CPU
CPU由三大部件组成,分别是寄存器、控制器和运算器。数据和程序都存储在内存里。首先PC指向下一条需要执行的指令,取出指令放在指令寄存器。接下来就是分析和执行指令。指令需要的数据被放在数据寄存器,而运算器执行实际运算。
RAM随机访问存储器
RAM分为Static Ram和Dynamic Ram。静态Ram速度快,容量小,价格较贵,主要用于CPU和主存之间的缓存。动态Ram速度相对较慢,容量大,主要用于主存。
硬盘
磁盘由磁头、盘片、主轴等组成。
当磁头不动时,磁头在旋转的盘片上划出的一个圆弧就是一个磁道。每个磁道被等分为若干区段,叫做扇区。每个磁盘被等分为若干个扇面。
每个盘片上下两面都有读写头。主轴带动盘片快速旋转,磁头沿着盘片的半径移动来切换磁道。同半径的磁道在垂直方向组成了柱面。
注意硬盘不是随机访问,若要访问某处的数据,需要先寻道,再读取。
计算机工作原理
下图模拟了CPU执行某条指令的过程。CPU依次读取指令,分析指令,根据指令执行一些数据操作,最后执行运算。“Move 1000 EAX”指令表示将内存地址为1000的数据取出放到EAX寄存器里。“ADD EAX EBX”表示把EAX寄存器和EBX寄存器里的数相加。
注意数据和指令以二进制形式不加区分的存储在内存里。也就是说,即使你把数据当指令传给CPU,CPU也没办法区分,只能试图分析该数据的二进制可能代表的指令。
指令和流水线
指令分为操作码和操作数。操作码比如Move,Add等。后面的则是操作数,可能有一位、两位或三位。指令的执行分为取指令、译码和执行三步。每一步都需要需要一个时钟周期(一个时钟周期是计算机最小的时间单位,在一个时钟周期内,计算机仅能完成一个基本动作)。
当译码时,取指令的电路就空闲了,所以它可以取下一条指令。这样当译码完成后,下一条指令也就完成取指,每一个时钟周期都能够执行一条指令,而不是需要三个时钟周期才能执行一条指令。
现代CPU建立了12级、18级甚至更多层级的流水线,一个更多级的流水线意味着同一时刻有更多的指令被处理。为了能够更快的执行这些指令,CPU使用了乱序执行部件,指令被同时执行,而且是乱序执行,当前指令在等待数据的时候下一条指令可以优先被执行。但是对程序来说,乱序执行是不可见的,每一条指令退出执行的顺序同它们在指令队列的顺序一致。虽然执行过程是乱序的,但是结果同8086的老CPU没什么不同,仍然是顺序的。乱序执行部件还包括了猜测执行等功能,来处理诸如分支跳转之类的问题。
三、速度不匹配问题
从上图可以看出,如果CPU做一件事需要一秒(实际是0.3ns,大约三个时钟周期),那么缓存大约慢数倍到数十倍,主存大约慢几百倍,闪存和固态相对普通硬盘还是比较快的,但是相对于主存要慢的多得多。这就是速度不匹配问题,CPU太快,其它硬件太慢,如何充分发挥CPU的速度优势成为重大问题。
如何解决?
同步—>异步
不等待耗时的操作,如IO操作,而是先去做别的事情,等耗时操作完成后通知CPU来处理,如直接内存访问技术(DMA)。
顺序—>并发
把时间分成很多小的时间片,不停的在不同的程序之间切换,每个程序分配一个时间片来执行。由于速度够快,因此用户察觉不到这一过程,这就是分时操作系统的基本原理。
如果顺序执行,那当前执行程序不执行完成,无法执行下一程序。同一CPU同时执行多个程序叫并发,多个CPU每个CPU同时执行不同程序叫并行。
增加中间层
局部性原理
时间局部性:程序中的某条指令一旦执行,则不久之后该指令可能再次执行;如果某条数据被访问,则不久之后该数据可能再次被访问。
空间局部性:一旦程序访问了某个存储单元,则不久之后,其附近的存储单元也将被访问。
根据局部性原理,可以增加中间层——缓存。在缓存中,存放一些热点代码或数据。CPU执行指令时将先在缓存中查找数据,如果找不到再到内存中查找。缓存概念应用广泛,比如Web应用的缓存主要是数据库,因此应该把常用的数据缓存到内存中。
存储器的层次结构
越往上的存储器速度越快,价格越昂贵,存储空间越小。越往下的存储设备速度越慢,价格越便宜,存储空间也越大。
四、按下电源后发生了什么
>主板开始供电
>BIOS启动POST(Power On Self Test)
>POST完毕后,会将BIOS中的程序加载到内存尾部
>CPU从某个特定内存地址查找第一条指令,这是一条跳转指令,跳转到内存中BIOS程序位置,开始执行BIOS程序
>按照CMOS中设定的顺序,查找可以启动的设备(bootable device)
>读取设备的MBR(主引导记录,该设备的前512个字节)
MBR中有一个分区表,该分区表指定一个分区为活动分区,找到活动分区,就可以装载操作系统了。或者读取MBR前446字节,由Boot Loader它来装载操作系统。
参考:计算机是如何启动的?
>将启动程序(boot loader)加载到内存某处,开始执行
>操作系统被载入内存,开始接管电脑
五、HelloWorld的一生
>程序写好以后编译成二进制文件存放在硬盘里
>执行程序,操作系统将程序从硬盘加载到内存
>CPU开始执行main程序里的指令
>指令将hello world字符从内存复制到寄存器
>指令将hello world字符从寄存器复制到显示设备,显示在屏幕上