作为一个计算机人同时也面试过很多毕业生,不得不谈的就是计算机思维和对计算机整体的理解。计算机领域发展到现在语言、框架层出不穷,但是计算机思维和底层是不变的。我来谈谈我对计算机的理解,希望通过本文你可以构建计算机思维,底层并不困难,架构也并不高深,希望可以给你一些思考和帮助。
导读:《大学》里面有一句话'物有本末,事有终始。知所先后,则近道矣' 。什么意思呢,每样东西都有根有枝,每件事情都有始有终。明白了这本末始终的道理,就接近事物发展的规律了。那么我们思考计算机有没有本末始终的规律,答案是有的,本文将通过计算机系统底层(组成原理、体系结构、汇编原理、汇编语言、intel开发手册、CSAPP、C++等)的一些书籍,帮助我们正确、高效掌握计算机思维。
易有太极,太极生两仪
易经中有这样一句话'易有太极,太极生两仪 ',理解为所谓太极即是阐明宇宙从无极而到太极,以至万物化生的过程。计算机也不例外,经历从无到有从1936年艾伦・图灵提出的一种抽象的计算模型,将人们进行数学运算的过程进行抽象,由一个虚拟的机器替代人类进行数学运算,我们称之为图灵机。到冯诺依曼提出计算机的体系结构,从此往后不管是最原始的、还是最先进的计算机,使用的仍然是冯·诺依曼体系 最初设计的 计算机体系结构。
开天辟地,太极生两仪 - 从图灵机到冯诺依曼体系
那么冯诺依曼体系就引出了计算机体系结构,我们翻开《计算机体系结构精髓》一书中描述了,这个模块应该去了解的知识有数字逻辑(电压、晶体管等等)、处理器的基本原理、操作数与指令、大量篇幅描述了CPU、存储器和总线等,那我们总结就是冯诺依曼体系中的4大组成(运算器、控制器、存储器和输入输出)。
二生三,三生万物 - 冯诺依曼体系与计算机体系结构
既然如此我们看下现在计算机都有哪些组成,宏观上也就是 计算机的视角,我们打开电脑能考到的就是键盘鼠标这类的外设(输入输出),计算机内部是CPU(运算器+控制器)、内存和磁盘(存储器);微观上,CPU内部视角,有寄存器(MU)和主存、运算单元(AU)、控制单元(CU)几个模块,这些模块顾名思义寄存器=存储器、主存=输入输出(因为要与CPU外部交互)、运算单元=运算器、控制单元=控制器。
现代计算机组成
这里仅仅是理解计算机和CPU与冯诺依曼体系的关系,如果理解困难,我们可以换个角度,在我们进行软件开发时的MVC架构,View=输入输出、Controller=运算器+控制器、Model=存储器,这样是否可以理解冯·诺依曼体系的神器之处了呢?
冯诺依曼体系与现代计算机、CPU的关系
至此我们算是初步浅识了计算机的整个底层,并且知道了CPU是计算机的核心(运算+控制),因为我们并没有深入到CPU(寄存器)、内存(数据内存、指令内存)等等。
物有本末,事有终始。知所先后,则近道矣
作为一名程序员,光有底层的逻辑还不够,还要建立完整的代码到编译到执行的思维,也就是所谓知其先后,我们写的代码究竟是如何操作上述硬件的?我们知道根据冯诺依曼体系,计算机只能识别 0 1 的代码诸如00000101,所以我们开始往上推,人类是不可能去写这个东西的,人类只需要将0101的代码对应上即可,但是不管是熟知C语言还是Java都不能直接变成0101的代码,那么一定是中间有一系列什么东西做了将高级语言转化为机器语言的步骤(编译器->汇编器)。
于是出现了ISA汇编指令集,在《计算机组成原理》中有详细讨论,这里我们理解为,将驱动控制CPU的代码封装成特定的指令集,由CPU厂商去做代码的开发,用来操作控制单元,将存储单元中的数据放入计算单元,将计算结果返回给存储单元。
ISA操作CPU的过程
那么有了汇编指令集,诸如Java、PyThon、Go语言等还是不能直接操作ISA指令集的,我们还需要一个能把左右语言 编译 成统一的汇编语言(编译器),再去调用汇编代码的过程。将高级语言翻译成汇编语言或机器语言的过程,就是《编译原理》了,然后汇编语言操作CPU,去控制其他硬件完成操作(操作CPU的控制单元、控制存储单元,运算单元对缓存的数据进行操作)。
编译原理
小结: 至此我们得到了一个所有语言共同的编译运行模型和计算机体系模型,简单构建了ISA汇编指令集与汇编原理的了解。
从代码到硬件的过程
君子之道,辟如行远必自迩,辟如登高必自卑
《中庸》里 '君子之道,辟如行远必自迩,辟如登高必自卑 ',翻译过来就是 君子的中庸之道,就好像是走远路必须从近处开始,就如同是登高必须从低处开始。 经过上述两讲我们知道了计算机体系结构和代码编译的过程,但是都仅仅是最简单的了解,现在我们开始登山!
一、惟兹何功,孰初作之?——计算机组成原理-ISA和CPU处理指令原理
上述说到控制计算机,需要控制CPU,而ISA操作CPU,从而操作其他的一切,ISA 是一个集合,那么就一定有多个内容,我可以 控制CPU干什么呢?所以机器指令会存在很多个 -> 我怎么识别谁是谁 ?-> 所以机器码根据CPU厂商给定的规则(ISA),汇编器 按照这个规则将01放入规则指定的位置即可,实际上不管指令多么神奇,终究还是数据是代码,就需要容器去操作需要内存去存储。下图CPU内存模型。
CPU内存模型
指令操作的数据最终都会在内存,所以按照功能将内存存放数据的区域切割即可:指令段私有的数据(栈)+指令段共享的数据(堆) -> 指令段可能又有多个,那么就代表我可以 call 多个指令 -> (堆栈)数据结构来表示这种存储关系。其中,指令流动态分配的私有数据,分为栈顶栈底寄存器,由于内存是连续的且有高低之分,按照栈内存的增长方向可分配内存。指令流动态分配的共享数据称为堆内存。在汇编代码的 .data段定义的数据是数据内存。指令内存,存放指令而又由于指令中可以写入数据(mov 1,寄存器),所以又存放了数据,只不过这个数据嵌在指令中。
说了这么多,是想告诉读者,每个指令流都有自己的栈帧,栈帧里面保存当前指令流操作的数据,当使用cell指令,转移指令流后。只需要保存上一个栈帧的栈底。所以我们的程序中,栈内存 就可以理解为多个指令流私有的数据;堆内存 是多个指令流共享 的数据(动态数据);代码段内存 是 保存程序的指令流;数据内存是保存程序数据(编译时存在的数据静态数据)。
由此可见底层CPU、OS与JVM的内存模型是息息相关的,这里初步构件对内存模型的了解,对于CPU以及寄存器的了解远不止于此,寄存器、高速缓存、intel手册等等笔者会放在后续博文中详细介绍。
二、冥昭瞢暗,谁能极之?——再探编译原理与汇编语言
上述说道编译原理是将高级语言翻译成汇编语言或机器语言的过程,编译的过程有一个三明治(三层)架构——前端、中间结构、后端。前端是由不同需要接入当前编译器的前端开发者进行开发,来适配多门语言,中间结构会产生一个叫IR的中间语言解耦,后端由不同需要接入当前编译器的后端开发进行开发,来适配多门目标机器语言(ISA)。中间的细节类似于我们做英语翻译分析语言的语法语义,生成中间语言,最后转化为目标语言,可以参考Java语言的编译过程,java源代码->通过javac指令编译成字节码,这个字节码就是相当于 IR中间语言。这里面JIT(即时编译器)实现了编译原理的前端和后端,只不过在内存中动态对语言进行编译;用一门语言(IR)对其他语言进行解释,执行逻辑是解释器;编译器是具有前端和后端的整套逻辑,将一个语言(源语言)编译为目标语言。
编译原理与Javac编译
了解了编译过程之后,编译的目标语言我们称为汇编语言,汇编语言有两个编码规则,一个是AT&T,指令形如mov eax 1;另一个是intel公司,指令形如mov 1 eax;(颠倒了)。高级语言会抽象为汇编语言;我们是否可以推理出来,指令段 相当于函数;指令段之间的调用(call指令)类似于方法之间的调用(函数调用);而操作单元的大小抽象为类型系统(基础类型 byte short int的内存单元为2^n(n>=0),其他类型:结构体(不同规则的内存单元)、数组(相同类型的多个内存单元));数据地址操作抽象为指针操作。
指令段1想要指令段2去修改内存单元中的值的过程
总结
本篇文章意在带你建立计算机底层架构的思维和逻辑,从图灵机、冯诺依曼体系反推 计算机体系结构 和 ISA指令集架构,从高级语言向下递推编译器、汇编语言、汇编器 、到机器语言,ISA指令集 闭环。希望通过本文你可以初步构建计算机思维,通过已知可以继续向更深(寄存器、链接器、解释器等等)、更高(MVC、DDD、分布式、云原生等等)的层面延伸推理。
高级语言与计算机硬件的关联
求点赞、收藏、关注、转发 ~
感谢支持,下期更精彩~