理解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\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 }

 

  運行的結果為:

  從上面運行結果可以知道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\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,可以發現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\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

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM