zoukankan      html  css  js  c++  java
  • 理解brk和sbrk

    brksbrk的定义

      在man手册中定义了这两个函数:

     

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

     

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

     

      有了前面program break的概念后,我们来看下brksbrk的作用。brk通过传递的addr来重新设置program break,成功则返回0,否则返回-1。而sbrk用来增加heap,增加的大小通过参数increment决定,返回增加大小前的heapprogram 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
    ", (char *)(&bss_end) + 4);
    11     tret = sbrk(0);
    12     if (tret != (void *)-1)
    13         printf ("heap start: %p
    ", tret);
    14     return 0;
    15 }

     

      运行的结果为:

      从上面运行结果可以知道bssheap是不相邻的,并且同一个程序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
    ", (char *)(&bss_end) + 4);
    13     tret = sbrk(0);
    14     if (tret != (void *)-1)
    15         printf ("heap1 start: %p
    ", tret);
    16     
    17     if (brk((char *)tret - 1) == -1)
    18         printf("brk error
    ");
    19         
    20     tret = sbrk(0);
    21     if (tret != (void *)-1)
    22         printf ("heap2 start: %p
    ", tret);
    23     
    24     pmem = (char *)malloc(32);
    25     if (pmem == NULL) {
    26         perror("malloc");
    27         exit (EXIT_FAILURE);
    28     }
    29     printf ("pmem:%p
    ", pmem);
    30     
    31     tret = sbrk(0);
    32     if (tret != (void *)-1)
    33         printf ("heap1 end: %p
    ", tret);
    34     
    35     if (brk((char *)tret - 10) == -1)
    36         printf("brk error
    ");
    37         
    38     tret = sbrk(0);
    39     if (tret != (void *)-1)
    40         printf ("heap2 end: %p
    ", tret);
    41     return 0;
    42 }

     

      运行结果为:

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

    堆的管理

      上面的函数我们其实很少使用,大部分我们使用的是mallocfree函数来分配和释放内存。这样能够提高程序的性能,不是每次分配内存都调用brksbrk,而是重用前面空闲的内存空间。brksbrk分配的堆空间类似于缓冲池,每次malloc从缓冲池获得内存,如果缓冲池不够了,再调用brksbrk扩充缓冲池,直到达到缓冲池大小的上限,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
    ", tret);
    13                 
    14         pmem = (char *)malloc(64);  //分配内存
    15         if (pmem == NULL) {
    16                 perror("malloc");
    17                 exit (EXIT_FAILURE);
    18         }
    19         printf ("pmem:%p
    ", pmem);
    20         tret = sbrk(0);
    21         if (tret != (void *)-1)
    22                 printf ("heap size on each load: %p
    ", (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

     

     

     

  • 相关阅读:
    五秒原则,做一件事之前数 5 秒,1,2,3,4,5 立马去做。比如睡觉:数五秒,立马放下手机,闭眼。
    Perl 安装 JSON 包
    Perl: hash散列转换为Json报错集, perl.c,v $$Revision: 4.0.1.8 $$Date: 1993/02/05 19:39:30 $
    叫法: 表名 表字段名 定义每个表字段
    失误1: 把i放到循环体内部,i++失效
    沈南鹏@《遇见大咖》: A轮没投,投了8个月以后就证明了张一鸣是对了,在美国都没有张一鸣这种模式
    xshell通过xftp传输Windows文件到Linux:在输入put后,再摁 TAB 键,可显示当前文件夹的文件
    LeetCode84 Largest Rectangle in Histogram
    全排列问题及其引申问题
    LeetCode Weekly Contest 8
  • 原文地址:https://www.cnblogs.com/chengxuyuancc/p/3566710.html
Copyright © 2011-2022 走看看