STL ==> 内存都是自动分配和释放的
|
C++ ==> new/delete 会调用malloc/free,并调用构造/析构函数
|
C ==> malloc/free
|
UNIX规范(POSIX) ==> sbrk/brk
|
Linux系统 ==>mmap
|
----------------------
系统调用(内核) ==> kmalloc/vmalloc
|
get_free_page() 获取空闲的内存页面
首先简单介绍一下概念:
程序:在磁盘保存的可以运行的文件
进程:正在运行的程序,存在于内存中
一个进程空间被划分为以下部分:
操作系统给每个进程拥有独立的4G空间,操作系统会把进程使用到的逻辑地址,映射到物理地址。
0~3G属于用户空间
3~4G属于内核空间
1)代码区:要执行的程序被放入此区,只读区域(08048000开始的一定是代码区,代码区4K)
2)全局区:保存全局变量,mian函数执行前分配
3)BSS段:保存未初始化的全局变量,BSS段在mian函数执行前会被清0(这就是为什么static变量未初始化默认为0)
4)堆区:也叫自由区,保存new,malloc...出来的变量(堆区可能没有,可能有多个,如果没有malloc,就可能没有堆区)
5)栈区:保存局部变量(包括函数参数),内存分配释放都是自动进行的
栈底的地址是用户可访问的最高地址区域,每次在栈内开辟空间,栈顶指针会变小(即地址变小)
堆开始分配的地址比较低,与全局区相邻,每次在堆内开辟空间,开辟的地址比上一次开辟的地址大。
1.段错误
1.1 对内在空间的操作超出权限的限制,会导致段错误,比如写只读空间
1.2 访问没有对应物理空间的虚拟地址,会导致段错误
2.对空间的分配
malloc会在后台维护一个双向链表的数据结构,并且每次malloc会在结束位置有标记(12字节,存有链表信息)
越界访问修改会破坏标记,从而导致内存释放出现问题,总而言之,使用malloc时不要越界访问。
3.内存页面管理(详细查看内存的段页式管理方式)
操作系统分配物理内存空间时,会按页为单位进行分配。一般系统一页为4096Bytes.操作系统分配好物理空间之后,会给进程
分配虚拟内存与物理空间一一对应起来,进行内存映射。
对于malloc来说,操作系统第一次会直接分配33个页面的物理空间,并做内存映射,在以后的malloc操作,如果需要的内存没有
超过之前分配的33个页面,操作系统不会再次分配新的空间给进程,只有下次malloc时,上次的页面都用完了,系统会一个一个的
分配页面。
测试代码
#include <stdio.h> #include <stdlib.h> int i1 = 10; //全局 int i2 = 20; //全局 int i3; //BSS段 static int i4 = 40; //全局 const int i5 = 50; //代码区 void fa(int i6) //栈区 { int i7 = 70; //栈区 static int i8 = 80; //全局区 const int i9 = 90; //栈区 int *pi = malloc(sizeof(int)); //堆区 char *str = "abcde"; //代码区 char str2[] = "abcde"; //栈区 printf("&i6=%p\n", &i6); printf("&i7=%p\n", &i7); printf("&i8=%p\n", &i8); printf("&i9=%p\n", &i9); printf("pi=%p\n", pi); printf("str=%p\n", str); printf("str2=%p\n", str2); } int main(int argc, const char *argv[]) { printf("&i1=%p\n", &i1); printf("&i2=%p\n", &i2); printf("&i3=%p\n", &i3); printf("&i4=%p\n", &i4); printf("&i5=%p\n", &i5); fa(6); while(1); return 0; }