Linux中brk(),sbrk(),mmap(),malloc(),calloc()的異同

來源:互聯網
上載者:User

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>

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.