计算机指令——从纸带说起
Posted 东东7_7
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算机指令——从纸带说起相关的知识,希望对你有一定的参考价值。
前言
其实很多时候我都会感叹计算机的伟大,通过一个个电路就完成了如今各种系统,通过各种各样的语言就能够指挥设备完成不同的动作,当写下第一个hellow world的时候我就在想他什么怎么出现,今天搞明白其中的原理,我在这和大家分享
打孔卡
在早期的计算机中,并不是使用高级语言来来操控机器,而是通过一种打孔卡来,它上面存在着一个个0,1数字,人们通过将他打穿,来代表指令,之后再将他们交给计算机去处理,就完成了计算机的控制。
那我们计算机为什么能认识我们的高级语言,并将他变为了一种一堆0,1码,同样这些0,1码在计算机中是如何被处理的,这就引出了机器码和计算机指令
CPU做了什么事?
从硬件上来说,CPU是超大型集成电路,实现了加法和乘法运算和各种的逻辑处理
而从软件上来说的话,CPU就是执行各种指令的逻辑机器,计算机指令就是CPU能听懂的语言,就是机器语言
同样的,不同的CPU能听懂的语言是不同的,比如电脑使用的是intel的cpu是复杂指令集,而在手机中使用的是ARM架构的精简指令集,这就是我们将电脑上的程序拿到手机上却不能运行的原因,他们的CPU语言是不相通的。
一个计算机的程序是由成千上万的指令组合而成的,但是它并不能将程序储存在CPU中,CPU不会只能运行一种程序,而是将程序放在内存中,在需要运行的时候加载到CPU中,我们称这种计算机为储存程序型计算机
那历史中还有不是储存程序型计算机的吗,其实,在没有现代计算机之前,有着聪明才智的工程师们,早就发明了一种叫 Plugboard Computer 的计算设备。我把它直译成“插线板计算机”。在一个布满了各种插口和插座的板子上,工程师们用不同的电线来连接不同的插口和插座,从而来完成各种计算任务。下面这个图就是一台 IBM 的 Plugboard,看起来是不是有一股满满的蒸汽朋克范儿?
从编译到汇编,代码怎么变为机器码?
下面为一段简单的C程序,我们可以看看他是怎么从一条条程序变为机器指令的,最后被CPU执行
int main()
int a = 1;
int b = 2;
a = a + b;
他是一段简单的C语言代码,他在linux上跑起来的时候,我们需要把程序翻译为一个汇编语言(ASM程序),我们称为编译过程,再通过汇编器将他变为一条条机器码,这些机器码就由0,1组成,他们组成一串串16进制数字,就是机器真正能认识的机器码
通过linux指令将对应的机器码打印出来
$ gcc -g -c test.c
$ objdump -d -M intel -S test.o
得到下面的结果,就是我们需要的机器码,一条C语言代码往常对应着一条或者几条汇编代码,但是汇编代码和机器码确实一一对应的
test.o: file format 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 fc 01 00 00 00 mov DWORD PTR [rbp-0x4],0x1
int b = 2;
b: c7 45 f8 02 00 00 00 mov DWORD PTR [rbp-0x8],0x2
a = a + b;
12: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8]
15: 01 45 fc add DWORD PTR [rbp-0x4],eax
18: 5d pop rbp
19: c3 ret
这时候你可能要问了,既然GCC编译器可以之间一键生成机器码,为什么还要生成汇编代码多一次重复率,因为这样更有利于后期的调试,在出现问题的时候,程序员不用去学习机器码,而只用去了解相关的汇编语言,调试起来更加方便,汇编代码到机器码的转坏仅用机器形成就好,但是这样的分层更有利于编译器编写的复杂度,提高了效率,这个就像网络通信分层一样,将问题拆分开来,更有利于去解决
解析指令和机器码
我们平时所用的intel处理器机器码有2000条之多,所以我们将常用的指令分为5种
- 第一类是算术类指令。我们的加减乘除,在 CPU 层面,都会变成一条条算术类指令。
- 第二类是数据传输类指令。给变量赋值、在内存里读写数据,用的都是数据传输类指令。
- 第三类是逻辑类指令。逻辑上的与或非,都是这一类指令。
- 第四类是条件分支类指令。日常我们写的“if/else”,其实都是条件分支类指令。
- 最后一类是无条件跳转指令。写一些大一点的程序,我们常常需要写一些函数或者方法。在调用函数的时候,其实就是发起了一个无条件跳转指令
不同的cpu有着不同的指令集,下面我们来看一下一个简单的CPU指令集,MIPS
他的指令是一个32位的整数,高6位为操作码,代表指令具体是什么样的指令,剩下的有三种格式,R,I,J
R 指令是一般用来做算术和逻辑操作,里面有读取和写入数据的寄存器的地址。如果是逻辑位移操作,后面还有位移操作的位移量,而最后的功能码,则是在前面的操作码不够的时候,扩展操作码表示对应的具体指令的。
I 指令,则通常是用在数据传输、条件分支,以及在运算的时候使用的并非变量还是常数的时候。这个时候,没有了位移量和操作码,也没有了第三个寄存器,而是把这三部分直接合并成了一个地址值或者一个常数。
J 指令就是一个跳转指令,高 6 位之外的 26 位都是一个跳转后的地址
应的 MIPS 指令里 opcode 是 0,rs 代表第一个寄存器 s1 的地址是 17,rt 代表第二个寄存器 s2 的地址是 18,rd 代表目标的临时寄存器 t0 的地址,是 8。因为不是位移操作,所以位移量是 0。把这些数字拼在一起,就变成了一个 MIPS 的加法指令。
使用一个4行八列的纸带打出来就是下图这样,代表之着上面的加法运运算
总结
我们上面提到的打孔卡就是一种储存式计算机,只是计算机的机器码不是机器编译出来的,而是人脑编译出来的,我们这里说了一个程序经历了汇编变为一个个对应着一条条语句的汇编语言,再由解释器变为机器码
扩展
我们上面使用的C为编译型语言,还有着python这样的解释性语言,或者java这样使用虚拟机的语言,都是将我们写好的代码转化为机器码来执行的
只是解释型语言是解释器在在程序运行的时候逐步翻译,而 Java 这样使用虚拟机的语言,则是由虚拟机对编译出来的中间代码进行解释,或者即时编译成为机器码来最终执行。
注:
解释型语言的执行和编译型语言的执行有啥优缺点: 解释型语言的执行要比编译型语言的执行慢,但是编译型语言编译成机器码之后,是要占用更多的存储空间的。 虽然编译型语言的执行要比解释型语言的执行快,但是编译型语言的编译过程要比解释型语言的解释过程慢很多。 所以Java语言采用了折中办法。非热点代码解释执行,热点代码编译执
以上是关于计算机指令——从纸带说起的主要内容,如果未能解决你的问题,请参考以下文章