内存管理主要分为以下几个层次:
- 硬件层次:内存结构管理
- 内核层次:内存映射/堆扩展
- 语言层次:c(malloc)/c++(new)
c语言用malloc依次申请4个字节空间的代码:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int *p1 = malloc(4);
int *p2 = malloc(4);
int *p3 = malloc(4);
int *p4 = malloc(4);
printf("p1 = %p\n",p1);
printf("p2 = %p\n",p2);
printf("p3 = %p\n",p3);
printf("p4 = %p\n",p4);
return 0;
}
打印输出:
p1 = 0x11ca010
p2 = 0x11ca030
p3 = 0x11ca050
p4 = 0x11ca070
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
int main()
{
int *p1 = new int;
int *p2 = new int;
int *p3 = new int;
int *p4 = new int;
printf("p1 = %p\n",p1);
printf("p2 = %p\n",p2);
printf("p3 = %p\n",p3);
printf("p4 = %p\n",p4);
return 0;
}
p1 = 0x1794c20
p2 = 0x1794c40
p3 = 0x1794c60
p4 = 0x1794c80
malloc怎么分配空间?
new 与malloc的关系?
linux对内存的结构描述
在linux系统中,/proc/${pid}/目录下存放进程运行时所有的信息,包括内存结构。进程结束,该目录会清除。运行下面这段代码:
int main()
{
while(1){
};
}
查看该该进程的运行目录如下图:
其中:
exe:指向执行进程的文件
cwd:指向执行进程所在的目录
fd:为进程打开的所有文件
cpuset:是cpu的信息
maps:保存了程序运行的所有内存结构
任何程序的内存空间被分为4个基本部分:
- 代码区
- 全局栈区
- 堆
- 局部栈
include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int add(int a,int b)
{
return a+b;
}
int a1 = 1;
static int a2 = 2;
const int a3 = 3;
int main()
{
int b1 = 4;
static int b2 =5;
const int b3 = 6;
int *p1 = malloc(4);
printf("a1=%p\n",&a1);
printf("a2=%p\n",&a2);
printf("a3=%p\n",&a3);
printf("b1=%p\n",&b1);
printf("b2=%p\n",&b2);
printf("b3=%p\n",&b3);
printf("p1=%p\n",p1);
printf("main=%p",main);
printf("add=%p",add);
printf("pid=%d",getpid());
while(1);
return 0;
}
运行结果为:
a1=0x601050 全局区
a2=0x601054 全局区
a3=0x400774 全局常量在代码区
b1=0x7ffc8f8e4698 局部栈区
b2=0x601058
b3=0x7ffc8f8e469c 局部常量放在局部栈区
p1=0x15ee010
main=0x4005ea
add=0x4005d6
pid=2456
查看该进程的maps文件如下图:
函数调用栈空间的分配与释放
#include<stdio.h>
int add(int a,int b)
{
return a+b;
}
int main()
{
int (*func)(int) =(int(*)(int)) add;
int ret = func(20); //只传入一个参数
printf("ret = %d\n",ret);
int ret2 = func(10,20,30); //传入三个参数,第三个参数没用,能正常得到结果
return 0;
}
//代码能编译通过,执行的结果是错误的。
函数执行的时候,函数拥有自己的临时栈。函数的就在临时栈中,如果函数传递实参,则用来临时参数变量。
__stdcall,__cdecl,__fastcall
1.决定函数栈压栈的参数顺序
2.决定函数栈的清空方式
3.决定了的名字转换方式
虚拟内存的分配
栈:编译器自动的生成代码来负责维护
堆:地址是否映射?,映射空间是否被管理?
1.brk/sbrk 内存映射函数
分配释放函数:
int brk(void* end); //分配空间,释放空间
void *sbrk(int size); //返回空间地址
应用:
- 使用sbrk分配空间
- 使用sbrk得到没有映射的虚拟地址(sbrk(0))
- 使用brk分配空间
- 使用brk释放空间
sbrk(int):如果是第一次调用,则返回没有映射的虚拟地址空间首地址,同时产生一个数据,指向地址。
int main()
{
int *p = sbrk(4); //分配4字节的空间
*p =8888;
printf("%d\n",*p);
return 0;
}