先天是不能够存储数据的,否则会引发段错误。虚拟内存地址,只有映射零物理内存/硬盘文件后,才能存
储数据,才占据内存。
虚拟内存地址分为用户空间和内核空间。0-3G为用户空间,3-4G是内核空间。用户空间不能直接访问内核空间,但可以通过系统提供的函数进入内核空间。内存地址的基本单位是字节,内存映射的基本单位是内存页,一个内存页是4k(或者4k的倍数)。
内存分配和回收的函数(运算符)
STL -》 自动分配,自动回收
|
C++ -》new ,delete
|
c语言 -》malloc() free()
|
Unix系统函数 -》brk() sbrk()
|
Unix系统函数 -》mmap(),munmap() 用户层
————————————————————————————
|
Unix系统函数 -》kmalloc() 内核层
1)malloc 和 free
malloc只有在第一次时映射内存,一次映射33个内存页。如果用完, 才再次映射。
如果系统映射超过33页,系统就会映射稍多一点内存。
需要注意的是:
malloc分配内存时,系统会额外占用一些空间,用于存储下一个空间的 附加信息。
int *p1 = malloc(4); //内存映射,映射33个内存页
int *p2 = malloc(4);//只分配,不再映射内存
int *p3 = malloc(4);
int *p4 = malloc(12);
printf("%p %p %p %p \n",p1,p2,p3,p4);//堆之间的地址并没有连续
//p1地址前面存放着附加信息,将附加信息清零 ==》错误:已放弃,核心已转移
*(p1 -1) = 0; 。
free(p1);
free()一定释放虚拟内存,但是不保证解除物理内存的映射,而且最后的33个内存页是无法free的,进程
结束才能解除最后的33个内存页。为了提高效率, 用空间换时间。
例如,我首先分配内存
void *p = malloc(1);//此时系统一次性映射了33个内存页
free(p); //此时并没有解除物理内存的映射.
2) sbrk 和 brk
sbrk()和brk()底层维系了一个位置。通过位置的移动来分配和回收内存。
void sbrk(intptr_T increment)
increment 移动增量负数逆向移动(释放内存),正数向前移动(分配内存),
为0返回当前位置。成功返回移动之前的位置,失败返回零。
sbrk实打实的每次一次分配一页空间,如果超出一页就再分配一页。
char *p_num = sbrk(4);//分配了四个字节的内存,返回首地址
*p_num = 100;
printf("%d\n",*p_num); //打印出100
sbrk(-4);//释放3个字节内存,返回值没有意义
brk 函数决定了所分配空间的结尾地址.如下:
void *p = sbrk(0);//返回当前地址,并不映射内存。
brk(p+4); //分配了四个字节,映射了一页地址。
brk(p+8); //重新分配四个字节
brk(p+4);//回收四个字节
sbrk用来分配内存方便,而brk用来释放内存更方便.(两个函数都可以用来分配和释放内存)
//分配空间存放int
int *pi = (int*)sbrk(4);
*pi = 100;
//分配空间存放double
double *pd = (double*)sbrk(8);
*pd = 1.0;
//分配空间存放string
char *p_str = (char*)sbrk(6);
strcpy(p_str,"abcde");
//释放所有空间
brk(pi);
3)mmap,munmap
用来映射或者取消映射.代码如下:
#include<sys/mman.h>
#include<unistd.h>
#include<string.h>
int main()
{
void *p_map = mmap(0, //内核选择虚拟首地址
1, //映射的字节数,不足内存页补足
PROT_READ | PROT_WRITE,//读写权限
MAP_SHARED | MAP_ANONYMOUS,//共享,匿名
-1,0); //映射一页内存
if( p_map == MAP_FAILED)
{
perror("mmap");
return -1;
}
int *pi = p_map;
*pi = 100;
strcpy(pi + 4,"kongdeju"); //证明映射了一页。
printf("%d,%s\n",*pi,(char*)(pi + 4));
munmap(p_map,1);//取消映射
return 0;
}