虚拟内存的分配brk/sbrk
可以看到一个进程地址空间的主要成分为:
-
正文:这是整个用户空间的最低地址部分,存放的是指令(也就是程序所编译成的可执行机器码)
-
初始化数据段:这里存放的是初始化过的全局变量
-
未初始化数据段:这里存放的是未初始化的全局变量
-
Heap:堆,这是我们本文重点关注的地方,堆自低地址向高地址增长,后面要讲到的brk相关的系统调用就是从这里分配内存
-
Stack:这是栈区域,自高地址向低地址增长
-
命令行参数和环境变量:用户调用的最底层。
由上文知道,要增加一个进程实际的可用堆大小,就需要将break指针向高地址移动。Linux通过brk和sbrk系统调用操作break指针。两个系统调用的原型如下:
栈:编译器自动生成代码维护
堆:地址是否映射,映射的空间是否被管理.
1.brk/sbrk 内存映射函数
malloc是c中常用的内存操作函数,malloc动态的申请一块指定大小的内存,方便存放数据
c++中的new实际上除了malloc分配内存之外还会调用构造函数初始化数据
而brk/sbrk则是实现malloc的底层函数,其中brk是系统调用。操作起来更为灵活,但很多人往往不容易理解。
- int brk(void *addr); //分配空间,释放空间
- void *sbrk(intptr_t increment); //返回空间地址
返回值:
brk()成功返回0,出错返回-1,并且errno被设置为ENOMEM
sbrk()成功返回上一个程序结束点,出错返回-1,并且errno被设置为 ENOMEM
brk()和sbrk()改变程序间断点的位置(break指针)。程序间断点就是程序数据段的结尾。(程序间断点是为初始化数据段的起始位置).通过增加程序间断点进程可以更有效的申请内存
当addr参数合理、系统有足够的内存并且不超过最大值时brk()函数将数据段结尾设置为addr,即间断点设置为addr
sbrk()将程序数据空间增加increment字节。当increment为0时则返回程序间断点的当前位置。
什么是程序间断点呢?
可以这样理解,进程在内存中被分为代码区,数据区,栈区和堆区。程序间断点指向堆区的起始位置。同时他也是数据段的结尾。
Linux进程内存分布,地址从低到高依次是代码段,数据段,堆,栈,堆栈之间是mmap映射的共享内存空间以及共享库,再上是命令行参数,环境变量等,其中栈是从高地址向低地址分配,堆是从低地址向高地址分配。程序间断点就是当前进程映射虚拟地址的终止位置,通过移动这个位置来维护进程的映射的内存.而brk/sbrk的作用就是维护这个位置.
brk改变绝对位置
sbrk相对改变位置 >0则增加位置 <0则收缩位置
#include <unistd.h> #include <stdio.h> #include <stdlib.h> main() { void *p; void *old; //存放原始间断点位置 int r; old = p = sbrk(0); //sbrk(0)返回当前的程序间断点 if (p == (void*)-1) printf("sbrk error\n"), exit(-1); printf("当前程序间断点位置:%p\n", p); p = sbrk(1); //位置+1,但返回的是之前的位置而不是+1后的位置 printf("%p\n", p); p = sbrk(1); //位置实际+2,但返回的是位置+1的值 printf("%p\n", p); r = brk(old); //将程序间断点设置为原始位置 if (r == -1) printf("brk error\n"), exit(-1); printf("原始位置:%p\n", sbrk(0));//输出原始位置与第一次调用sbrk(0)的位置相同 }
输出结果:
应用brk/sbrk小案例,求1-1000的素数.
#include <unistd.h> #include <stdio.h> #include <stdlib.h> int judge(int num) { int i; for(i=2; i<num; i++) { if(num%i == 0) return 0; } return 1; } main() { int *p; void *old; int i; old = sbrk(0); if (old == -1) printf("sbrk error\n"), exit(-1); p = (int*)old; for(i=1; i<=1000; i++) { if(judge(i) == 1) { p = sbrk(4); //int类型所以又移4个字节 if (p == -1) printf("brk error\n"), exit(-1); *p = i; } } p = (int*)old; while(p != sbrk(0)) { printf("%d ", *p); p ++; } if (brk(old) == -1) //输出完毕,收回空间 printf("brk error\n"), exit(-1); }

浙公网安备 33010602011771号