一,内存管理
在linux系统编程中,主要有以下几种内存分配和释放方式,他们是一一对应的;
mmap/munmap,sbrk/brk,malloc,calloc,realloc/free;
以下是函数的原型:
void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);
函数功能:映射一片内存空间或者将一个文件映射到一片虚拟内存空间,直接通过操作内存的方式来操作文件;
第一个参数:指定映射的起始虚拟地址,如果为0,则起始地址由系统指定;
第二个参数:映射的空间的大小,建议为4K的倍数,不是4K的倍数会自动对齐;
第三个参数:映射内存的权限(内存保护方式),PROT_READ,PROT_WRITE,PROT_EXEC依次为 读,写,执行;
第四个参数:内存的标志位,也就是映射模式,MAP_PRIVATE内存私有只能在本进程访问,MAP_SHARED内存共享
MAP_ANONYMOUS 匿名映射,如果使用该标志位,则文件描述符fd,文件开始映射的位置offset会无效.
第五个参数:一个已经打开的文件的描述符,如果进行文件映射,则上一参数不能使用匿名映射选项MAP_ANONYMOUS;
第六个参数:文件开始映射的位置.
int munmap(void *start, size_t length); 用来释放mmap分配的内存
该函数主要用来解除映射,参数分别为要解除的地址,该地址所对应空间的大小;
mmap的一个小例子:
#include <sys/mmam.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h>
int main() { int *p; printf("pid=%d\n",getpid()); //物理内存映射 p=mmap(0,4,PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_SHARED,0,0); //如果映射成功 if(!p) printf("内存分配失败\n"),exit(0); printf("成功建立内存映射\n"); *p=8090; printf("*p=%d\n",*p); munmap(p,4); printf("释放OK!\n"); return 0; }
void *sbrk(intptr_t increment); 分配和修改内存,返回上次sbrk/brk后的内存的末尾地址
int brk(void *end_data_segment); 修改内存末尾地址
一个小列子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
void *p=sbrk(0); //系统会默认给一个起始地址
printf("%p\n",p);
int r=brk(p+4); //将指针p扩充4个字节
brk(p+8);
printf("%p\n",p+8);
*((int*)p)=4;
*((int*)(p+4))=6;
//由于sbrk在第一次分配内存时,映射了一个page大小的内存,所以下边的访问不会出现段错误
*((int*)(p+8))=8; //没有进行分配,但是可以访问,这种内存存在危险,破坏数据
if(0==r)printf("修改内存成功!\n");
else printf("%m\n");
printf("%d******%d********%d\n",*((int*)p),*((int*)(p+4)),*((int*)(p+8)));
printf("pagesize=%d\n",getpagesize());
//下边两行能正常执行 因为内存地址在pagesize范围内,访问的是已经映射过的内存
*((int*)(p+4092))=4092;
printf("%d\n",*((int*)(p+4092)));
//下边两行出现段错误
*((int*)(p+4096))=4096;
printf("%d\n",*((int*)(p+4096)));
return 0;
}
mmap主要用来分配较大的内存空间,比sbrk/brk底层,而malloc是c的标准库函数,他的底层实现也是用sbrk/brk来实现的,适合分配较小的内存块,
由于malloc分配的内存在系统中以一个链表来维护,这样一来便可以合理利用内存碎片;