brk 和 sbrk 区别

转自:https://www.cnblogs.com/chengxuyuancc/p/3566710.html

brk和sbrk的定义,在man手册中定义了这两个函数:

1 #include <unistd.h>
2 int brk(void *addr);
3 void *sbrk(intptr_t increment);

  手册上说brk和sbrk会改变program break的位置,program break被定义为程序data segment的结束位置。感觉这句话不是很好理解,从下面程序地址空间的分布来看,data segment后面还有bss segment,显然和手册说的不太一样。一种可能的解释就是手册中的data segment和下图中的data segment不是一个意思,手册中的data segment应该包含了下图中的data segment、bss segment和heap,所以program break指的就是下图中heap的结束地址。

  有了前面program break的概念后,我们来看下brk和sbrk的作用。brk通过传递的addr来重新设置program break,成功则返回0,否则返回-1。而sbrk用来增加heap,增加的大小通过参数increment决定,返回增加大小前的heap的program break,如果increment为0则返回program break。

  从上面的图可以看出heap的起始地址并不是bss segment的结束地址,而是随机分配的,下面我们用一个程序来验证下:

 1 #include <stdio.h>
 2 #include <unistd.h>
 3
 4 int bss_end;
 5
 6 int main(void)
 7 {
 8     void *tret;
 9
10     printf("bss end: %p\n", (char *)(&bss_end) + 4);
11     tret = sbrk(0);
12     if (tret != (void *)-1)
13         printf ("heap start: %p\n", tret);
14     return 0;
15 }

  运行的结果为:

  从上面运行结果可以知道bss和heap是不相邻的,并且同一个程序bss的结束地址是固定的,而heap的起始地址在每次运行的时候都会改变。你可能会说sbkr(0)返回的是heap的结束地址,怎么上面确把它当做起始地址呢?由于程序开始运行时heap的大小是为0,所以起始地址和结束地址是一样的,不信我们可以用下面的程序验证下。

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <stdlib.h>
 4
 5 int bss_end;
 6
 7 int main(void)
 8 {
 9     void *tret;
10     char *pmem;
11
12     printf("bss end: %p\n", (char *)(&bss_end) + 4);
13     tret = sbrk(0);
14     if (tret != (void *)-1)
15         printf ("heap1 start: %p\n", tret);
16
17     if (brk((char *)tret - 1) == -1)
18         printf("brk error\n");
19
20     tret = sbrk(0);
21     if (tret != (void *)-1)
22         printf ("heap2 start: %p\n", tret);
23
24     pmem = (char *)malloc(32);
25     if (pmem == NULL) {
26         perror("malloc");
27         exit (EXIT_FAILURE);
28     }
29     printf ("pmem:%p\n", pmem);
30
31     tret = sbrk(0);
32     if (tret != (void *)-1)
33         printf ("heap1 end: %p\n", tret);
34
35     if (brk((char *)tret - 10) == -1)
36         printf("brk error\n");
37
38     tret = sbrk(0);
39     if (tret != (void *)-1)
40         printf ("heap2 end: %p\n", tret);
41     return 0;
42 }

  运行结果为:

  程序开始的时候打印出来heap的结束地址,并用这个地址减1来重新设置heap的结束地址,结果两次的结束地址居然是一样的,那说明这个结束地址就是heap的起始地址,再减小这个起始地址是不允许的,不过brk也不会报错。然后调用malloc获取内存,并打印出该内存的起始地址pmem,可以发现pmem与heap的起始地址相差8个字节,为什么会有8个字节没有?这8个字节应该是用来管理heap空间的(不深究)。最后再次获得heap的结束地址,并用这个地址减10来重新设置heap的结束地址,这下地址设置成功了。

堆的管理

  上面的函数我们其实很少使用,大部分我们使用的是malloc和free函数来分配和释放内存。这样能够提高程序的性能,不是每次分配内存都调用brk或sbrk,而是重用前面空闲的内存空间。brk和sbrk分配的堆空间类似于缓冲池,每次malloc从缓冲池获得内存,如果缓冲池不够了,再调用brk或sbrk扩充缓冲池,直到达到缓冲池大小的上限,free则将应用程序使用的内存空间归还给缓冲池。

  如果缓冲池需要扩充时,一次扩充多少呢?先运行下面的程序看看:

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <stdlib.h>
 4
 5 int main(void)
 6 {
 7         void *tret;
 8         char *pmem;
 9
10         tret = sbrk(0);
11         if (tret != (void *)-1)
12                 printf ("heap start: %p\n", tret);
13
14         pmem = (char *)malloc(64);  //分配内存
15         if (pmem == NULL) {
16                 perror("malloc");
17                 exit (EXIT_FAILURE);
18         }
19         printf ("pmem:%p\n", pmem);
20         tret = sbrk(0);
21         if (tret != (void *)-1)
22                 printf ("heap size on each load: %p\n", (char *)tret - pmem);
23     free(pmem)
24     return 0;
25 }

  运行结果如下:

  从结果可以看出调用malloc(64)后缓冲池大小从0变成了0x20ff8,将上面的malloc(64)改成malloc(1)结果也是一样,只要malloc分配的内存数量不超过0x20ff8,缓冲池都是默认扩充0x20ff8大小。值得注意的是如果malloc一次分配的内存超过了0x20ff8,malloc不再从堆中分配空间,而是使用mmap()这个系统调用从映射区寻找可用的内存空间。

参考

  http://blog.csdn.net/sgbfblog/article/details/7772153

原文地址:https://www.cnblogs.com/sylar5/p/11508821.html

时间: 2024-08-02 15:45:37

brk 和 sbrk 区别的相关文章

Linux中brk()系统调用,sbrk(),mmap(),malloc(),calloc()的异同【转】

转自:http://blog.csdn.net/kobbee9/article/details/7397010 brk和sbrk主要的工作是实现虚拟内存到内存的映射.在GNUC中,内存分配是这样的:       每个进程可访问的虚拟内存空间为3G,但在程序编译时,不可能也没必要为程序分配这么大的空间,只分配并不大的数据段空间,程序中动态分配的空间就是从这一块分配的.如果这块空间不够,malloc函数族(realloc,calloc等)就调用sbrk函数将数据段的下界移动,sbrk函数在内核的管理

brk 和 sbrk()的使用 及分配内存方式

brk 和 sbrk的使用: 作用:malloc的底层实现,用于分配开辟内存,但是brk是系统调用 而sbrk不是  ,sbrk调用了brk 用法: 特别注意 : brk返回值 0成功 -1 失败                         sbrk返回的是开辟之前的地址,错误返回-1                开辟后要brk(p)释放掉内存空间简单示例: 1 1 #include <stdio.h> 2 2 #include <unistd.h> 3 3 #include

brk()和sbrk()使用方法解析

brk() , sbrk() 的声明如下: [cpp] view plaincopy #include <unistd.h> int brk(void *addr); void *sbrk(intptr_t increment); 首先说明一点 sbrk()是函数库调用,brk()是系统调用 这两个函数都用来改变 "program break" (程序间断点)的位置,这个位置可参考下图: 如 man 里说的: 引用 brk()  and  sbrk() change the

菜鸟随笔(2)---brk()与sbrk()函数的学习与使用

一只菜鸟横空出世,码农世界闯一闯,每天进展多一丢丢. brk()与sbrk()函数的学习与使用 brk()与sbrk()函数定义如下: #include <unistd.h> int brk(boid *addr): addr:把内存末尾指针设置为addr.返回值:0表示成功,非0表示失败 void *sbrk(intptr_t increment); increment:把内存的末尾指针移动increment个字节.返回值:上次调用sbrk/brk的内存末尾指针. 内容摘选转自:https:

linux内存分配与brk(), sbrk()原理与应用

在Linux系统上,程序被载入内存时,内核为用户进程地址空间建立了代码段.数据段和堆栈段,在数据段与堆栈段之间的空闲区域用于动态内存分配.内核数据结构mm_struct中的成员变量start_code和end_code是进程代码段的起始和终止地址,start_data和 end_data是进程数据段的起始和终止地址,start_stack是进程堆栈段起始地址,start_brk是进程动态内存分配起始地址(堆的起始地址),还有一个 brk(堆的当前最后地址),就是动态内存分配当前的终止地址. 每个

Linux系统下深究一个malloc/brk/sbrk新内存后的page fault问题

有耳可听的,就应当听 -<马可福音> 周四的休假团建又没有去,不因别的,只因年前东北行休假太多了,想缓缓-不过真实原因也确实因为假期剩余无几了-思考了一些问题,写下本文. ??本文的缘起来自于和同事讨论一个关于缺页中断按需调页的讨论.真可谓是三人行必有我师,最近经常能从一些随意的比划或招架中悟出一丝意义,所以非常感谢周围的信息输出者!甚至从小小学校全员禁言的作业群里,我都能每天重温一首古诗词,然后循此生意,去故意制造另一种真实的意境,然后发个朋友圈?~ ??感谢大家的信息输入,每次收到的好玩的

brk(), sbrk() 用法详解

brk() , sbrk() 的声明如下: [cpp] view plaincopy #include <unistd.h> int brk(void *addr); void *sbrk(intptr_t increment); 这两个函数都用来改变 "program break" (程序间断点)的位置,这个位置可参考下图: 如 man 里说的: 引用 brk()  and  sbrk() change the location of the program break,

brk()和sbrk()函数的使用

根据上一篇文章继续解释 brk和sbrk的定义 在man手册中定义了这两个函数: 1 #include <unistd.h> 2 int brk(void *addr); 3 void *sbrk(intptr_t increment); 手册上说brk和sbrk会改变program break的位置,program break被定义为程序data segment的结束位置.感觉这句话不是很好理解,从下面程序地址空间的分布来看,data segment后面还有bss segment,显然和手册

内存映射

http://www.cnblogs.com/mahaikai/p/6025512.html 在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中.这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的内存地址块.在Linux系统中, 内核进程和用户进程所占的虚拟内存比例是1:3,而Windows系统为2:2(通过设置Large-Address-Aware Executables标志也可为1:3).这并不意味着内核使用那么多物理内存,仅表示它可