brk和sbrk及內存分配函數相關

brk和sbrk主要的工作是實現虛擬內存到內存的映射.在GNUC中,內存分配是這樣的:
       每個進程可訪問的虛擬內存空間爲3G,但在程序編譯時,不可能也沒必要爲程序分配這麼大的空間,只分配並不大的數據段空間,程序中動態分配的空間就是從這一塊分配的。如果這塊空間不夠,malloc函數族(realloc,calloc等)就調用sbrk函數將數據段的下界移動,sbrk函數在內核的管理下將虛擬地址空間映射到內存,供malloc函數使用。(參見linux內核情景分析)

#include <unistd.h>

       int brk(void *end_data_segment);

       void *sbrk(ptrdiff_t increment);

DESCRIPTION
       brk   sets   the   end   of   the   data   segment   to   the value specified by end_data_segment, when that value is reasonable, the system   does   have enough   memory   and   the process does not exceed its max data size (see setrlimit(2)).

       sbrk increments the program's data   space   by   increment   bytes.    sbrk isn't a system call, it is just a C library wrapper.   Calling sbrk with an increment of 0 can be used to find the current location of the   program break.

RETURN VALUE
       On   success,   brk returns zero, and sbrk returns a pointer to the start of the new area.   On error, -1 is returned, and errno is set to ENOMEM.


sbrk不是系統調用,是C庫函數。系統調用通常提供一種最小功能,而庫函數通常提供比較複雜的功能。

在Linux系統上,程序被載入內存時,內核爲用戶進程地址空間建立了代碼段、數據段和堆棧段,在數據段與堆棧段之間的空閒區域用於動態內存分配。

內核數據結構mm_struct中的成員變量start_code和end_code是進程代碼段的起始和終止地址,start_data和 end_data是進程數據段的起始和終止地址,start_stack是進程堆棧段起始地址,start_brk是進程動態內存分配起始地址(堆的起始地址),還有一個 brk(堆的當前最後地址),就是動態內存分配當前的終止地址。

C語言的動態內存分配基本函數是malloc(),在Linux上的基本實現是通過內核的brk系統調用。brk()是一個非常簡單的系統調用,只是簡單地改變mm_struct結構的成員變量brk的值。

mmap系統調用實現了更有用的動態內存分配功能,可以將一個磁盤文件的全部或部分內容映射到用戶空間中,進程讀寫文件的操作變成了讀寫內存的操作。在 linux/mm/mmap.c文件的do_mmap_pgoff()函數,是mmap系統調用實現的核心。do_mmap_pgoff()的代碼,只是新建了一個vm_area_struct結構,並把file結構的參數賦值給其成員變量m_file,並沒有把文件內容實際裝入內存。
Linux內存管理的基本思想之一,是只有在真正訪問一個地址的時候才建立這個地址的物理映射。

==================================================================================
C語言跟內存分配方式
(1) 從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量,static變量。
(2) 在棧上創建。在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運

算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。
(3)從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc或new申請任意多少的內存,程序員自己負責在何時用free或delete釋放內存。動態內存的生存期由我們決定,使用非常靈活,但問題也最多

C語言跟內存申請相關的函數主要有 alloc,calloc,malloc,free,realloc,sbrk等.其中alloc是向棧申請內存,因此無需釋放. malloc分配的內存是位於堆中的,並且沒有初始化內存的內容,因此基本上malloc之後,調用函數memset來初始化這部分的內存空間.calloc則將初始化這部分的內存,設置爲0. 而realloc則對malloc申請的內存進行大小的調整.申請的內存最終需要通過函數free來釋放. 而sbrk則是增加數據段的大小;

malloc/calloc/free基本上都是C函數庫實現的,跟OS無關.C函數庫內部通過一定的結構來保存當前有多少可用內存.如果程序 malloc的大小超出了庫裏所留存的空間,那麼將首先調用brk系統調用來增加可用空間,然後再分配空間.free時,釋放的內存並不立即返回給os, 而是保留在內部結構中. 可以打個比方: brk類似於批發,一次性的向OS申請大的內存,而malloc等函數則類似於零售,滿足程序運行時的要求.這套機制類似於緩衝.

使用這套機制的原因: 系統調用不能支持任意大小的內存分配(有的系統調用只支持固定大小以及其倍數的內存申請,這樣的話,對於小內存的分配會造成浪費; 系統調用申請內存代價昂貴,涉及到用戶態和核心態的轉換.
函數malloc()和calloc()都可以用來分配動態內存空間,但兩者稍有區別。  
     malloc()函數有一個參數,即要分配的內存空間的大小:   
     void *malloc(size_t size);
     calloc()函數有兩個參數,分別爲元素的數目和每個元素的大小,這兩個參數的乘積就是要分配的內存空間的大小:  
     void *calloc(size_t numElements,size_t sizeOfElement);
     如果調用成功,函數malloc()和calloc()都將返回所分配的內存空間的首地址。
     malloc() 函數和calloc()函數的主要區別是前者不能初始化所分配的內存空間,而後者能。如果由malloc()函數分配的內存空間原來沒有被使用過,則其中的每一位可能都是0;反之,如果這部分內存空間曾經被分配、釋放和重新分配,則其中可能遺留各種各樣的數據。也就是說,使用malloc()函數的程序開始時(內存空間還沒有被重新分配)能正常運行,但經過一段時間後(內存空間已被重新分配)可能會出現問題。
     calloc() 函數會將所分配的內存空間中的每一位都初始化爲零,也就是說,如果你是爲字符類型或整數類型的元素分配內存,那麼這些元素將保證會被初始化爲零;如果你是爲指針類型的元素分配內存,那麼這些元素通常(但無法保證)會被初始化爲空指針;如果你是爲實數類型的元素分配內存,那麼這些元素可能(只在某些計算機中)會被初始化爲浮點型的零。
     malloc() 函數和calloc()函數的另一點區別是calloc()函數會返回一個由某種對象組成的數組,但malloc()函數只返回一個對象。爲了明確是爲一個數組分配內存空間,有些程序員會選用calloc()函數。但是,除了是否初始化所分配的內存空間這一點之外,絕大多數程序員認爲以下兩種函數調用方式沒有區別:
     calloc(numElements ,sizeOfElement);
     malloc(numElements *sizeOfElement) ;
     需要解釋的一點是,理論上(按照ANSIC標準)指針的算術運算只能在一個指定的數組中進行,但是在實踐中,即使C編譯程序或翻譯器遵循這種規定,許多C 程序還是衝破了這種限制。因此,儘管malloc()函數並不能返回一個數組,它所分配的內存空間仍然能供一個數組使用(對realloc()函數來說同樣如此,儘管它也不能返回一個數組)。
     總之,當你在calloc()函數和malloc()函數之間作選擇時,你只需考慮是否要初始化所分配的內存空間,而不用考慮函數是否能返回一個數組。
     當程序運行過程中malloc了,但是沒有free的話,會造成內存泄漏.一部分的內存沒有被使用,但是由於沒有free,因此係統認爲這部分內存還在使用,造成不斷的向系統申請內存,是的系統可用內存不斷減少.但是,內存泄漏僅僅指程序在運行時,程序退出時,OS將回收所有的資源.因此,適當的重起一下程序,有時候還是有點作用.

sbrk(int incr) 本函數用來增加分配給調用程序的數據段的空間數量,增加incr個字節的空間brk函數的原形是:int    brk(void    *endds)
   它的功能是:更改數據段空間的分配
   char    *p;
   p=malloc(1);
   這時p指向的內存空間大小是1    byte
   brk(p+100)
   這時p指向的內存空間大小是101    bytes

      程式分配虛擬內存也不是你要一個字節就給你一個字節,而是你要一個字節給你一個頁面,因爲映射物理內存時只能以頁爲單位。你要另一個字節時,它在這個頁面的剩餘空間給你。

注意大部份UNIX虛擬內存的使用是隻增不減的。

 

CODE:malloc(32 * 1024) --->;sbrk += 32 * 1024
free()    --->;sbrk 不減少。
但如如果再來一次
malloc(32 * 1024) ---->;sbrk 也不增,使用原有空間.
但對於LINUX來說它是要以內存的最大數收縮的;

CODE:<code>
a = malloc(32 * 1024) -->;sbrk += 32 * 1024
b = malloc(32 * 1024) -->;sbrk += 32 * 1024
if(****){
free(b); --->;sbrk -= 32 * 1024;
}
else{
free(a); --->;sbrk 不減少。只是多了個空洞.
}
</code>
CODE:<code>
/* linux kernel code */
brk()
/*
*   sys_brk() for the most part doesn't need the global kernel
*   lock, except when an application is doing something nasty
*   like trying to un-brk an area that has already been mapped
*   to a regular file.   in this case, the unmapping will need
*   to invoke file system routines that need the global lock.
*/
asmlinkage unsigned long sys_brk(unsigned long brk)
{
unsigned long rlim, retval;
unsigned long newbrk, oldbrk;
struct mm_struct *mm = current->;mm;
down_write(&mm->;mmap_sem);
if (brk < mm->;end_code)
goto out;
newbrk = PAGE_ALIGN(brk);
oldbrk = PAGE_ALIGN(mm->;brk);
if (oldbrk == newbrk)
goto set_brk;
    /******虛擬內存在這裏收縮******/
/* Always allow shrinking brk. */
if (brk <= mm->;brk) {
if (!do_munmap(mm, newbrk, oldbrk-newbrk))
goto set_brk;
goto out;
}
/* Check against rlimit.. */
rlim = current->;rlim[RLIMIT_DATA].rlim_cur;
if (rlim < RLIM_INFINITY && brk - mm->;start_data >; rlim)
goto out;
/* Check against existing mmap mappings. */
if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE))
goto out;
/* Check if we have enough memory.. */
if (!vm_enough_memory((newbrk-oldbrk) >;>; PAGE_SHIFT))
goto out;
/* Ok, looks good - let it rip. */
if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
goto out;
set_brk:
mm->;brk = brk;
out:
retval = mm->;brk; /****這就是返回值*****/
up_write(&mm->;mmap_sem);
return retval;
}
</code>
在LINUX中sbrk(0)能返回比較精確的虛擬內存使用情況,
在SOLARIS/HP中sbrk(0)返回以頁爲單位的虛擬內存使用情況。使用sbrk(0)來返回程式當前使用了多少內存。

<code>
main(){
int start,end;
start = sbrk(0);
....
malloc(***);
....
end = sbrk(0);
printf("hello I used %d vmemory",end - start);
}

</code>

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/ugg/archive/2009/07/13/4344522.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章