歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Glibc 內存申請和釋放及堆連續檢查

Glibc 內存申請和釋放及堆連續檢查

日期:2017/3/1 9:13:25   编辑:Linux編程

C語言有兩種內存申請方式:

1、靜態申請:當你聲明全局或靜態變量的時候,會用到靜態申請內存。靜態申請的內存有固定的空間大小。空間只在程序開始的時候申請一次,並且不再釋放(除非程序結束)。

2、自動申請:當你聲明自動變量的時候會使用自動申請。函數參數、局部變量都屬於自動變量。這些變量空間在程序執行致相關語句塊申請,離開語句塊時釋放。

還有一種內存申請方式:動態內存申請。C語言變量並不支持動態內存申請,這一功能由庫函數實現。C裡面沒有動態這個存儲類型!!

當你需要存儲一些數據,但又不知道這些數據有多大或者這些數據會不斷的增加。這是後使用動態申請內存技術會非常有用。動態申請是一種顯示申請行為,你必須調用相關函數或者宏,並指名申請的大小。申請到的內存空間只能使用指針訪問,動態內存還會消耗額外的計算時間,使用起來頗為不便。所以除非必須自動申請和靜態申請都不管用,才會考慮動態申請。當然啦,動態申請是一項很重要的技術,沒了他不行。

malloc函數原型:

void *malloc(size_t size)

說明:  malloc函數申請的內存空間並不會初始化。我們需要手動使用memset函數將內存快置零。

內存對齊:malloc申請到的內存在32位系統中以8的倍數對齊,64位系統中以16的倍數對齊。如果想以一整頁對齊,則需要aligned_alloc或者posix_memalign函數。

返回值:無法申請內存時,返回空指針。

memset函數原型:

void *memset(void *block, int c, size_t size)

如果你想為字符串申請內存,記得大小為長度+1——字符串的最後的空字符並沒有被計入長度。

char *ptr;
...
ptr = (char *) malloc (length+1); //強制轉換

  內存釋放函數(用來釋放malloc申請的內存):

#include<stdlib.h>
void free(void *ptr); //釋放ptr指向的內存塊
void cfree(void *ptr); //和free一樣,但提供與SunOS的兼容性。推薦使用free

如果你想調整動態申請的內存的大小,使用realloc函數,原型如下:

#include<stdlib.h>
void *realloc(void *ptr, size_t newsize)
/*如果舊的內存塊後面沒有足夠的空間申請額外的內存,realloc會重新找一塊
*地方申請newsize大小的內存,並將舊內存復制到新內存中。
*返回值:無法申請空間時返回空指針,原內存空間無變化。
*/

申請一塊干淨的內存空間,我們之前使用malloc,在使用memset清空內存空間。現在我們使用calloc一步到位。

#include <stdlib.h>
void *calloc(size_t count, size_t eltsize)
/*等同於
void * calloc(size_t count, size_t eltsize)
{
size_t size = count * eltsize;
void *value = malloc(size);
if (value != 0) memset(value, 0, size);
return value;
}
*/

malloc的效率問題:

  GNU C庫中,malloc實際申請的內存大小不會以2的n次方呈現,他會和周圍的內存碎片合並,從而避免內存的浪費。

  如果需要申請大的內存塊(大於一個頁),可以使用mmap。mmap函數申請的內存實際上是內存IO映射。我們知道程序面對的是虛擬內存,虛擬內存的背後要有實際物理內存。實際物理內存可以借用輔存。內存是按頁劃分的,申請大於一個頁的內存會有什麼事情發生呢?這一頁的實際物理內存會被系統鎖定。那麼很大的物理內存無法被其他程序利用。而我們使用mmap函數,是將虛擬內存和輔存上的文件對應,最重要的是:只有程序對對這塊虛擬內存操作的時候,才會申請相應大小實際物理內存。還有個問題是碎片化,如果你申請的內存恰好在兩個被使用的內存之間,而你申請的內存有很大,那剩下的空間就很小(可能誰也用不了)(個人理解)

關於內存對齊

  malloc 和realloc返回的塊地址永遠是8的倍數或者64位系統下16的倍數(這和之前所說的大小並不是一回事),如果你想返回更高的倍數可以使用aligned_alloc或者posix_memalign(不過多大的倍數,都必須是2的n次方)

#include<stdlib.h>
void *aligned_alloc(size_t alignment, size_t size)
/*說明*/
//申請size大小的內存塊,地址為alignment的倍數(size必須為alignment的倍數)
//如果出錯會設置errno,詳情請man aligned_alloc
//ISO C11之後的標准支持該函數,在非POSIX系統中兼容性比posix_memalign好

#include <stdlib.h>
void *memalign(size_t boundary, size_t size)
/*說明*/
//memalign同樣申請size大小的內存,返回的地址為boundary的倍數。不同的是memalign實際申請的內存大小會稍微大一些(避免內存碎片化),返回的地址實際為內存塊裡的一個特定邊界

#include<stdlib.h>
int posix_memalign(void **memptr, size_t alignment, size_t size)
/*說明*/
//posix_memalign通過memptr返回地址,所以memptr使用了指針的指針。(應該不難理解)
//與memalign不同的地方:alignment的值必須是sizeof(void *)的正數倍,且為2的n次方。(當然所有的內存對齊申請函數的alignment都必須是2的n次方)

#include<stdlib.h>
void *valloc(size_t size)

/*說明*/
//等同於如下的函數
void * valloc (size_t size)
{
return memalign(getpagesize(), size);
}

//可見,valloc申請的內存邊界是以頁為大小的。

想要設置內存申請的參數,可以使用mallopt函數

#include <malloc.h>
int mallopt(int param, int value);

傳進的參數是一對的,param=value。詳細介紹以後再寫,或者查看man手冊。

動態申請的內存塊是堆內存,堆內存不一定是連續的。如果我們想檢查其連續性可以使用mcheck函數

#include<mcheck.h>
int mcheck(void (*abortfn) (enum mcheck_status status))
/*簡要說明*/
//參數:函數指針,該函數指針指向的函數需要接收mcheck_status的枚舉類型。
//mcheck 在檢查堆連續性時,如果發現不連續,會調用abortfn指向的函數。如果你提供的時空指針,則會使用默認函數(打印相關信息後停止程序)
//為了能讓mcheck函數起作用,你需要在編譯選項中添加-lmcheck選項。
//另外你也可以在gdb調試時調用mcheck函數。
//狀態值:描述了不連續的類型(具體的查看手冊)

Linux升級Glibc http://www.linuxidc.com/Linux/2015-04/116472.htm

危險!GHOST(幽靈)漏洞曝光 http://www.linuxidc.com/Linux/2015-01/112496.htm

GNU glibc 爆 gethostbyname 緩沖區溢出漏洞 http://www.linuxidc.com/Linux/2015-01/112486.htm

glibc gethostbyname緩沖區溢出漏洞(CVE-2015-0235) http://www.linuxidc.com/Linux/2015-01/112516.htm

Linux glibc幽靈漏洞測試與修復方法 http://www.linuxidc.com/Linux/2015-01/112562tm

Glibc 的詳細介紹:請點這裡
Glibc 的下載地址:請點這裡

Copyright © Linux教程網 All Rights Reserved