首页 > 代码库 > 计算机的本质

计算机的本质

我相信很多人都听过这句话:计算机本质上只有0、1和加法

可是0和1到底是怎么构成面前这个花花绿绿的互联网世界的呢?这其实是一个很重要的话题,我认为任何一个想要入行或者已经在业内但没有认真想过这个问题的人都应该好好理解下这句话的意思。这个话题主要涉及的课程是大学里的《计算机体系结构》,这是一个立足之本,是你在行业中遇到陌生问题,奇异问题的推理的根源。

让我们来看看面前这个被叫做电脑的大铁盒子,它里面到底包含了哪些重要的东西?有点常识的人大概都能报出一些名字来,CPU,内存,硬盘,主板,显卡,键鼠,显示器等等

CPU是计算机的核心,这应该很多人都知道,但CPU到底是什么?他能干什么?

答案是,基本的四则运算(我就不解释加法如何衍生出四则运算了,这并不重要),数值比较,数据读写,指令跳转。以上四类指令构成了CPU指令集的核心部分,当然CPU越来越强大的情况下,有很多新的指令出现比如MMX,但这不影响我们理解最基本的计算机模型

也许你已经注意到了他并不是只有加法而已,实际上数值比较看起来就不是用加法器实现的。更重要的是,数据读写?从哪读?往哪写?指令跳转?什么是指令?当提出这些问题时,你应该已经意识到了,CPU并不能独立的运作,他至少需要一些额外的东西——即存储,严格的来说,是内存——这是直接和CPU打交道的重要硬件(词汇丰富的同学请先忘掉L1L2之类的东西)

什么是内存?内存就是一个超大的盒子,这个盒子被分一个个的小格(被称作一位(Bit))整整齐齐的排放着,每个格子都有一个自己的位置(叫做地址),每个盒子里只能装0或者1,不会是其他状态,他们总是0或者1。只要你告诉内存一个地址,他就会告诉你对应盒子里的数据——立刻告诉你,反应非常快,这就是为什么他的学名叫做随机访问存储器——你随机给他一个地址,他就能瞬间访问其中的数据。

但只有0和1实在是太不方便了——这几乎什么都干不了,所以我们把连续的八个格子(八位)组合成一个整体,这样他就能表示0-255的一个整数(参见二进制),当然表示什么是我们说了算的,这取决于我们具体怎么使用和解释它——举个例子来说,你在纸上画了一个圈代表苹果,你女朋友在纸上画了一个圈代表橙子,那么圈到底是苹果还是橙子?这取决于是你去解释还是你女朋友去解释,当然如果写的人和解释的人不一样,那就会出现混乱——明明你画的时候想的是苹果,她却解释成橙子,这是要出问题的,所以我们有了一些约定,或者叫数据标准

八位的数据叫做一个“字节”(Byte),一个字节可以用来表示0-255,这叫做无符号数,也可以用来表示-128-127(感谢指正),这叫做有符号数,还可以用来表示字母,数字,各种英文标点,这叫做ASCII码——而解释权,在于程序员,一个常见的新手题是,怎么输出A的下一个字母,答案是A+1,为什么呢,因为ASCII中,字母是按顺序排列的,而这个题里,其实程序员“故意”滥用了数据标准,他获得了一个A,但他知道A本质上只是一个内存里的数字,所以他可以把A当做一个无符号数来做加法,回头再把它解释回ASCII,他就变成了B

这种“故意”的行为在程序圈还有很多,作为各种各样的编程技巧存在,当你无法理解一个编程技巧时,那么不妨尝试这去想想他背后的真实实现到底是什么样

0-255其实也不怎么够用,虽然很多人应该都很熟悉255这个上限了,在早期内存紧张的时候,很多游戏里的数据都禁止超过255,但现在来说,255显然不够用了,我们的CPU一般是64位的,老一点的可能是32位,32位,就是4个字节,他可以表示一个0-大约4亿的正整数——这通常都够用了,而64位,已经远远超出了日常需求(除了魔兽中变态的BOSS血量)

CPU的位数实际是说他所能计算的(对,就是四则运算和比较运算)数字的上限,既然如此,自然也就是他从内存中取数据的长度——64位的CPU最多一口气取一个64位的数据,所以很多事情我们都得一步步来做,比如比较两个字符串“aaaaaaaaa”,"aaaaaaaaa",按刚刚说的,如果只有8个a,我们倒是可以假装他们是一个64位整数,然后取进来比较,可是9个a就不行了,我们必须至少分两次来做——实际上为了方便人类理解,我们通常都分9次来做,反正对电脑来说不差7次,这恐怕连1微秒都不需要

那指令跳转又是什么意思呢?什么是指令呢?指令就是一条条的计算机命令,比如把2和5加起来呀,把刚刚加完的结果放到内存第8字节呀,再把3和3加起来放到第6字节呀,然后用第八字节里的数减去第六字节里的数,如果大于0,就消灭全人类,否则,就从头再算一遍之类的——显然,其中消灭全人类不是一个有效的指令,CPU指令集里没这条,所以电脑只能表示一脸懵逼,可是从头再算一遍,这个是说什么呢?

我们回来看看刚才说的那么一大段,这其实就是一个简单的“程序”,显然他由一系列的指令构成,指令也是有顺序的,每条指令都包含了一个指令编号,和一组参数,显然指令编号也是一个数字——那么整个指令,其实就是一组数字,一个程序,也就是一组数字,那么他自然会被放到内存当中

CPU总是从一个程序的开头开始执行指令,然后依次执行下一条——除非你使用了跳转命令,他则会执行你指定的地方,然后执行那条命令的下一条,你可以想象CPU里存在一个箭头,指向当前执行的指令,每执行完一条指令,他会自动指向下一条,而跳转命令,可以让你手动控制这个箭头的位置

让我们来看看比较刚刚两个字符串的例子吧

我们假设两个字符串分别已经被放到了第0-8字节,和第10-18字节

那么我们怎么比较这俩字符串呢,你当然可以这么写:

比较第0和第10,如果一样,继续比较,否则认为不相等并结束程序

 

比较第1和第11,如果一样,继续比较,否则认为不相等并结束程序

 

比较第2和第12,如果一样,继续比较,否则认为不相等并结束程序

 

…………

比较第8和第18,如果一样,认为相等并结束程序,否则认为不相等并结束程序

但你应该意识到和太麻烦了——我们写了太多重复的事情,当然我不指望我们能自己发明这种新的写法,但当你知道问题所在时,应该可以很快理解为什么别人要这么写:

 

在第100格记录0

比较第(第100格的数字)和第(第100格的数字+10),如果一样,继续比较,否则认为不相等并结束程序

给第100格的数字+1

如果第100个的数字等于9,认为字符串相等,并结束程序,否则,跳转到第2条指令继续运行

是不是简单了很多?不管是多长的字符串,我们都可以用这么四行来完成比较,而不像刚才需要写9行

没错,这个就叫循环——新人通常最难理解的第一个关卡

先说到这里吧,下一次再来说说外设,即刚刚我们假设内存里已经被放好两个字符串,那么他们是怎么放进来的,以及完成比较之后,我们又要做什么才能告诉计算机面前的人,嘿,我比完了,结果是相等/不相等

计算机的本质