2020-Architecture-1-1 从计算机原理看架构设计


主要内容概要

主要讲述的是计算机原理中最基本的运算设备àCPU以及与CPU配合的存储体系。
与此同时讲述这些内容在实际的项目使用过程中到底会对软件的设计以及编码人员造成什么影响。

  • CPU是什么?
  • 为什么会出现不同的系统不兼容的情况?
  • CPU的运算依赖于什么?
  • 存储体系的几大模块
  • 存储体系在设计系统与开发系统时的重要性
  • 缓存Cahce,缓冲Buffer你能分清两者的区别嘛?
  • 在实际的项目中,缓存与缓冲是怎么使用的?

CPU是什么

我们都知道的定义是这样的:
CPU又称中央处理器,它的英文全称为:Central P[rocessing Unit,但同时如果你听到:Central Processor或者Main Processor往往同样也指的是CPU。

普通程序员开发的程序,其实绝大多数指的就是对中央处理器也就是CPU的编程。
这句话也可能会被面试人员反着描述为,计算机的可编程性主要是指对中央处理器的编程。
在最初的时候,大约是1970年代以前,中央处理器由多个独立单元构成,后来才发展成集成电路,这些高度收缩的组件就是所谓的:微处理器

注意: ENIAC(Electronic Numerical Integrator And Computer)是世界上第一台“电子存储式可编程计算机”,但是由于他们也存在计算功能,而CPU的标准定义为:执行软件(计算机程序)的设备所以,ENIAC理论上不存在标准的CPU,但是最早于存储程序型计算机一通登场的设备是可以被称作CPU的,也就是说:CPU并不一定是微型的,早起的同步CPU是非常大的。直到1950~60年代的晶体管CPU时,CPU才慢慢变小。不再以体积庞大,不可靠与易碎的开关组件(继电器/真空管)组成或建造。

参考来自于维基百科,与哈佛计算机原理书籍,国内的一些小众参考资料与翻译是错误的,一定要注意。CPU并不小。尤其是百度百科,它所提供的参考资料只提及了”微”,但是万物相对论,没有相对何来形容词?

CPU的主要运作原理很简单。不论其外观,都是执行存储与被称为”程序”里的一系列指令。所以经常说,计算机是最愚蠢的傻子,没有码农,他们就是一坨铁。
在冯诺依曼架构中,程序以一系列数字存储在计算机存储器中。
而只要是冯诺依曼机,CPU的运作原理就可以看成四个阶段:提取,解码,执行,回写。

提取: 从程序内存中检索指令(程序内存,指的可不是你程序里面定义了一个int的这个内存,而是存储程序的内存),由程序计数器指定程序存储器的位置,计数器保存供识别当前程序位置的数值(原始资料原文)。

拿人话说:程序计数器记录了CPU在当前程序里的踪迹(你跑哪去了?)提取指令后,根据指令式等内容的长度增加存储器单元。指令的提取常常必须从相对较慢的存储器查找,导致CPU等候指令的送入。(关键问题是:CPU指着整个计算机存储体系温柔的说道,我不是指你啦外存,我是说在坐的各位,都是垃圾)

上面是理工科的话,再拿麻瓜的话来说:
程序计数器其实非常傻,他记录的是内存的地址,而不是指令。这时候就尴尬了,他的增长取决于指令在内存中所占的单位数,在固定长度的指令ISA(微处理器的指令集架构Instruction Set Architecture)中,每个指令所占用的内存单位是相同的(简单说就是它傻到只能某几个某几个数,而不能一个一个数)。例如:32位的ISA固定长度指令使用8位内存单位。而且每次将增加4个PC单位,也就是32Bit。而类似于现在咱们大家常用的X86,虽然也是默认指代32位,然而这之间有一些区别。X86的CPU其实是可以处理16位与8位的,他的PC在内存中的增长量取决于最后一个指令的长度。而更复杂的的CPU中,最后一个指令的运行不一定会导致PC单位的增长,特别是大量数据传输和超标量体系结构中。
所以如果要讨论指令集,那么你必须讨论清楚所有的细节,否则,你们没人说错,但是永远对不了。

上面说了一堆的CPU提取动作,下来来看看后面几个顺理成章的事情。
解码:CPU从存储器提取到指令,他需要被解码才能成为有意义的片段,根据不同CPU的指令集会变成对应的指令。一部分为运算码,其他的是必要信息。或者是寄存器/存储器地址,以地址模式决定。
MSIL就是微软的汇编语言。看到上面这段,是不是特别的熟悉呢?

执行:执行是最容易理解的地方,根据不同的指令与对应的其他信息,来进行简单的运算与逻辑运算,例如加法/位操作。
其实CPU有多愚蠢呢?他也就只会:与或非异或。
是的他就是如此的愚蠢。

最后的回写:则更容易理解,由于CPU是专门负责计算的,总不能算完了还放在CPU里面?那不行。
所以,它会先回写/写回到缓冲区,然后同步给主存,然后就没然后了,程序该干啥干啥。

为什么会出现不同的系统不兼容的情况?

根据上面CPU的工作原理,其实非常容易看出来一个事情。
那就是:指令集问题怎么办?你能看到英特尔与AMD还能兼容,那是因为他们都是X86架构体系的。

如果你把他们对比成:X86与ARM,那就好玩了。
甚至于说X86与X64有时候都不兼容。
如果你工作足够早,你应该听说过X86跟X64是完全不能跑的,但是后面似乎成了32位的能在64下跑,而64不能在32上跑。
而这个事情其实只有一个原因:X64原始名字应该是X86-64,也就是X86指令集的64位扩展。

系统级别的不兼容其实更容易理解。
由于每一个系统的API都是不同的,普通开发编写的程序都是在Kernal之上的,那么与其说你是面向CPU,倒不如说是面向系统的API编程,那么LINUX与windows的API一日不相同,则永远不可能出现完全兼容。
甚至每个系统的策略也不同,例如WIN7开始不允许进行MAC层编程,而LINUX可以,这也就是为什么工控软件为什么不会出现windows 的原因。我没有IP,你还不允许我使用MAC层编程,怎么着?让我们集体跳楼嘛?

完整版如下:IA-32(X86 I386 标准32位),X86-64 也就是64位,其实是一个扩展,但是它兼容16位与32位的X86架构 也有叫IA-32E EM64T CT或者直接Intel 64的,其实都是一回事情,其实是AMD1999年提出的。
Intel凑不要脸。

存储体系的几大模块

有些人说:存储体系分为4个,有些人说分为6个,有些人说3个。
其实大家都对,只是大家的理解方式不同。
完整的完全体是6个。如下:

  • A1 寄存器(32位处理器的话每个寄存器是32位X86架构下有16个寄存器)
  • B2 第一级高速缓存
  • B3 第二级高速缓存
  • B4 第三级高速缓存
  • B5 主存(内存条)
  • C6 外存

i9的缓存大小,二级缓存是2MB,三级缓存是16MB,所以说栈肯定不是在这里。
任何堆变量什么的都是在内存条这。
CPU的输出速度极快,主存来不及存,所以CPU先会经过寄存器,一级,二级,三级高速缓存,再到主存;因此,读写速度不一致的时候,需要用缓存。
4个的人认定的是大写英文字母版本,也就是寄存器,高速缓存,主存,外存。
6个的人认定是完全版,也就是数字版本。
而3个的人说的是能够使用软件去想办法利用的资源比较偏向于软件。
注意:软件不是不能利用寄存器,只是正常情况下尤其是类似编写。NET或JAVA等高级语言的人员,是没有意义的。
作为架构师,你需要兼容不同的用户与描述方。

存储体系在设计系统与开发系统时的重要性

上面有这样一句话:
CPU说:不不不,我不是说外存你,我是说,在座的各位,都是垃圾。
其实这句话的含义你仔细理解一下,它完全代表了软件的架构模式,而且是100%匹配。
你的For循环或者任何一段运算速度是绝对快于内存的访问速度的,例如redis的存储与获取。
这时候你如果不想让Redis崩溃掉,你唯一的选择是什么?对的,你给本地加缓存
这个场景其实与CPUß<–>主存之间的工作模式是完全一致的。
由于主存与CPU之间的性能差异,又不可能说让CPU去等待主存将内容完全读取走,这才出来了1234级缓冲,而如果你对硬件稍微熟悉的话,你会发现,每一层缓冲区数量普通情况下是越来越大的。

同样的情况,你来合计一下,这次是主存与外存了,那么对标软件中的哪个位置呢?
你在开发软件的时候或者设计架构的时候什么时候其实就是硬件架构的软件实现呢?
咱们来一起落地这个场景吧。
500W高并发的时候,SLB的转发一定要有策略,不能随机,随机转发最后一定会将瓶颈落在Redis上,可以考虑在主存做一级缓存(35s),在被分发的服务端本地做二级缓存(1.3min),在Redis做三级缓存(4min)。从一级击穿后到二级,二级击穿就到三级,三级也被击穿就直接到数据库。
因为SLB的分发策略,用户A进来后永远在1号服务器,数据都在一条链路上,不存在不同步。

缓存Cahce,缓冲Buffer两者的区别

大家伙经常说:所谓概念就是概念,没啥用。我背过就好。
那你应该如何去阐释,作业本长一样,习题内容一样。考的试一样,老师一样,时间一样精确到皮秒。的你跟你班上的学霸,是如何出现越来越打的差异的?

这就是,你跟你的学霸朋友,谁是缓存了,谁是缓冲了的问题。
从存储可靠度来说,这两者都不可靠,你在设计软件时,不能认为缓存是可靠的,更不能认为缓冲是可靠的。
但是如果非要在这两者之间选择:相比较于缓冲,你更需要的是缓存。
例如刚才的例子:
学霸是缓存下来,回去消化,而你是缓冲下来,下课就倒掉了。这是个很尴尬的场景。

仔细斟酌上述例子,你能发现有什么可落地的场景嘛?
注意一个细节:缓存是不可复用的,而缓冲是必须复用的。对了,做UI的人肯定听说过一个叫做双重缓冲的技术。这个又是什么鬼?

在实际的项目中缓存与缓冲该如何使用?

  1. 视频处理项目的缓冲与缓存(视频正在加载缓冲中。。。)
  2. 在线直播项目的缓冲与缓存(服务器端缓存,给出去的东西缓冲)
  3. 2B项目的缓冲与缓存
  4. 金融项目的缓冲与缓存(用户缓存,交易缓冲)
  5. 引擎类项目的缓冲与缓存(非常高频繁的使用核心业务,避免脏读,例如百度)
  6. 通信项目的缓冲与缓存(tcp接受到的数据放缓冲里,转码后的数据放缓存里,缓冲区不能一直被占着,只能放缓存里,缓冲区不能有其他业务,例如缓冲区只有一包,读到十个包之后组成一个数据,这时候是放在缓存里的)

什么时候定义struct,什么时候定义class?缓冲使用结构体,缓存使用类。API收到一个参数,收到即扔,用结构体。视频采集卡里面一般都是使用结构体。
什么东西贼快,什么东西用完就扔,就是缓冲。
Cpu 玩栈,很快;堆一般都不是,稍慢。

影响和结论

  • 计算机的基本运作过程
  • 我们该如何利用计算机的这个特点来设计软件与搭建框架

PS: 如何确认自己到底是会了一个东西还是只是背过了一个概念,反问自己3个为什么即可。
例如
数据一致性是什么呢?
XXXXXX
那么XXXXX又是为什么呢?
YYYYYY
那么YYYY又是为什么呢?
ZZZZZ
那么ZZZZ又是为什么呢?
QQQQQ


文章作者: Chaoqiang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Chaoqiang !
评论
 上一篇
TypeScript Basic Tutorial (7) TypeScript Basic Tutorial (7)
接口接口的作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用。接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这
下一篇 
TypeScript Basic Tutorial (6) TypeScript Basic Tutorial (6)
静态属性 静态方法: //ES5中的静态方法 静态属性 function Person(){ this.run1=function(){ } } Person.name='哈哈哈'; Person.run2=function(){
  目录