文章目录
- 基础定义
- 汇编语言的语法
- 常用指令
- 函数的调用机制
- 函数的内部处理机制
- 从汇编语言角度看多线程的安全问题
基础定义
助记符:
例如在加法运算的本地代码中加上 add(addition的缩写)、在比较运算的本地代码中加上 cmp(compare 的缩写)等
这些缩写称为助记符
汇编语言:使用助记符的编程语言称为汇编语言
汇编器:把汇编源代码转换成本地代码的程序叫作汇编器
汇编:汇编器转换的过程称之为汇编
汇编语言的语法
语法结构:是操作码 + 操作数 A(也存在只有操作码没有操作数的指令)
备注:在高级编程语言的源代码中,即使指令和数据在编写时是分散的,编译后也会在段定义中集合汇总起来
图:
汇编语言与 C 语言的区别
1、汇编语言的源代码和本地代码是一一对应的
图:
2、C 语言的源代码同本地代码不是一一对应的 ?
备注:会添加相应的段定义,伪代码和引用的文件库
图:接着往下看
常用指令
mov指令:
指令中最常使用的是对寄存器和内存进行数据存储,mov 指令的两个操作数,分别用来指定数据的存储地和读出源
mov ebp,esp
mov eax,dword ptr [ebp+8]
push、pop指令:
栈是存储临时数据的区域,它的特点是通过 push 指令和 pop 指令进行数据的存储和读出
图:
函数的调用机制
C 语言源码
int AddNum(int a, int b) {return a + b;}void MyFunc(){int c;c = AddNum(123, 456);}
汇编语言代码
_TEXT segment dword public use32 'CODE'_TEXT ends_DATA segment dword public use32 'DATA'_DATA ends_BSS segment dword public use32 'BSS'_BSS endsDGROUP group _BSS,_DATA_TEXT segment dword public use32 'CODE'_AddNum proc near; ; int AddNum(int a, int b); push ebpmov ebp,esp; ; {; return a + b;; mov eax,dword ptr [ebp+8]add eax,dword ptr [ebp+12]; ; }; pop ebpret _AddNum endp_MyFunc proc near; ; void MyFunc(); push ebpmov ebp,esp; ; {; int c;; c = AddNum(123, 456);; push 456push 123call _AddNumadd esp,8; ; }; pop ebpret _MyFunc endp_TEXT endsend
步骤图:
备注:栈结构对传入参数进行压入、压出操作,按照编写顺序进行相应值的转换
函数的内部处理机制
特点:“函数的参数是通过栈来传递,返回值是通过寄存器来返回的”
理由:函数调用完毕,使用的栈内存也跟着销毁了,所以将返回值存储到了寄存器当中
为什么局部变量只能在定义该变量的函数内进行参阅呢?
这是因为,局部变量是临时保存在寄存器和栈中的。正如本章前半部分讲的那样,函数内部利用的栈,在函数处理完毕后会恢复到初始状态,因此局部变量的值也就被销毁了,而寄存器也可能会被用于其他目的。因此,局部变量只是在函数处理运行期间临时存储在寄存器和栈上
从汇编语言角度看多线程的安全问题
情景:
在进行函数调用时,发现与预期结果不符
原因分析:
假设 MyFunc1 函数在读出counter 的数值 100 后,还未来得及将它的 2 倍值 200 写入 counter 时,正巧 MyFunc2 函数读出了 counter 的数值 100,那么结果就会导致counter 的数值变成了 200
// 定义全局变量
int counter = 100;
// 定义 MyFunc1 函数
void MyFunc1()
{counter *= 2;
}
// 定义 MyFunc2 函数
void MyFunc2()
{counter *=2;
}
mov eax,dword ptr[_counter] ; 将 counter 的值读入 eax 寄存器
add eax,eax ; 将 eax 寄存器的值扩大至原来的 2 倍
mov dword ptr[_counter],eax ; 将 eax 寄存器的数值存入 counter 中
图: