Unix系统调用之sbrk():
sbrk()和brk()是Unix的系统函数,机制和malloc()完全不同,借助系统维护的一个位置进行内存的分配和回收
void* sbrk(int increment)
功能:分配/回收内存,大多数情况下用于分配内存
参数increment:分配/回收的增量,为正数表示分配内存,为负数时表示回收,为0时取当前的位置
返回值:返回移动之前的位置,如果出错返回(void*)-1
sbrk()在分配内存是非常方便,但是回收内存时需要计算字节数,因此比较麻烦,不利
于管理,brk()则相反,一般情况下,都是使用sbrk()分配内存,使用brk()释放内
存(虽然两个函数都可以分配和回收)
sbrk内存分配过程:
在sbrk()函数分配的过程中,有一个指针指向的是分配成功的内存块的尾地址,但是函数返回的却是移动前的位置,即内存块的首地址。当我们有sbrk函数来回收内存时(如图中sbrk(-2)所示),成功后返回的是释放前的内存块的尾地址,因为该内存已被释放,所以要先用sbrk(0)定位到内存分配指针的位置才能使用内存。
例子:
#include <stdio.h>
#include <unistd.h>
int main(){
printf("pid=%d\n",getpid());
int*p1 = sbrk(4);//分配+映射
int*p2 = sbrk(4);//分配
int*p3 = sbrk(4);//分配
printf("%p,%p,%p\n",p1,p2,p3);
sbrk(-4);//释放内存时返回值没意义
sbrk(-4);
int*p4 = sbrk(0);
printf("p4=%p\n",p4);//p1,p2
sleep(15);
int*p5 = sbrk(4093);
printf("4097byte\n");
sleep(15);
sbrk(-1); //回到一页
printf("4096byte\n");
sleep(15);
sbrk(-4096); //全部释放,同时解除映射
printf("freeall\n");
while(1);
}
Unix系统调用之brk():
int brk(void* position)
功能:分配/回收内存,一般用于回收内存
参数position就是新的位置,无论原来的位置在哪里
返回:成功返回0,失败返回-1
sbrk()和brk()都是以一个内存页作为映射的基本单位,一旦释放就会同时解除内存映射
brk()和sbrk()都不会清除回收内存的数据,数据会在下次使用被覆盖。可以使用memset函数自行清空内存
brk内存分配与回收过程:
如果觉得看不明白,那么你可以采用这样一种方式来理解,操作系统在分配内存时有一个指针指向了内存空间。在这个指针的左侧为用户分配的内存,右侧则为未分配的内存。当你使用brk()函数时,就等于是让这个指针移动到了你的目标位置,而不管你原来的位置在哪里。当你使用brk(p+4)是就意味着内存分配指针(以下简称指针)移动到了图中4个字节的位置,即分配了4个字节的内存空间。而使用brk(p+8)则意味着指针移动到了图中分配4个字节的位置,即在原有的基础上又再分配了4个字节的内存空间。最后当你使用brk(p+4),意味着指针移动到了图中蓝色箭头所指向的位置,即回收了第二次分配的那4个字节的内存。由此我们可以得出,我们只需要将内存块的首地址交给brk函数即可回收掉这个从这块内存块首地址开始往后的所有内存。
例子:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(){
void* p = sbrk(0);
brk(p+4); //分配4个字节
brk(p+8); //又分配4个字节
brk(p+4); //回收4个字节
int* pi = p;//存放一个int
*pi = 100;
double *pd = sbrk(0);
brk(pd+1);
*pd = 3.14;
char *pc = sbrk(0);
brk(pc+10);
strcpy(pc,"zhangfei");
printf("%d,%lf,%s\n",*pi,*pd,pc);
printf("%d\n",brk(pc)); //回收pc的内存
printf("%d\n",brk(pd)); //回收pd的内存
printf("%d\n",brk(pi)); //回收pi的内存
return 0;
}
Unix系统调用之mmap():
mmap()是用户层能使用的功能最强大的函数,可以设置一些内存相关的数据
mmap()实现内存映射,包括映射物理内存和硬盘文件,但映射硬盘文件需要使用文件描
述符,需要文件的相关函数做支持
void* mmap(void* addr,size_t size,intprot,int flags,int fd,off_t offset);
功能:内存映射,比sbrk()强大的多,失败返回-1
参数:addr指定映射的首地址,为0交给内核选择
size就是映射内存的大小
prot即使内存的权限,一般PROT_READ|PROT_WRITE,多个权限和多个选项用位或|连接
flags是映射的标识,主要包括:MAP_SHARED MAP_PRIVATE 2选1
MAP_SHANED代表这块内存其他进程可以共享,但只对映射文件有效。
MAP_PRIVATE就是其他进程不能共享,只能本进程使用。
MAP_ANONYMOUS代表映射物理内存,不写就映射硬盘文件(默认映射)
fd是文件描述符,映射文件时有效,映射物理内存为0
offset是文件的偏移量,选择映射文件的位置,映射物理内存为0
返回:成功返回映射的首地址,失败返回MAP_FAILED
Unix系统调用之munmap():
void* mmap(void* addr,size_t size)
功能:回收addr所指向的长度为size的内存空间。
参数:addr指定映射的首地址
size表示映射内存的大小
返回:成功返回0,失败返回-1
例子:
#include <stdio.h>
#include <sys/mman.h>
int main(){
int* pi = mmap(NULL,4,//choose the address of memory by kernel
PROT_READ|PROT_WRITE, //file operating authority
MAP_PRIVATE|MAP_ANONYMOUS, //
0,0); //file description
if(pi == MAP_FAILED){
perror("mmap");
return -1;
}
*pi = 100;
printf("%d\n",*pi);
printf("%d\n",*pi);
munmap(pi,4); //free memory
return 0;
}
PS:Unix操作系统内存相关实现及malloc()函数和free()函数细节请参见我的另一篇博文——UnixC内存管理那些事儿(上)