MMU=Segmentation Unit+Paging Unit //MMU: Memory Management Unit
logical address=>Segmentation Unit=>linear address=>Paging Unit=>physical address
Linux系统中采用虚拟内存管理技术来进行内存空间的管理, 即: 每个进程都可以拥有0~4G-1的虚拟内存地址空间(虚拟的,并不是真实存在的), 其中0~3G-1之间的地址空间叫做用户空间,而3G~4G-1之间的地址空间叫做内核空间,由操作系统(Paging Unit)负责建立虚拟内存地址到真实物理内存/文件的映射, 因此不同进程中的虚拟内存地址看起来是一样的, 但所对应的物理内存/文件是不一样的,%p打印出来的是虚拟内存地址(线性地址), 不是真实的地址
一般来说,绝大多数程序都运行在用户空间中, 不能直接访问内核空间, 但是内核提供了一些相关的函数可以用于访问内核空间,
虚拟内存技术可以解决物理内存不够用的问题eg:我们需要4G物理内存=>1G 的真实物理内存,3G的硬盘
内存地址的基本单位是字节, 内存映射的基本单位是内存页, 目前主流的OS一个内存页的大小是4Kb;
Segmentation Fault:
试图操作没有操作权限的内存区域时可能引发段错误, eg: 试图修改只读常量区中的数据内容时
试图访问没有经过映射的虚拟地址时可能引发段错误, eg: 读取顶定义地址(无效地址)中的数据内容时
使用malloc()申请动态内存时, 除了申请参数指定大小的内存空间之外, 还会申请额外的12byte(也可能不是12)用于存储该动态内存的管理信息, eg:大小, 是否空闲etc.
使用malloc()申请动态内存时, 注意避免对内存空间的越界访问, 以避免破坏该动态内存的管理信息, 也就避免Segmentation fault的产生
一般来说, 使用malloc()函数申请比较小块的动态内存时, 操作系统会一次性映射33个内存页的存储空间, 以防止多次malloc, 提高系统效率
malloc()在linux平台的实现会调用sbrk()
eg #include<stdlib.h> int *p1=(int*)malloc(sizeof(int)); //会一口气分配33页, 把p1的管理信息放在p1之后的12byte int *p2=(int*)malloc(sizeof(int)); //因为p1时分配的33页还没用完, 所以直接分配在p1的管理信息后 //管理信息的区域可以写入,但是你写了之后free(p1)就会段错误,所以不要写 //超出33page的内存你访问都不行, 直接段错误 #include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<stdlib.h> int main(){ printf("当前进程进程号:%d\n",getpid()); int *pi=(int*)malloc(sizeof(int)); printf("pi=%p\n",pi); //0x21000 就是33个内存页//0x 1000 就是 1个内存页 //故意越界一下试试, 不超过33内存页的范围 *(pi+1024*30)=250; printf("*(pi+1024*30)=%d\n",*(pi+1024*30)); //没有发生段错误 //故意越界一下试试, 超过33内存页的范围 *(pi+1024*33)=250; //ATTENTION:pi是int*, 所以pi+1024*33可是pi+4*1024*33byte啊 printf("*(pi+1024*33)=%d\n",*(pi+1024*33)); //发生段错误 while(1); return 0; } /* $ ./a.out 当前进程进程号:2787 pi=0x9c40008 *(pi+1024*30)=250 Segmentation fault (core dumped) */ free() a) #include <stdlib.h> void free(void *ptr);frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc() or realloc()
Note:
使用free释放多少, 则从映射的总量中减去多少,当所有的动态内存全部释放时, 系统可能会保留33个内存页, 以提高效率
free() does not check the pointer passed to see whether it is NULL and does not set the pointer to NULL before it returns, while setting a pointer to NULL after freeing is a good practice.If the pointer is NULL, then no action is performed and the program will execute without terminating abnormally;