深入浅出计算机组成原理(一)--计算机指令

因为计算机或者说 CPU 本身,并没有能力理解这些高级语言,计算机只能理解 “机器码”,即一连串的 “0” 和 “1” 这样的数字。

CPU

CPU 是计算机的大脑,Central Processing Unit,中文为 中央处理器

从硬件角度: CPU 是一个超大规模的集成电路,通过电路实现加法、减法、乘法等逻辑运算。

从软件角度:CPU 是一个执行 计算机指令的逻辑电器。这里的计算机指令是指 CPU 能够听懂的语言,可以把它看做 机器语言

每种 CPU 能够听懂的语言是不同的,例如个人 PC 中的 Intel 的 CPU, IPhone 中的 ARM 的 CPU。每种 CPU 有自己能够支持的 计算机指令。不同 CPU 间的计算机指令是不可互通的,例如你不能把运行在计算机上的程序复制到手机上,这是没有办法运行的。

一个计算机程序编译后,有很多的计算机指令。

从编译到汇编,代码是怎样变成机器码的?

1
2
3
4
5
6
7
// test.c
int main()
{
int a = 1;
int b = 2;
a = a + b;
}

要让这段程序在 Linux 系统上跑起来,我们需要把整个程序翻译成一个 汇编语言(ASM,Assembly Language)的程序,这个过程我们叫做 编译(Compile) 成汇编代码。之后再使用汇编器将汇编代码翻译成用 “0”和”1” 组成的机器码。这一条条机器码就是一条条 计算机指令

使用 GCC 编译器 和 objdump 命令,把对应的汇编代码和机器代码打印出来:

1
2
3
//MacOS 环境下
gcc -g -c test.c
objdump -d -S test.o > text.txt

text.txt 文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
test.o: file format Mach-O 64-bit x86-64

Disassembly of section __TEXT,__text:
_main:
; int main(){
0: 55 pushq %rbp
1: 48 89 e5 movq %rsp, %rbp
4: 31 c0 xorl %eax, %eax
; int a = 1;
6: c7 45 fc 01 00 00 00 movl $1, -4(%rbp)
; int b = 2;
d: c7 45 f8 02 00 00 00 movl $2, -8(%rbp)
; a = a + b;
14: 8b 4d fc movl -4(%rbp), %ecx
17: 03 4d f8 addl -8(%rbp), %ecx
1a: 89 4d fc movl %ecx, -4(%rbp)
; }
1d: 5d popq %rbp
1e: c3 retq

汇编语言其实就是让程序员能够看懂机器语言。

将上面文件中的汇编语言和机器语言分别拆分出来,分别如下:

1
2
3
4
5
6
7
8
9
10
pushq   %rbp
movq %rsp, %rbp
xorl %eax, %eax
movl $1, -4(%rbp)
movl $2, -8(%rbp)
movl -4(%rbp), %ecx
addl -8(%rbp), %ecx
movl %ecx, -4(%rbp)
popq %rbp
c3 retq
1
2
3
4
5
6
7
8
9
10
0:   55
1: 48 89 e5
4: 31 c0
6: c7 45 fc 01 00 00 00
d: c7 45 f8 02 00 00 00
14: 8b 4d fc
17: 03 4d f8
1a: 89 4d fc
1d: 5d
1e: c3

解析指令和机器码

CPU 常见指令分类:

  • 算术类指令
  • 数据传输类指令
  • 逻辑指令
  • 条件分支类指令
  • 无条件跳转指令

不同的 CPU 对应着不同的指令,也就对应着不同的汇编语言和机器码。

场景:

在 Android 开发过程中,我们需要添加第三方 so 库,根据不同的手机需要添加不同的 CPU 架构的 so 包,具体参见资料-Android的.so文件、ABI和CPU的关系

除了 C 这样的编译型语言之外,不管是 Python 这样的解释型语言,还是像 Java 这样使用虚拟机的语言,最终都是把写好的代码转换为 CPU 理解的机器码来执行。

解释型的语言,通过解释器在程序运行的时候逐句翻译,而 Java 这样 使用虚拟机的语言,则由虚拟机对编译出来的中间代码(.class)进行解释,或者即时编译成为机器码来最终执行。

运行编译型语言: 是相对于解释型语言存在的,编译型语言的首先将源代码编译生成机器语言,再由机器运行机器码(二进制)。像C/C++等都是编译型语言。编译型语言程序在执行之前需要一个专门的编译过程,把程序编译成 为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果就行了。程序执行效率高,依赖编译器,跨平台性差些。如C、C++、Delphi等.

解释性语言 编写的程序不进行预先编译,以文本方式存储程序代码。在发布程序时,看起来省了道编译工序。但是,在运行程序的时候,解释性语言必须先解释再运行。