计算机程序的三段人生(一)--计算机原理 -- 未完待续

系列导览

阶段一. 计算机原理:
计算机由哪些部件组成,程序是如何运行起来的呢?
链接:

阶段二. 编译原理:
当有了高级语言,有了各种各样的高级语言,比如C,Golang, Java这些,他又是怎么被计算机硬件所识别呢?
链接:

阶段三. 操作系统的介入
当程序不再直接运行在计算机的硬件设备上,而是由一个称为"操作系统"的超级大管家帮我们运行时,那又是一番怎样的情形呢?
链接:

前言


上大学的时候你一定学过"计算机原理"这门课程,说到"计算机原理", 虽然软硬不分家,但实际上这门课程更偏向于计算机硬件,

而对于狭义的软件,一般指的是高级语言编写的程序,可能大部分都是谭浩强他老人家了,哈哈哈哈哈....

本文是基于计算的核心硬件CPU, 寄存器,内存等这些概念,结合程序或叫软件来进行阐述,其中软件中关于高级语言的编译,则单独使用一篇博文进行讲解....

在学习计算机的底层运行原理时,要时刻将一个概念刻入脑子里:
计算机原理无非是这2点:

  硬件方面:给数据找个安置的地方即"存储;   将数据进行"计算"并得到结果;

       软件方面:而指导这一切的称为"指令"。

此处的"指令"其实就是我们所编写的软件程序在计算机底层时的身份。

计算机硬件


前面说了,计算机的核心影响只有两个:计算 和 存储,其余一切都是为了他们而服务

1. 中央处理器(CPU)

Central Processing Unit,是一块超大规模的集成电路,是一台计算机的运算核心(Core)和控制核心(Control Unit)。它的功能主要是解释计算机指令以及处理计算机软件中的数据。

中央处理器主要包括运算器(算术逻辑运算单元,ALU,Arithmetic Logic Unit),译码器,寄存器组成,和高速缓冲存储器(Cache),控制及状态的总线(Bus),以及实现它们之间联系的数据(Data)一起协同工作。

CPU内部存储器(Memory)和输入/输出(I/O)设备合称为电子计算机三大核心部件。
 
 
2. 存储
       关于内存,外存,硬盘,RAM,ROM这些概念,相信很多人都不陌生,但也有很多人比如我就觉得似是而非,很多人说RAM是内存,ROM是外存/硬盘,包括我也一直这么大概认为着,
但实际这是错误的,当然产生这种错误是有历史原因,下面我们梳理一下。
 
主要参考链接
 
2.1 存储介质有哪些
 RAM:随机存取存储器(random access memory),半导体存储器,存储单元的内容可按需随意取出或存入,存取的速度与存储单元的位置无关的存储器。
          虽然断电会丢失数据,但是有电期间也不是高枕无忧,所以更细划分有如下两种类型RAM:
  1)SRAM:静态随机存储器(Static RAM),不需要刷新电路即能保存它内部存储的数据。存储单元是0是1只要上了电,除非重新赋值,否则不会改变。SRAM速度非常快,是目前最快的存储设备,但是造价昂贵。
  2)DRAM:动态随机存储器(Dynamic RAM),每隔一段时间,要刷新充电一次,否则内部的数据即会消失。比SRAM速度慢但造价低,当然比ROM是快很多的。
         DRAM更更细分的话还有很多种,但是这里不再展开。
 
 ROM:只读存储器(Read-Only Memory),属于半导体存储器,在系统停止供电的时候仍然可以保持数据。在早期,只能读出信息不能写入,也就是说在出厂的时候就"定死"了。随着技术的发展,ROM已经不再是严格的只能读出不能写入了,衍生出如下几种:
  1)PROM:可编程ROM(Programmable ROM),用户可以用专用的编程器将自己的资料数据写入,只有一次机会,一旦写入在也无法修改。缺点是成本高速度满所以应用场景少。
  2)EPROM:可擦出可编程ROM(Erasable Programmable ROM),顾名思义,在PROM的基础上有了可重复擦除和写入的特性。缺点是需要借助专用工具且写内容时要求加一定的编程电压。
  3)EEPROM:电可擦除可编程ROM(Programmable ROM),不需要借助设备,用电子信号就可以修改其内容,说到电子信号,不就是CPU执行机器码干的事情么?所以说,用程序就可以控制写入了。另外,EEPROM是以Byte为最小的修改单位。
  4)FLASH :闪存,有的资料上说是在EEPROM上发展出来的,有的说是EPROM上的,不管怎么说他都是ROM的高级品,同样掉电不易失,同样电可擦除,但不是按字节为单位的擦除,而是可以按块(block)甚至整个擦除,效率更高一些,更加细分还分为:
      (1)NOR FLASH:数据线和地址线分开,可以像RAM一样随即寻址,可以读取任何一个字节,但擦除还是按块来
     (2)NAND FLASH:数据线和地址线复用,不能随机寻址,只能按页读取,同样擦除还是按块。擦除和写入速度比NOR快,读比NOR慢。
 
 
 
2.2 与实际硬件的关系
市面上一般有两种说法:RAM就是内存,ROM就是硬盘;RAM和ROM都是内存,硬盘是磁盘(非半导体存储器,另一种存储介质)。
经过整合各方材料,我认为以下的说法是准确的:
内存:RAM和ROM都是内存,我们常说的内存条属于RAM(严格说是DRAM),或者叫主存Main Memory; 还有一块内存用于存储BIOS信息,属于出厂就在的,属于ROM
          在研究系统启动原理的时候,我们常常会看到这样的描述:"ROM或者硬盘上寻找引导程序...”,那时候还疑惑,有硬盘啥事阿?
缓存:数据交换的缓冲区(称作Cache),当某CPU要读取数据时,会首先从缓存中查找需要的数据,如果找到了则直接执行,找不到的话则从内存中找。
          由于缓存的运行速度比内存快得多,故缓存的作用就是帮助CPU更快地运行。Cache在CPU中容量很小,一般会分为一级缓存(L1)和二机缓存(L2)。
          描述到这里,是不是猜到使用的是那种存储媒介了?没错,就是SRAM。
 
寄存器:Register,属于CPU内部的部件,可以用FF(电路里的边沿触发器flip-flop)实现,比较大的寄存器组也可以用具有多读写端口的SRAM做,具体选哪个取决于微架构的设计和取舍。
 
 
硬盘分为两种,一种是机械硬盘(即磁盘HDD),一种是固态硬盘(SSD)
  磁盘:也就是我们通常说的硬盘,HDD字眼是不是很熟悉?是不是在很多硬盘上都能看到这三个字母?这类硬盘属于磁性原理存储,的和ROM没什么关系,
  固态硬盘:也算是前几年火起来,我弟弟就曾经跟我说,给你电脑加块固态硬盘,速度会快些,现在想起来,若有所思了。
                  固态硬盘和磁盘不同,固态硬盘用到的颗粒是基于NAND FLASH技术,和u盘以及手机存储有点相似,此时的硬盘,可以认为是一种ROM
 
另:关于以上关键存储的速度,可以参考,主要说了Cache和寄存器
 
2.3 小小结
RAM:属于内存;可读可写,访问速度快,断电会丢失数据; 分SRAM和DRAM两种,分别作为CPU的Cache和内存条。
ROM(基础版):属于内存;可读断电不丢失数据;用于存储CPU的固化信息比如BIOS
ROM(进阶版) :属于外存中硬盘的一个种类:固态硬盘。至于机械硬盘,已经是另外一种材质了....
 
 
 
 wxy碎碎念:
1)CPU主要两件事:运算和控制。
 运算谁?运算数据!数据在那里?在存储中,包括寄存器,缓存和内存!谁来控制这种行为?由指令来控制!
   怎样控制?(1) 指令由不同的计算机架构决定代表一种什么样的具体行为
      (2) 指令通过总线指挥,包括数据总线和地址总线!
2)关于三个存储器件:寄存器,缓存和内存
   寄存器,缓存:属于CPU内部的存储介质;  内存:同属于RAM类型介质; 

编程语言


1. 编程语言的历史

    开始, 计算机只能运行二进制,即她只认识0101,所以最开始的程序是用0101编码出来的,称为"机器语言"。用这些01串去控制CPU进行计算,什么样的01串对应什么样的操作,以及01串具体如何控制计算机硬件设备,则由计算机架构决定,见下一小节。除此之外,还要手动为这一坨0101....分配存储空间以及输入和输入,所以显然,这是反人类的.....。

  后来,芯片厂商发明了一些助记符,也就是"汇编语言"。所谓的01串,无非就是让CPU去计算,去移动,去获取等等,于是把这些行为用固定的一个"单词"去代替。但是,汇编语言不过是机器码的另一种表现形式而已,还是会直接和硬件比如寄存器打交道,只要与硬件相关那么他的可移植性就....

  最后,"高级语言"诞生了,管你什么硬件,我代表的就是一个逻辑(算法),至于硬件怎么去实现我的逻辑?不管!于是,可移植性大大提升!高级语言需要有编译工具将之转换成最终的机器码,这部分由阶段二进行详解。

       wxy碎碎念:这三个语言并不是"层层递进"的关系,高级语言 -- 编译--> 汇编语言  --等同于--> 机器语言

2. 计算机架构(Computer architecture)

参考wiki:
https://en.wikipedia.org/wiki/Computer_architecture
https://en.wikipedia.org/wiki/Reduced_instruction_set_computer

及其他相关的连接

wiki定义:
是描述计算机系统功能,组织和实现的一组规则和方法。在有些定义中,计算机架构被描述成计算机的能力和编程模型,但并不是特定的实现. 在其他定义中,计算机体系结构包括指令集体系结构设计,微体系结构设计,逻辑设计和实现。

wxy: 首先,所谓架构并不是具体的实现,而是一种规范;其次,所谓的规则和方法,其实就是让软件指令和硬件设备,如何具体结合起来,即一条"指令"到底意味着什么, 他的背后对应着哪些硬件操作....

2.1 指令集架构(ISA)

wiki说:

Instruction set architecture (ISA) defines the machine code that a processor reads and acts upon as well as the word size, memory address modes, processor registers, and data type.

In general, an ISA defines the supported data types, the registers, the hardware support for managing main memory, fundamental features (such as the memory consistency, addressing modes, virtual memory), and the input/output model of a family of implementations of the ISA.

An ISA specifies the behavior of machine code running on implementations of that ISA in a fashion that does not depend on the characteristics of that implementation, providing binary compatibility between implementations. 

An instruction set architecture is distinguished from a microarchitecture, which is the set of processor design techniques used, in a particular processor, to implement the instruction set. Processors with different microarchitectures can share a common instruction set. 
解析:
定义了处理器在读取机器码后,综合其word size、内存地址、处理器寄存器、以及数据类型,所做的操作。
总体来说,ISA定义了支持的数据类型,有哪些寄存器,用于管理主存的硬件支持,基本功能(例如内存一致性吗, 寻址模式, 虚拟内存),以及对应的一系列IO模型。
一个ISA定义了基于这个架构运行的机器码所对应的具体行为,运行方式并不依赖于具体的实现,在不同的具体实现之间实现了二进制兼容(binary compatibility).
指令集架构不同于微体系架构(microarchitecture), 后者是针对特定的处理器,为了实现指令集,所使用的一种处理器设计技术。具有不同为微架构的处理器可以共享一个公共指令集。

wxy:
ISA即指令集架构,是将计算机各个硬件以及硬件如何被操作通通抽象出来,用一条条指令去定义这些,
然后,只要是对于基于某个特定架构实现的计算机,所有二进制程序都可以兼容的运行于其上。
另外,还提了一点微体系架构,正如其名称中的"微"字,是独立于架构之外,各个cpu厂商为了实现这个架构,自己处理器内部的实现技术。

一个疑惑我若干年的问题:所谓指令集是用在制定汇编到机器码(machine code)对应关系的,还是用在CPU拿着机器码如何控制电子电路呢?
         现在终于明确:指令集是CPU如何控制电子电路的规则集合,不禁疑惑为啥国内的文档就没有这么明确的说明呢?还是我没检索对地方???
 
 

相关概念区分:

machine code: 机器码,CPU只认识这样的指令,即一组组0101010110....

instruction: 指令
instruction set:指令集
instruction set architectures:指令集架构

opcode: 操作码,是operation code的缩写, 也称为instruction machine code, instruction code, instruction syllable(音节), instruction parcel or opstring),
    是在机器语言指令中,用来指定所要执行的操作那部分. 除了操作码自身,大部分指令还会指定要处理的数据称为操作数(operands).
    另外,不仅硬件CPU的指令集架构会使用操作码; 在abstract computing machines(虚拟机?)中,操作码也作为其字节码规范的一部分。
    wxy: 1 + 2 = 3,"1"是操作数(operands),"+"是操作码(opcode),整个算式是一条指令(instruction)

 
精简指令集(RISC) 和 复杂指令集(CISC)
指令集架构ISA只是一个定义,那具体的实现呢?目前有两种类型的的基础实现 
 
 
另外,何为精简指令集,何为复杂指令集
精简指令集:RISC(Reduced Instruction Set Computer),比如存,取,加这类指令
复杂指令集:CISC(Complex Instruction Set Computer),更复杂的指令比如POP等。
学过电路原理的知道,几个晶体管就能组成一个加法器,如果要得到更高级的操作有两种方案:
1)拆分成多条精简指令,多次执行。
2)就得搭建更复杂的电路,真正执行的时候使用的是微码的技术,即需要Decode更小的计算单元
 
更多的参考:
 
小结:
  总结了各方的说法,初步可以得到如下的结论
  1)指令集指的是汇编和和机器码之间的映射关系
  2)计算机架构也叫微架构, 有一种说法是"CPU是某个特定指令集在硬件上的实现",另一句话"指令集决定微结构的一部分硬件逻辑设计"需要好好理解下,因为架构可以支持一种,两种指令集,并不是一对一关系。另外,实际上实现指令集是微架构的重点但不是全部,这个架构还决定了怎么取指令,解码,分指出流水线等,也就是光有对照表肯定不幸,要让电路实际工作起来才是终极目的。
  3)上面说到解码,结合推荐博文中有这样的后记:"而这个decode的过程,让曾经泾渭分明的RISC和CISC两种CPU架构的界限变得模糊了起来....",我的理解是
  所谓精简指令集,精简嘛,可以认为是一个机器码对应一种电路操作
  复杂指令集,一个机器码实际包含了多种操作,经过Decode才变成了单一的电路操作,也就是微码
  但是微码和精简指令不是一回事,只是类似的都属于单一的执行单元

2. 从高级语言到机器语言的转化

根据语言的历史我们基本上可以判断,转化基本上就是 高级 --->汇编 ---->机器

汇编编译器assembler编译目标代码二进制文件(nasm -f elf -g -F stabs *.asm),连接器linker(ld -o bin_file *.o)除了把目标代码组合成一个单个的块,还要确保模块以外的函数调用能够指向正确的内存引用(连接器必须建立一个索引,也就是符号表,里面存放的是它连接的每一个目标模块中的每一个已命名项,其中存放着一些关于哪个名字或叫符号指向模块内部哪个位置的信息)。
立即数,内置在机器指令内部,它不是存放在寄存器中,也不是存放在位于某个指令之外的内存中。
1, 寄存器打中括号代表寄存器的内存地址中的内存数据。例:
mov eax,[ebx+16]。
2, 在汇编中,变量名代表的地址,不是数据。例:
Msg: "Hello World"
mov ecx,Msg         #复制Msg地址到ecx寄存器,而不是数据
mov edx,[Msg]       #复制数据,而不是地址
MsgLen: equ $-Msg   #$代表末尾,长度=末尾位置减开始位置

各家的汇编格式可能不一样,例如gcc的汇编器使用的是AT&T文法,常见的汇编语言可以使用nasm编译。

指令的执行


 
2,第二步:程序被加载到存储中
   如果是无操作系统的,比如单片机,则需要提前将编译得到HEX格式文件烧到flash种,或者说是ROM中
   如果是有操作系统的,则首先会执行一段叫做BIOS的程序,由BIOS加载操作系统到xxx,而这个BIOS是CPU出厂就写入到ROM中的,即BIOS ROM
                    详细可以参看我的系列博客“Linux内存管理-预备篇2(寻址与内存映射) ”
  总的来说,CPU工作的最最开始都是先和ROM中的指令(机器码)打交道,之后指令被???取到Cache?在取到
  • 机器语言(CPU可以直接识别的语言)不存在“变量”这个概念,它只能操作内存和寄存器(CPU内部的几个暂存电路,数量很有限)。变量这个概念在实现的时候通常是将之对应于某个寄存器或者某一片内存。
  • CPU提供了一系列指令来方便程序员维护一个叫“栈”的数据结构。栈位于内存当中,栈顶和栈底都保存在特殊的寄存器当中,CPU可以随时将数据压栈或者出栈。这个“数据”的含义实际上比较宽泛,它可以是一个数字、字符,也可以是CPU的运行状态。CPU可以将某一刻的运行状态压栈,然后跳转到其他地方执行一段程序,然后出栈恢复之前的执行状态,这就实现了函数的调用。没错,CPU也不太认识什么叫做“函数”,不过将运行状态压栈以及恢复运行状态都有专门的指令,一般就把它们成为”子程序调用指令“和“返回指令”。
  • 对于“局部变量”,CPU连“变量”都不认识,所以局部变量什么的也不存在了。不过这玩意可以依靠栈来实现,通过压栈就可以随时分配一个内存,出栈就可以认为这个内存区域不再被使用。配合刚才说过的“函数”调用的实现方法,这种依靠压栈分配出的内存一定只能被当前正在执行的函数使用。因为函数调用前,这片内存还没有被分配,返回后这片内存也不再使用了
C语言等高级语言肯定是不能直接执行的,需要进行编译,得到最终的机器语言,然后交给CPU执行机器码
1,第一步:编译
    目标都是机器码,可以直接编译得到机器码;
                               可以先得到汇编(编译),再从汇编得到机器码(汇编);
                               如果涉及到库,编译/编译+汇编 之后,再经过链接才能得到可执行文件,也就是一堆机器码,详细的原因可以参考
    另外,需要说明的是一般的博文常常会直接拿汇编解释CPU的行为,因为汇编几乎可以认为和机器码一一对应,只不过汇编使用了人类可理解的符号去表示机器码(后来学了个词叫"助记符",嗯,很好!)
              而高级语言就不同了,高级语言是"一句话包含多个含义",即一条语句可能被翻译成多条汇编
              即,汇编语言就是机器语言,表示不同罢了
                     高级语言属于另一个层级的概念,需要"分解"一下才能对应成汇编
 
 
 
 
 
 
 
 
 
原文地址:https://www.cnblogs.com/shuiguizi/p/14580224.html