计算机是如何识别代码的

这个问题要展开的话蛮复杂的,展开到几万几十万字都可以。但针对题主的问题可以有个更具体精确的回答方式。因为我也不是 CS 科班出身,也才大二,就几个关键的节点说一下我的理解。可能有些错漏,权当小导论看看得了,有错误敬请指出。

大致过程

我的理解中,整个过程大致可以分为两段

  1. 代码到机器码的过程
  2. 机器码被 CPU 执行

其中阶段 1 是编译原理的领域,阶段 2 是计组的领域,1 和 2 中间应该是操作系统的领域。

举个简单的例子,然后让过程再具体一点的话(依旧忽略一些不那么关键的步骤),可能是:

  1. 编译阶段:把高级语言变成机器语言(按题主的问题,其实可能更需要这方面的信息)

    1. 分析高级语言的代码并预处理
    2. 进行编译,把高级语言代码变成汇编语言代码
    3. 进行汇编,把汇编语言代码变成机器语言,生成可执行文件(二进制形式)
    4. (链接阶段可以在编译阶段进行但具体时机不固定,因不涉及关键所以略)
  2. 系统阶段:加载程序、调度进程、管理内存等(对不起原谅我系统还没怎么学)

    1. 读取可执行文件
    2. (省略一些非关键部分)
    3. 把程序从硬盘加载到内存中
  3. 处理器阶段:执行二进制程序

    1. 从内存读取程序的指令
    2. 指令通过数据通路
    3. 在流水线的每个阶段进行不同的操作

      1. 把机器码指令拆分出不同部分
      2. 根据指令执行不同的操作(读取、写入、计算等)

其中系统阶段可能比较倾向于中介的成分,不是特别特别关键。因为这个问题我前阵子也在思考,实际上就算对系统一无所知(我就是),光靠知道“编译阶段得到机器码程序,然后经过一些过程把机器码加载到指令内存中”,就足够把编译部分和执行部分串联起来了。

就我而言,在这个问题中,以前卡住的最关键的点是:文本到信号的过程,也就是把机器码加载到指令内存的过程。这部分只要学一下加载器应该就可以彻底贯通了。

补充:计组部分的具体过程

上学期刚学完 MIPS,这里就用 MIPS 举例了。

基础概念

  • 寄存器:操作起来最快最便捷的核心存储单元,数量相当有限(比如 MIPS 有 32 个通用寄存器)
  • 内存(RAM):操作起来要多废时间读写的存储空间,量大管饱性能差(相比寄存器而言)

处理器结构 & 数据通路

image.png

用计组黑皮书的一张图举例(图 4.11)。这个通路叫 Datapath(数据通路),数据从左边流向右边,在通路的不同部分进行各种操作。

关键元件解释(从左到右,忽略上方的次要部分):

  1. PC 寄存器:指向指令内存中的指令的指针,用于决定要执行指令(指哪取哪)
  2. 指令内存:专门存储程序指令的内存空间
  3. 寄存器:通用寄存器
  4. ALU:算术逻辑单元,用于执行运算
  5. 数据内存:专门用于存储数据的内存空间
  6. 蓝色的标签:控制单元连接过来的信号
  7. 数据单元:根据当前运行的指令,控制各个部分的部分参数(类似于全局辅助参数的集合)

每次执行一条指令,先从 PC 寄存器得到要执行的指令的地址,把指定取出然后译码,拆出不同的部分。比如一条 MIPS 的 R 类指令的二进制存储结构是这样的:

image.png

对于不同指令的结构问题,可以去看别的文章,这里不赘述,比如可以看看这篇文章

R 型指令包括了基本的加减乘除等计算指令,我们这里用加法举例。

比如 add $t0, $t1, $t2 # $t0 = $t1 + $t2 这条指令的作用是把寄存器 t1, t2 的值加起来,存入 t0。寄存器对应的编号是:

  • $t0 → 8 (二进制 01000
  • $t1 → 9 (二进制 01001
  • $t2 → 10 (二进制 01010

所以结构是这样的:

Fieldopcoders ($t1)rt ($t2)rd ($t0)shamtfunct
Binary00000001001010100100000000100000

拼起来就得到:000000 01001 01010 01000 00000 100000,这就是上面这条汇编指令在指令内存中的形式。

这条命令被取出来,然后译码阶段把这条指令再次拆开,拆出不同的部分,然后根据 opcode 和 funct 可以知道这条指令是 R 型指令的加法指令,于是在 ALU 里的行为就可以知道了。

可以从图上看到 ALU 的结果进入了一个 MUX,也就是多路复用器,另一个输入是来自数据内存的,这个 MUX 代表:这条指令可能需要把 ALU 的计算结果存到寄存器(比如 R 型指令),或者要从内存里读取数据到寄存器(比如 lw 指令),要根据情况,把对应的值存到寄存器里。

话说回来,对于 ADD 指令,计算结果就会通过 ALU 的出口,到 MUX,再到寄存器,这条指令就结束了。

对于 PC 连着的上半图的部分呢,其实是用于决定 PC 指针的移动的。右上角的 MUX 的输出是连着 PC 的输入的,它会从两个值里取其一并输入给 PC 寄存器。这两个输入代表着两种情况:

  1. 程序顺序运行,PC 自然移动到下一条指令,则 PC:=PC+4。因为一条指令长 32 位,所以是 4 字节,按字节寻址,所以每次加 4 字节移动到下一条指令。
  2. 程序遇到分支,这时第二个输入对应分支要跳转的位置,赋值给 PC 就可以实现程序的跳转。

其它

本来还有很多东西,比如流水线、冒险等等,这里不详细展开了,离主线比较远。


安逐悲
3 声望1 粉丝