「这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战」。
简介
内存空间布局
从mars中可以查看到内存分布起始物理地址
转成图后:
栈的伸缩在mips和x86架构中是由高地址往低地址进行伸缩, 在arm架构中可升序也可降序
内存碎片
在内存动态分配(heap区)过程中容易出现一些小且不连续的空闲内存区域,这些未被使用的内存称作内存碎片
分类:
- 内部碎片:比如数据在内存中采用4个字节对齐的方式进行存储, 比如我们申请一块3个字节的空间用于存储一个数据,但是系统给我们分配了4个字节空间,这时多出来的一个字节的空间就被称之为内部碎片
- 外部碎片:在我们进行内存回收和分配的时候容易出现外部碎片,比如我连续申请了三块4个字节的内存空间,当我释放第二块内存空间然后紧接着申请一块8个字节的空间,此时由于之前释放的4个字节空间太小无法使用,这就造成了内存块空闲,这种碎片叫做外部碎片
PC 寄存器
称作 程序计数寄存器(Program Counter Register) :用于存储程序即将要执行的指令所对应在内存中的实际物理地址, 如果改变该值可以让指令跳转到我们想要跳转的地方
如何修改pc寄存器中的值
使用以下转移指令
-
jr指令jr 寄存器 #$ra寄存器实际上就是保存着函数调用前的pc寄存器的值 -
jal指令jal 标号 #跳转的同时给$ra寄存器赋值 -
j指令j 标号 #直接跳转
内存数据的读写
-
从指定内存中读取数据
#整型数据 lw $t0,0x10010000 #读取4个字节数据 赋值给t0寄存器 ld $t0,0x10010000 #读取8个字节数据 赋值给t0和t1寄存器 #单精度浮点数据 lwc1 $f0,0x10010000 #双精度浮点数据 ldc1 $f0,0x10010000 #字符数据 ### 由于字符数据是以ascii码16进制的形式存放到内存中,因此只能获取到ascii码值,该值属于整型数据,直接使用lw或者ld即可 lw $t0,0x10010000 #读取4个字节数据 赋值给t0寄存器 ld $t0,0x10010000 #读取8个字节数据 赋值给t0和t1寄存器从内存中读取数据的宽度取决于寄存器的大小,由于32位cpu寄存器最大存储32位数据,因此
lw $t0表示一次性读取4个字节的数据到$t0寄存器, 如果想要连续读取八个字节的数据,那么需要使用ld $t0,表示一次性读取8个字节的数据到$t0,$t1连个连续的寄存器, -
往指定内存中写入数据
- 第一种 数据定义的同时指定物理地址
.data 0x10010020 ;将以下定义的数据存放在0x10010020这个物理地址 .ascii "a" .ascii "b" .data 0x10010000 .ascii "c"- 第二种 在代码段中使用指令
#整型数据 li $s1,4 sw $s1,0x10010000 ;将$s1寄存器中的数据存入0x10010000这个物理地址 #单精度浮点数 .data f1: .float 3.14 .text lwc1 $f2,f1 swc1 $f2,0x10010000 #双精度浮点数 .data d1: .double 3.14 .text ldc1 $f2,d1 sdc1 $f2,0x10010000
以上直接使用的是简单粗暴的十六进制表示物理地址,很多时候内存的地址会保存在寄存器中,你可能会看到以下写法:
lw $s1, $s2
sw $s1, $s2
或者
lw $s1, 20($s2)
sw $s1, 20($s2) ;将地址往高位偏移20个字节 相当于sw $s1, 20+$s2
或者
lw $s1, -20($s2)
sw $s1, -20($s2) ;将地址往低位偏移20个字节
注意: 往指定内存中读取写入数据时,代码段不允许直接写入和读取