《程序是怎么跑起来的》第十章 通过汇编了解程序的实际构成

10.1 汇编与本地代码一一对应

在各本地代码中,附带上表示其功能的英语单词缩写,如加法运算的本地代码加上add,这些缩写称为助记符,使用助记符的编程语言称为汇编语言

即使使用汇编语言编写的源代码,最终也需要转换成本地代码才能运行,负责转换工作的程序称为汇编器,转换这一处理本身称为汇编。用汇编语言编写的源代码,和本地代码是一一对应的,所以本地代码也可以转换成汇编语言的源代码,这一过程称为反汇编

在这里插入图片描述

10.2 通过编译器输出汇编语言的源代码

在这里插入图片描述

10.3 不会转换成本地代码的伪指令

汇编语言的源代码,是由转换成本地代码的指令和针对汇编器的伪指令构成的。伪指令负责把程序的构造及汇编的方法指示给汇编器,不过伪指令无法汇编转换成本地代码

在这里插入图片描述
由伪指令segment和ends围起来的部分,是给构成程序的命令和数据的集合体加上一个名字而得到的,称为段定义,段定义指的是命令和数据等程序的集合体的意思,一个程序由多个段定义构成

伪指令proc和endp围起来的部分,表示的是过程的范围,汇编中,这种相当于C语言的函数的形式称为过程

10.4 汇编的语法 = 操作码 + 操作数

汇编语言指令的语法结构是 操作码+操作数。操作码表示的是指令动作,操作数表示的是指令对象

在这里插入图片描述
本地代码加载到内存后才能运行,内存中存储着构成本地代码的指令和数据。程序运行时,CPU会从程序中把指令和数据读出,然后将其存储在CPU内部的寄存器中进行处理
在这里插入图片描述
寄存器并不仅有存储指令和数据的功能,也有运算的功能。寄存器的名称会通过汇编语言的源代码指定给操作数,CPU内部也有程序员无法直接操作的寄存器

在这里插入图片描述

10.5 最常用的mov指令

指令中最常使用的是对寄存器和内存进行数据存储的mov指令。mov指令的两个操作数,分别用来指定数据的存储地和读出源,如果指定了没有用方括号围起来的内容,就表示对改值进行处理,如果指定了用方括号围起来的内容,方括号中的值就会被解释为内存地址,然后对该内存地址对应的值进行读写

如: mov ebp,esp :esp中的值被直接存储到了ebp寄存器中

如:mov eax, dword ptr [ebp + 8]:ebp寄存器的值加8后被解释为内存地址,经过dword ptr后,eax存储了ebp+8这个内存地址中的值

dword ptr 表示的是从指定的内存地址读取出4字节数据

10.6 对栈进行push和pop

程序运行时,会在内存上申请分配一个称为栈的数据空间,栈使用push和pop操作数据

push指令运行后,操作数中指定的值就会被自动push入栈,pop指令运行后,最后存储在栈中的值就会被pop到指定的操作数中出栈
在这里插入图片描述
push指令和pop指令中只有一个操作数,该操作数表示的是‘push以及pop什么’,push和pop操作后,esp寄存器的值会自动更新

10.7 函数调用机制

在这里插入图片描述
第5行执行call指令时,把程序流程跳转到了操作数中指定的AddNum函数所在的内存地址处。AddNum函数处理完毕后,程序流程需要返回到第6行。

call指令运行后,call指令的下一行的内存地址会自动push入栈,该值会在AddNum函数处理的最后通过ret指令pop出栈,然后程序流程返回到第6行

在这里插入图片描述
第6行会把栈中存储的两个参数进行销毁处理,即栈清理处理。虽然通过两次pop也可达成同样的目的,但采用esp+8的方式更有效率

10.8 函数内部的处理

函数的参数是通过栈来传递,返回值是通过寄存器来返回

AddNum函数的具体操作:
在这里插入图片描述
ebp寄存器的值在第1行入栈,第5行出栈,是为了把函数中用到的ebp寄存器的内容,恢复到函数调用前的状态

第3行指定栈中存储的第一个参数123,并将其读出到eax寄存器中,第4行将参数456与eax中的123相加并存储到eax中

第6行ret指令运行后,函数返回目的地的内存地址会自动出栈,程序流程回到函数调用后
在这里插入图片描述

10.9 始终确保全局变量的内存空间

C中,在函数外部定义的变量称为全局变量,在函数内部定义的变量称为局部变量,全局变量可以参阅源代码的任意部分,而局部变量只能在定义该变量的函数内进行参阅

10.10 临时确保局部变量用的内存空间

局部变量是临时保存在寄存器和栈中的,函数内利用的栈,在函数处理完后会恢复到初始状态,因此局部变量的值也就销毁了,而寄存器也可能会用于其它目的

局部变量只是函数处理运行期间临时存储在寄存器和栈上的

10.11 循环处理的实现方法

C中for循环对应的汇编代码:
在这里插入图片描述
C语言的for语句通过在括号中指定循环计数器的初始值(i=0),循环的继续条件(i<10),循环计数器的更新(i++)这3种形式来进行循环处理的
而在汇编语言的源代码中,循环是通过比较指令(cmp)和跳转指令(jl)来实现的

10.12 条件分支的实现方法

条件分支的实现方法同循环处理的方法类似,使用的也是cmp指令和跳转指令
在这里插入图片描述
使用了3种跳转指令,分别是比较结果小时跳转的jle(jump on less or equal),大时跳转的jge,不管结果怎样都跳转的jmp

在汇编语言中,如果不使用相当于C的goto语句的jmp指令,就无法实现分支和循环

10.13 了解程序运行方式的必要性

使用两个函数对100翻倍处理:
在这里插入图片描述
上面的程序如果使用多线程处理时,同时调用了MyFunc1和MyFunc2函数,但结果有可能不是我们需要的400,而是200

线程:是操作系统分配给CPU的最小运行单位,源代码的一个函数就相当于一个线程,多线程处理指的是在一个程序中同时运行多个函数的意思

在这里插入图片描述
为了避免该bug,可以采用禁止线程切换的锁定方法,通过锁定,在特定范围内处理完成之前,处理不会被切换到其它函数中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值