程序锅

  • 首页
  • 分类
  • 标签
  • 归档
  • 关于

  • 搜索
基础知识 Etcd LeetCode 计算机体系结构 Kubernetes Containerd Docker 容器 云原生 Serverless 项目开发维护 ELF 深入理解程序 Tmux Vim Linux Kernel Linux numpy matplotlib 机器学习 MQTT 网络基础 Thrift RPC OS 操作系统 Clang 研途 数据结构和算法 Java 编程语言 Golang Python 个人网站搭建 Nginx 计算机通用技术 Git

计算机体系结构 | 计算机指令

发表于 2019-10-02 | 分类于 计算机体系结构 | 0 | 阅读次数 3225

1. 计算机指令是啥?

计算机指令是CPU能直接处理的,它是由一串0或1组成的。那么为什么是0和1呢?因为CPU是一个超大规模的集成电路,通过电路实现加法、乘法等各种运算,0或者1可以对应电路中的低电平或者高电平。从而实现运算功能。

CPU能处理计算机指令组成了计算机指令集(instruction set),然而不同CPU能处理的计算机指令集是不同的,现在指令集见的比较多有两类:

  • 一类是CISC:复杂指令集计算,x86架构为代表的(我们的PC);
  • 另一类是RISC:精简指令集计算,ARM为代表的(比如我们的手机);

指令集不同的意思比较直观的一点就是比如说在某种指令集中1010代表add,所以这条指令的指令码部分就是1010,在有的指令集里面,0000代表add,所以同样的二进制串在不同的指令集下有不同的解读。那么不同的指令集主要体现在面向的设备、对象、性能不同。

  • CISC复杂指令系统就是为了增强原有指令的功能,设置更为复杂的新指令实现部分大量重复的软件功能的硬件化。由于早期的电脑主频低、运行速度慢,为了提高运算速度,不得已将更多的复杂指令加入到指令系统中来提高电脑的处理效率,慢慢形成以桌面电脑为首的复杂指令系统计算机。CISC可以实现高性能CPU设计,但是设计起来就相当麻烦了,要保持庞大硬件设计正确是一件不容易的事情,还要确保性能有所提升,不能开倒车,因此桌面CPU研发时间也慢慢地变长。
  • RISC可以说是从CISC中取其精华去其糟粕,简化指令功能,让指令的平均执行周期减少,达到提升计算机工作主频的目的,同时引入大量通用寄存器减少不必要的读写过程,提高子程序执行速度,这样一来程序运行时间缩短并且减少了寻址,提高了编译效率,最终达到高性能目的。

从CISC、RISC的设计思路来看,CISC专注于高性能方向,但也带来了高功耗,而后者专注于做低功耗的嵌入式,而性能不是很强劲。下面附上这两种指令集的差异:

long long ago,编程是采用打孔卡的方式。要写的程序、要处理的数据变成了一条条纸带或者一张张卡片,然后交给计算机去处理。其中打孔和不打孔的分别表示0或者1.

2. 计算机指令的种类

上面我们讲了指令集的分类,而这一节要讲的是同一指令集下,指令又可以分成哪几类。

  1. 算数指令。加减乘除这些,都会变成一条条算数类指令
  2. 数据传输类指令。给变量赋值、在内存中读写数据,用的都是数据传输类指令
  3. 逻辑类指令。逻辑上的与或非,都是这一类指令;
  4. 条件分支指令。日常我们写的if/else,都会变成条件分支类指令;
  5. 无条件指令。调用函数的时候,就是发起一个无跳转指令。

3. 如何生成计算机指令

下面我们拿C语言和汇编语言来举例说明,C语言的程序是如何变成计算机指令的:

// test.c
int main()
{
  int a = 1; 
  int b = 2;
  a = a + b;
}

我们使用gcc来编译上述代码:

$ gcc -g -c test.c

这段程序首先会翻译成一个汇编语言(ASM,Assembly Language)的程序,这个过程叫做编译成汇编代码。针对得到的汇编代码,再使用汇编器(Assembler)翻译成机器码(0或者1组成),这些机器码就是一条条计算机指令。整个过程大致如下所示:

我们使用objdump来查看一下生成的文件的内容

$ objdump -d -M intel -S test.o

整个文件内容如下:

root@share-virtual-machine:~/c_ws# objdump -d -M intel -S test.o 

test.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
int main()
{
   0:	55                   	push   rbp
   1:	48 89 e5             	mov    rbp,rsp
  int a = 1; 
   4:	c7 45 f8 01 00 00 00 	mov    DWORD PTR [rbp-0x8],0x1
  int b = 2;
   b:	c7 45 fc 02 00 00 00 	mov    DWORD PTR [rbp-0x4],0x2
  a = a + b;
  12:	8b 45 fc             	mov    eax,DWORD PTR [rbp-0x4]
  15:	01 45 f8             	add    DWORD PTR [rbp-0x8],eax
  18:	b8 00 00 00 00       	mov    eax,0x0
}
  1d:	5d                   	pop    rbp
  1e:	c3                   	ret    

那么左边的一串数字(16进制)就是一条条计算机指令,右边的push、mov等是对应的汇编代码。一条C语言语句可能会对应多条汇编语言,但是汇编语言与计算机指令是一一对应的。因为我们直接看一串01,比如(48 89 e5 (16进制))不知道这个指令是干啥的,但是我们通过mov rbp,rsp这条汇编指令可以很清楚知道这条指令的作用,而且汇编语言相对计算机指令更容易记忆和使用。虽然汇编语言与计算机指令是一一对应的,但是它两是不能等同的,汇编指令还得经过汇编器才能翻译成计算机指令,汇编语言的出现是为了方便程序员的,面向的是程序员等这类人,而计算机指令面向的是CPU.

3.1. 汇编与计算机指令对应示例

那么汇编器是如何把对应的汇编代码翻译成计算机指令的呢?下面选用最简单的MIPS指令集,来阐述一下这个具体过程。

MIPS 的指令是一个 32 位的整数,高 6 位叫操作码(Opcode),代表这条指令具体要实现的操作是什么,剩下的 26 位有三种格式,分别是 R、I 和 J。

R 指令一般用来做算术和逻辑操作,里面有读取和写入数据的寄存器的地址。如果是逻辑位移操作,后面还有位移操作的位移量,而最后的功能码,则是在前面的操作码不够的时候,扩展操作码表示对应的具体指令的。

I 指令,则通常是用在数据传输、条件分支,以及在运算的时候使用的并非变量还是常数的时候。这个时候,没有了位移量和操作码,也没有了第三个寄存器,而是把这三部分直接合并成了一个地址值或者一个常数。

J 指令就是一个跳转指令, 整个26位表示跳转后的地址。

假如有这么一条汇编指令,那么对应的MIPS指令会是啥呢?

add $t0,$s2,$s1

由于add操作对应的R类型的指令,所以我们采用R指令的格式。操作码字段是0(add对应的操作码是0),rs是第一个寄存器s1的地址(0x11),rt是第二个寄存器s2的地址(0x12),rd是临时寄存器$t0的值(0x08),因为是相加运算所以位移量为0,那么最终拼接起来的值是:000000 10001 10010 01000 00000 100000,那么对应的16进制是0x02324020,这就是我们最终的MIPS指令。

MIPS 是一组由 MIPS 技术公司在 80 年代中期设计出来的 CPU 指令集。最近,MIPS 公司把整个指令集和芯片架构都完全开源了。MIPS相关资料。

本文主要参考

1.CPU架构有多少种?X86与ARM有哪些不同之处?看完这篇你就懂了

卷死我
dawnguo 微信支付

微信支付

dawnguo 支付宝

支付宝

  • 本文作者: dawnguo
  • 本文链接: /archives/159
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# 计算机体系结构
Nginx简单操作 | Nginx 搭建多服务站点
RPC | RPC 概念及 Thrift 简介
  • 文章目录
  • 站点概览
dawnguo

dawnguo

215 日志
24 分类
37 标签
RSS
Creative Commons
© 2018 — 2025 程序锅
0%