UnixC内存管理那些事儿(下)

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内存管理那些事儿(上)




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值