摘要:汇编器是怎么把对应的汇编代码,翻译成为机器码的。总结打孔卡,其实就是一种存储程序型计算机。推荐阅读了解的指令集参看计算机组成与设计软硬件接口第版的小节参考深入浅出计算机组成原理
你在学写程序的时候,有没有想过,古老年代的计算机程序是怎么写出来的?
当年写程序,不像现在这样,都是用一种古老的物理设备,叫作“打孔卡(Punched Card)”
用这种设备写程序,没法像今天,掏出键盘就能打字,而是要先在脑海/纸写出程序,然后在纸带/卡片上打洞
这样,要写的程序、要处理的数据,就变成一条条纸带或者一张张卡片,之后再交给当时的计算机去处理
上世纪60年代晚期或70年代初期,Arnold Reinold拍摄的FORTRAN计算程序的穿孔卡照片
人们在特定的位置上打洞或者不打洞,来代表“0”或者“1”。
为什么早期的计算机程序要使用打孔卡,而不能像我们现在一样,用C或者Python这样的高级语言来写呢?
因为计算机或者说CPU本身,并没有能力理解这些高级语言
即使在2019年的今天,我们使用的现代个人计算机,仍然只能处理所谓的“机器码”,也就是一连串的“0”和“1”这样的数字。
我们每天用高级语言的程序,最终是怎么变成一串串“0”和“1”的?这一串串“0”和“1”又是怎么在CPU中处理的?
1 在软硬件接口中,CPU帮我们做的事CPU(Central Processing Unit,中央处理器)就是计算机的大脑
硬件的角度
一个超大规模集成电路,通过电路实现了加法、乘法乃至各种各样的处理逻辑。
软件工程师的角度
一个执行各种计算机指令(Instruction Code)的逻辑机器
这里的计算机指令,就好比一门CPU能够听得懂的语言,即机器语言(Machine Language)
不同的CPU能够听懂的语言不太一样
个人PC用的是Intel的CPU,iPhone用的是ARM的CPU,这两者能听懂的语言就不太一样
类似这样两种CPU各自支持的语言,就是两组不同的计算机指令集(Instruction Set)
这里面的“Set”,其实就是数学上的集合,代表不同的单词、语法
如果我们在自己电脑上写一个程序,然后把这个程序复制一下,装到自己的手机上,肯定是没办法正常运行的,因为这两者语言不通
而一台电脑上的程序,简单复制一下到另外一台电脑上,通常就能正常运行,因为这两台CPU有着相同的指令集,它们语言相通
存储程序型计算机(Stored-program Computer)
计算机程序,不可能只有一条指令,而是成千上万条指令组成
但CPU不能一直放着所有指令,所以程序平时是存储在存储器
这种程序指令存储在存储器里面的计算机,我们就叫作
Plugboard Computer
在没有现代计算机之前,有着聪明才智的工程师们,早就发明了一种叫Plugboard Computer的计算设备
在一个布满了各种插口和插座的板子上,工程师们用不同的电线来连接不同的插口和插座,从而来完成各种计算任务
IBM的Plugboard
2 编译=>汇编 代码=>机器码代码,到底是怎么变成一条条计算机指令,最后被CPU执行的呢?
test.c
编译(Compile)成汇编代码
要让这段程序在Linux跑起来,需要把整个程序翻译成汇编语言(ASM,Assembly Language)的程序
针对汇编代码,可以再用汇编器(Assembler)翻译成机器码(Machine Code)
这些机器码由“0”和“1”组成的机器语言表示,这一条条机器码,就是一条条的计算机指令
这样一串串的16进制数字,就是我们CPU能够真正认识的计算机指令。
在Linux上,可使用gcc和objdump,把对应的汇编代码和机器码都打印出来。
左侧一堆数字,就是一条条机器码
右边一系列的push、mov、add、pop等,这些就是对应的汇编代码
一行C语言代码,有时候只对应一条机器码和汇编代码,有时候则是对应两条机器码和汇编代码
汇编代码和机器码之间是一一对应的。
实际在用GCC(GUC编译器套装,GUI Compiler Collectipon)编译器的时候,可直接把代码编译成机器码,为什么还需要汇编代码呢?
那一串数字表示的机器码,摸不着头脑
但即使你没有学过汇编代码,看的时候多少也能“猜”出一些这些代码的含义。
汇编代码就是“给程序员看的机器码”
也正因为这样,机器码和汇编代码是一一对应的
很容易记住add、mov这些用英文表示的指令
而8b 45 f8这样的指令,由于很难一下子看明白是在干什么,所以会非常难以记忆
从高级语言到汇编代码,再到机器码,就是一个日常开发程序,最终变成了CPU可以执行的计算机指令的过程。
3 解析指令和机器码了解了这个过程,下面我们放大局部,来看看这一行行的汇编代码和机器指令,到底是什么意思。
Intel CPU,有2000条左右的CPU指令,实在是太多了,没法一一讲解。不过一般来说,常见的指令可以分成五大类。
算术类指令加减乘除,在CPU层面,都会变成一条条算术类指令
数据传输类指令给变量赋值、在内存里读写数据,用的都是数据传输类指令。
逻辑类指令逻辑上的与或非
条件分支类指令日常的“if/else”
无条件跳转指令写一些大一点的程序,我们常常需要写一些函数或者方法
在调用函数的时候,其实就是发起了一个无条件跳转指令。
汇编器是怎么把对应的汇编代码,翻译成为机器码的。
不同的CPU有不同的指令集,也就对应着不同的汇编语言和不同的机器码
为了方便你快速理解这个机器码的计算方式,我们选用最简单的MIPS指令集,来看看机器码是如何生成的。
MIPS是一组由MIPS技术公司在80年代中期设计出来的CPU指令集。就在最近,MIPS公司把整个指令集和芯片架构都完全开源了。想要深入研究CPU和指令集的同学,推荐一些资料,可以自己了解下。
MIPS的指令是一个32位的整数,高6位叫操作码(Opcode)
也就是代表这条指令具体是一条什么样的指令,剩下的26位有三种格式,分别是R、I和J。
R指令
一般用来做算术和逻辑操作,里面有读取和写入数据的寄存器的地址
如果是逻辑位移操作,后面还有位移操作的位移量
而最后的功能码,则是在前面的操作码不够的时候,扩展操作码表示对应的具体指令的。
I指令
通常是用在数据传输、条件分支,以及在运算的时候使用的并非变量还是常数的时候
这个时候,没有了位移量和操作码,也没有了第三个寄存器,而是把这三部分直接合并成了一个地址值或者一个常数。
J指令
一个跳转指令,高6位之外的26位都是一个跳转后的地址
add $t0,$s2,$s1
下面都用十进制来表示对应的代码。
对应的MIPS指令里
opcode是0
rs代表第一个寄存器s1的地址是17
rt代表第二个寄存器s2的地址是18
rd代表目标的临时寄存器t0的地址是8
因为不是位移操作,所以位移量是0
把这些数字拼在一起,就变成了一个MIPS的加法指令。
为了读起来方便,我们一般把对应的二进制数,用16进制表示出来
在这里,也就是0X02324020。这个数字也就是这条指令对应的机器码。
回到开头我们说的打孔带
打孔代表1
没有打孔代表0
用4行8列代表一条指令来打一个穿孔纸带,那么这条命令大概就长这样:
你应该学会了怎么作为人肉编译和汇编器,给纸带打孔编程了,不用再对那些用过打孔卡的前辈们顶礼膜拜了。
4 总结打孔卡,其实就是一种存储程序型计算机。
只是这整个程序的机器码,不是通过计算机编译出来的,而是由程序员的人脑“编译”成一张张卡片的
对应的程序,也不是存储在设备里,而是存储成一张打好孔的卡片
但是整个程序运行的逻辑和其他CPU的机器语言没有什么分别,也是处理一串“0”和“1”组成的机器码而已。
我们看到了一个C语言程序,是怎么被编译成为汇编语言,乃至通过汇编器再翻译成机器码的。
除了C这样的编译型的语言之外,不管是Python这样的解释型语言,还是Java这样使用虚拟机的语言,其实最终都是由不同形式的程序,把我们写好的代码,转换成CPU能够理解的机器码来执行的。
只是解释型语言,是通过解释器在程序运行的时候逐句翻译,而Java这样使用虚拟机的语言,则是由虚拟机对编译出来的中间代码进行解释,或者即时编译成为机器码来最终执行。
5 推荐阅读了解Intel CPU的指令集参看
《计算机组成与设计:软/硬件接口》第5版的2.17小节
参考深入浅出计算机组成原理
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/76092.html
摘要:冯诺依曼体系结构示意图总结冯诺依曼体系结构确立了我们现在每天使用的计算机硬件的基础架构。因此,学习计算机组成原理,其实就是学习和拆解冯诺依曼体系结构。 showImg(https://ask.qcloudimg.com/http-save/1752328/g6cdrb45jg.png); 1 计算机的基本硬件组成 早期,DIY一台计算机,要先有三大件 CPU 内存 主板 1.1 C...
摘要:在上一篇中我们谈到过程序的执行时间指令数要提升计算机的性能,可以从上面这三方面着手。在摩尔定律和并行计算之外,在整个计算机组成层面,还有这样几个原则性的性能提升方法。 showImg(https://ask.qcloudimg.com/http-save/1752328/uskvyzme4j.png); 在上一篇中,我们谈到过 程序的CPU执行时间 = 指令数×CPI×Clock Cy...
摘要:这个办法,在现在计算机的内存管理里面,就叫作内存分页和分段这样分配一整段连续的空间给到程序相比分页则是把整个物理内存空间切成一段段固定尺寸的大小而对应的程序所需要占用的虚拟内存空间,也会同样切成一段段固定尺寸的大小。 showImg(https://image-static.segmentfault.com/290/765/2907653835-5d580caf245fd_articl...
摘要:反对的意见主要是这样可能会破坏掉无数个脚本,而且中已经有太多的魔法了。除此之外,的命名本身也算是一种包袱。首字母大写的,译作史努比,则是一只被很多人喜爱的漫画小狗。 showImg(https://segmentfault.com/img/remote/1460000019559250); 本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp....
摘要:而大写字母,就是第个,也就是二进制的,对应的十六进制表示就是。在中文世界里,最典型的就是手持两把锟斤拷,口中疾呼烫烫烫的典故。既然今天要彻底搞清楚编码知识,我们就来弄清楚锟斤拷和烫烫烫的来龙去脉。参考深入浅出计算机组成原理 showImg(https://image-static.segmentfault.com/206/872/2068726052-5d5922b2effb9_art...
阅读 1799·2021-11-22 09:34
阅读 3082·2019-08-30 15:55
阅读 661·2019-08-30 15:53
阅读 2050·2019-08-30 15:52
阅读 2999·2019-08-29 18:32
阅读 1988·2019-08-29 17:15
阅读 2391·2019-08-29 13:14
阅读 3556·2019-08-28 18:05