栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

汇编

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

汇编

文章目录
  • 前言
  • 内存单元地址
  • 段寄存器
    • 8086段寄存器
    • 全部8个段寄存器
  • 位运算
  • 寄存器
  • 内存与内存地址
  • 堆(heap)
  • 栈(stack)

前言

这篇文章可以对汇编语言有一个基础的认识

汇编语言入门教程

内存单元地址

CPU访问内存必须向内存提供物理地址,8086CPU在内部使用如下方法形成最终的物理地址

物理地址=段地址+偏移地址

8086CPU的地址总线长度为20,最大可寻址长度为2^20=1M
但是16位处理器只能处理2^16=64KB

于是采用物理地址=段地址*16+偏移地址,就可以实现1M的物理地址

原理类比:
要写2864,但是只有能写三位数的纸条,
于是第一张纸条写200,第二张写864
结果 = 第一张纸条*10+第二张纸条 = 200*10+864 = 2864


结论:cpu可以用不同的段地址和偏移地址形成同一个物理地址。

段寄存器 8086段寄存器

段寄存器是因为对内存的分段管理而设置的。计算机需要对内存分段,以分配给不同的程序使用(类似于硬盘分页)。在描述内存分段时,需要有如下段的信息:1.段的大小;2.段的起始地址;3.段的管理属性(禁止写入/禁止执行/系统专用等)。需要用8个字节(64位)存储这些信息,但段寄存器只有16位,因此段寄存器中只能存储段号(segment selector,也译作“段选择符”),再由段号映射到存在内存中的GDT(global (segment) descriptor table,全局段号记录表),读取段的信息。

8086CPU有四个段地址寄存器,所有的寄存器都是16位的,可以存放两个字节:CS、DS、SS、ES,在8086系统中,访问存储器的地址码由段地址和段内偏移地址两部分组成。段寄存器用来存放各分段的逻辑基值,并指示当前正在使用的4个逻辑段,包括代码段寄存器CS、堆栈段寄存器SS、数据段寄存器DS和附加段数据寄存器ES。

  1. 代码段寄存器CS(Code Segment):存放当前正在运行的程序代码所在段的段基址,表示当前使用的指令代码可以从该段寄存器指定的存储器段中取得,相应的偏移量则由IP提供。

  2. 数据段寄存器DS(Data Segment):指出当前程序使用的数据所存放段的最低地址,即存放数据段的段基址。

  3. 堆栈段寄存器SS(Stack Segment):指出当前堆栈的底部地址,即存放堆栈段的段基址。

  4. 附加段寄存器ES(Extra Segment):指出当前程序使用附加数据段的段基址,该段是串操作指令中目的串所在的段。

全部8个段寄存器

段寄存器一共有八个: CS(代码段),DS(数据段),SS(堆栈段),ES(拓展段),FS(应用层指向_TEB(线程环境块)内核层指向_KPCR(CPU控制块)),GS(x86下并未使用),TR,LDTR.

  • FS段寄存器:通过读取FS寄存器指定的内存可以获得很多系统关键信息, 主要是和进线程相关的很多信息

参考: [系统底层] 2.段寄存器(Segment Register)

位运算

位运算汇编指令

汇编指令C语言释义
and&与两个位都为1时,结果才为1
or|或两个位都为0时,结果才为0
xor^异或两个位相同为0,相异为1
not~取反0变1,1变0
shl<<左移各二进位全部左移若干位,高位丢弃,低位补0
shr/sar>>右移各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样, 有的补符号位(算术右移),有的补0(逻辑右移)

英文释义(方便记忆)

汇编指令英文释义中文释义
shlShift Logical Left逻辑左移
shrshift arithmetic right逻辑右移
xorexclusive(独有的) OR异或
sarshift arithmetic(算术) right向右算术移动
寄存器

有八个32位通用寄存器

EAX
EBX
ECX
EDX
EDI
ESI
EBP
ESP

特殊寄存器

EIP	# CPU即将执行指令地址,无法作为他用 

16位通用寄存器
就是32位寄存器的拆分

AX
BX
CX
DX
SP
BP
SI
DI

MOV指令

mov指令用于将一个数据从源地址传送到目的地址,例如:mov eax,1,把1给eax

使用mov指令修改32位寄存器的值
修改16位寄存器的值

修改八位寄存器的值

内存与内存地址

每个程序都有自己的独立的4GB的空间 ,但是是虚拟的,用多少给多少

这个内存是怎么得来的呢?

比如我们的计算机为32位,即最大存储单元为2^32Byte = 4294967296Byte = 4194304KB = 4096M = 4G

1Byte = 8位
1KB = 1024Byte
1MB = 1024KB
1GB = 1024MB

内存地址
内存是很大的,无法一一命名,所以就要用编号来标记。

一个编号,就代表一块内存。每个编号最少对应着一个字节。这个编号就叫内存地址。


堆(heap)

程序运行的时候,操作系统会给它分配一段内存,用来储存程序和运行产生的数据。这段内存有起始地址和结束地址,比如从0x1000到0x8000,起始地址是较小的那个地址,结束地址是较大的那个地址。

堆
程序运行过程中,对于动态的内存占用请求(比如新建对象,或者使用malloc命令),系统就会从预先分配好的那段内存之中,划出一部分给用户,具体规则是从起始地址开始划分(实际上,起始地址会有一段静态数据,这里忽略)。举例来说,用户要求得到10个字节内存,那么从起始地址0x1000开始给他分配,一直分配到地址0x100A,如果再要求得到22个字节,那么就分配到0x1020。

这种因为用户主动请求而划分出来的内存区域,叫做 Heap(堆)。它由起始地址开始,从低位(地址)向高位(地址)增长。Heap 的一个重要特点就是不会自动消失,必须手动释放,或者由垃圾回收机制来回收。

栈(stack)

除了 Heap 以外,其他的内存占用叫做 Stack(栈)。简单说,Stack 是由于函数运行而临时占用的内存区域。

请看下面的例子。

int main() {
   int a = 2;
   int b = 3;
}

上面代码中,系统开始执行main函数时,会为它在内存里面建立一个帧(frame),所有main的内部变量(比如a和b)都保存在这个帧里面。main函数执行结束后,该帧就会被回收,释放所有的内部变量,不再占用空间。

如果函数内部调用了其他函数,会发生什么情况?

int main() {
   int a = 2;
   int b = 3;
   return add_a_and_b(a, b);
}

上面代码中,main函数内部调用了add_a_and_b函数。执行到这一行的时候,系统也会为add_a_and_b新建一个帧,用来储存它的内部变量。也就是说,此时同时存在两个帧:main和add_a_and_b。一般来说,调用栈有多少层,就有多少帧。


等到add_a_and_b运行结束,它的帧就会被回收,系统会回到函数main刚才中断执行的地方,继续往下执行。通过这种机制,就实现了函数的层层调用,并且每一层都能使用自己的本地变量。

所有的帧都存放在 Stack,由于帧是一层层叠加的,所以 Stack 叫做栈。生成新的帧,叫做"入栈",英文是 push;栈的回收叫做"出栈",英文是 pop。Stack 的特点就是,最晚入栈的帧最早出栈(因为最内层的函数调用,最先结束运行),这就叫做"后进先出"的数据结构。每一次函数执行结束,就自动释放一个帧,所有函数执行结束,整个 Stack 就都释放了。


Stack 是由内存区域的结束地址开始,从高位(地址)向低位(地址)分配。比如,内存区域的结束地址是0x8000,第一帧假定是16字节,那么下一次分配的地址就会从0x7FF0开始;第二帧假定需要64字节,那么地址就会移动到0x7FB0。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/923463.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号