前言
准备复习软考啦,虽然现在河南(来河南读大学的福建人)的考试时间还没定下来,不过根据以往的惯例是在五月份开考,现在备考刚刚好,既不会太晚导致没学完,也不会太早导致之前学了考前又忘了。
网上想找找软考复习资料,开头都说的好听,最后都是想坑害我的钱包要我报班的,找到的很少是纯分享干货的,于是我干脆买本书自己啃,再结合软考通做题,然后看看b站的免费视频自己学。
这边开个备战软考的系列来记录一下,也算是巩固知识了。
处理器结构
处理器结构分为了冯诺依曼(普林斯顿)结构和哈佛结构。我们常用的STM32,ESP32都是哈佛结构的。
它们的差别就在于程序存储器和数据存储器是否分开。
冯诺依曼(普林斯顿)结构是合在一起的,也就是说我CPU在同一时间只能访问程序存储器或者数据存储器,这样效率是比较慢的,因此大佬们又设计出了别的处理器结构。
哈佛结构将程序存储器与数据存储器分开来了,就是这么的朴实无华,这一个小小的改变就使得我们的CPU可以同时访问程序存储器和数据存储器了,效率是大大的提升。
关于软考我们只需要知道哈佛结构是将程序存储器和数据存储器分开了,而冯诺依曼(普林斯顿)结构是将二者合起来了就可以。
软考之外我们再思考思考,既然哈佛结构效率高为什么没把冯诺依曼结构完全代替呢?
在上图中我们看到的仅仅是哈佛结构将俩存储器分开再多加一套地址线和数据线而已,但是现实中会复杂很多,也就是说成本会提升很多。因此现实中绝大多数计算机的程序和数据共享同一个地址空间,但缓存是分开的,也可以理解成CPU外,使用的是冯诺依曼结构,CPU内用的是哈佛结构。
我在知乎上找到一种说法。
其实冯诺依曼的核心思想是指令和数据都能从存储器中读出…至于是存一块还是分开,这并不是什么本质差别
计算机硬件组成
我们计算机的硬件由五个部分组成:运算器、控制器、存储器、输入设备和输出设备。
其中运算器+控制器+寄存器组+内部总线构成了CPU(Central Processing Unit,中央处理单元)。
CPU的功能是程序控制、操作控制、时间控制、数据处理等。
CPU中运算器又是由ALU(Arithmetic and Logic Unit,算术逻辑单元),AC(Accumulator,累加寄存器),DR(Data Register,数据缓冲寄存器),PSW(Program Status Word,状态条件寄存器)等组成的。
ALU实现对数据的算术和逻辑运算。
AC是运算结果或者源操作数的存放区。
DR暂存内存的指令或数据。
PSW保存指令运行结果的条件码。
控制器由IR(Instruction Regiter,指令寄存器),PC(Program Counter,程序计数器),AR(Address Register,地址寄存器),ID(Instruction Decoder,指令译码器)等组成。
IR存放的是CPU当前正在执行的指令。
PC存放的是下一条指令执行的地址,执行完指令后自动+1。
AR存放CPU当前正在访问的内存地址。
ID人如其名,就是分析指令操作码的。
上面内容如果学习过汇编的小伙伴应该都懂,看不懂的小伙伴只需要看看混个眼熟即可。
数据的表示
进制
计算机只认1和0(开和关两种状态),因此计算机内部存储数据用的是二进制。
不过我们在编程的时候基本不用二进制而是八进制,十进制,十六进制。
所以我们来了解一下这四种进制之间的转换。
首先是二进制转十进制。从右开始,每位的权值从1开始,每往左一位,权值乘2。
接下来是十进制转二进制,这个我们需要不断地将10进制数除2,将每次的余数取出就是二进制数了。
然后是二进制转八进制和二进制转十六进制。
如果我们能懂不同进制中每位数字的含义的话,那么是可以很容易地想到可以把二进制“浓缩”成八进制和十六进制的。
二进制每位的权重是以2为基数,八进制每位的权重是以8为基数,十六进制每位的权重是以16为基数。
因为2^3=8,2^4=16
所以每三位二进制数可以“浓缩”成一位八进制,每四位二进制可以“浓缩”成十六进制。
然后是十进制转八进制和十六进制,和转二进制类似,就是把除数从2改为8和16即可。
不过一般情况下我们会把十进制先转成二进制再转成八进制或是十六进制。
编码
计算机存储数据的形式是二进制,但是还进行了一些编码,一般是以补码的形式存储的。
一共有原码、反码、补码、移码这几种编码方式。
原码就是一个数的二进制形式,不过最高位拿来当符号位了,符号位为0时表示这个数为正数,符号位为1时表示这个数为负数。
正数的原码就是反码和补码,而负数的反码是原码中除了符号位之外全部取反得到的,补码则是在反码的基础上+1(符号位参与进位)。
无论是正数还是负数,移码都是在补码的基础上将符号位翻转。
那么为什么需要有这么多编码格式呢。
因为在计算机中做加法比做减法容易,如果是使用反码的话,那么减法也可以用加法替代,但是有一个小小的问题,那就是表示0的方式有两种,即0000B和1111B,它们都是0的意思,这样有一点点小浪费,于是就有了补码,这样0就只有一种表示形式即0000B了。
而移码通常用于浮点数表示中的阶码编码,这样可以避免对负阶码的直接处理。
浮点数
浮点数的表示会比整数复杂一些。
一段二进制数分为了阶符,阶码,数符,尾数四个部分。从我下面给的颜色可以看出,实际上我们可以浓缩成两个部分,因为阶符是阶码的符号位(0为正1为负),数符是尾数的符号位。
浮点数的表示方式接近于我们的科学计数法。
比如101.101=0.101101*(2^3),因为我们每位的权重是以2为基数的,因此小数点每左移一位,都需要乘2,移了三次,那么就需要乘2^3。
校验码
校验码有很多,不过一般只考三种:奇偶校验码,循环冗余校验码(CRC,Cyclic Redundancy Check),海明码。
奇偶校验码
这个很简单,就是数一下数据中(二进制)有几个1,如果我们选择的是奇校验,那么需要保证数据中有奇数个的1 ,选择的偶校验就保证数据中有偶数个1。
因为我们不能修改数据,那么我们就在数据的最前面或者是最后面加一个校验位,通过这个校验位来保持整个数据中1的个数。
这样还有一个问题,那就是如果我一次性让偶数个数据出错了,那么奇偶校验是校验不出的,只能校验出出现奇数个数据位出错的情况。
于是就有了下面的校验码。
循环冗余校验码
我们要使用循环冗余校验码需要先约定好一个多项式G(x),简单来说这个多项式的形式是这样的
y*X^n+……+y*X^1+y*X^0
其中y为0或1,并且规定这个多项式的最高位和最低位必须是1,因此X^0的y必是1。
接下来我以下面这个多项式为例来讲解一下循环冗余校验码。
G(X)=X^4+X+1
知道了多项式之后我们需要提取出一段二进制数,从高阶的y开始提取,以上面为例,那么我们提取出的就是10011。
假设需要校验的原始数据是10110,那么我们需要在原始数据后追加多项式的最高阶数个的0,我们上面多项式最高项是X^4,因此我们给原始数据加上4个0,也就是变成了10110 0000。
接下来我们拿多项式提取出的二进制数去除以修改过的原始数据,用的是模2除法,也就是不进位不借位。
最终得到余数1111,这个就是我们求得的CRC校验码。
然后把原始数据加上1111(不带上之前加的4个0),变成10110 1111,然后发送出去。
接收方收到数据之后根据之前约定好的多项式提取出二进制数10011,然后去除以收到的数据,如果能够整除那么表示数据没问题,如果不能整除,就表示数据在传输过程中出错了。
循环冗余校验码已经很厉害了,但是这样也有个小问题,那就是只能发现错误,但是具体是哪里出错了我们就不知道了。
由此诞生了更牛逼的校验码——海明码。
海明校验码
前俩校验码都需要事先约定好什么,比如奇偶校验需要先说好是奇校验还是偶校验,循环冗余校验码需要先约定好多项式是什么。
和之前两个校验码一样,海明码也需要先约定些什么,那就是奇校验或者是偶校验,但它和奇偶校验可不一样。
我们假设我们使用的是偶校验,并且原始数据是1011。
首先我们在2^n位上插入校验位,比如说2^0=1,2^1=2,2^2=4,2^3=8……,然后在其他位上按照顺序插入原始数据,这边需要注意一下,因为原始数据一共就4位,因此如果我们再1,2,4,8位上插入校验位,那么3,5,6,7位就是数据位,而8位之后没有数据,因此我们不需要第八位的校验位,也就是说只需要1,2,4位上放个校验位。
我们假设校验位有k个,数据长度是n,那么n+k<=2^k-1。我们只需要计算一下这个公式就能快速算出需要几位校验位了,一般软考中选择题就考到这了。
接下来我们就需要计算一下校验码都是什么值了。
首先我们把数据所在的位数都按照二进制拆分一下:
7 = 2^2 + 2^1 + 2^0
6 = 2^2 + 2^1
5 = 2^2 + 2^0
3 = 2^1 + 2^0
我们看用到2^0的有3,5,7位,那么第0+1个(最前面那个)校验码就是要和第3,5,7位上的数据做异或操作,由于我们是偶校验,得出的结果需要为0,因此可以算出第1个校验码是1。
用到2^1的有6,7位,那么第1+1个校验码就与6和7位做异或操作,最终结果需要为0,那么第二个校验码是0。
以此类推,第3个校验码是1,所以最终得到的海明码是1010101。
假设在传输过程中第6位出错了,那么接收方收到的数据就1010111。
这时我们再按照上面计算校验码的方式再求一遍。
将第3个校验码(第4位)和5,6,7位做异或得到结果是1。
将第2个校验码(第2位)和3,6,7位做异或得到结果是1。
将第1个校验码(第1位)和3,5,7位做异或得到结果是0。
因为我们是偶校验,因此需要结果全为0才是正确的,所以可以得出结果,数据传输过程中出错了。
更神奇的地方在我们把计算的结果按照校验位在高位的排在前的顺序排序,得到110,而110转为十进制是6,而我们出错的位数也是6。
这样,海明校验码不仅可以算出我们的数据是否出错,甚至还可以纠错一位,将出错的位数反转即可纠错。不过也只能纠错一位,就算是这样也很厉害了。