故宮角樓是很多攝影愛好者常去的地方,夕陽余輝下的故宮角樓平靜而安詳。
?
首先,了解一下進程的基本概念,進程在內存中布局和內容。
此外,還需要知道運行時是如何為動態數據結構(如鏈表和二叉樹)分配額外內存的。
進程:是一個可執行程序的實例。
程序:包含一系列信息的文件,這些信息描述了如何在運行時創建一個進程。包含如下信息:
進程的再定義:進程是由內核定義的抽象的實體,並為該實體分配用以執行程序的各項系統資源。
從內核的角度看,進程由用戶內存空間和一系列內核數據結構組成,其中用戶內存空間包含了程序代碼及代碼所使用的變量,而內核數據結構則用於維護進程狀態信息。
每個進程所分配的內存由很多部分組成,通常稱之為“段(segment)”。如上圖所示:
需要注意一點時,該內存布局的討論是在虛擬內存中的,並不是物理內存中的布局。
在後面會專門討論虛擬內存的一些細節。
?
堆:一段長度可變的連續虛擬內存,始於進程的未初始化數據段末尾,隨著內存的分配和釋放而增減。將堆的當前內存頂部邊界稱為“程序中斷(program break)”
program break是一個非常重要的概念,因為分配和釋放內存的實際動作就是改變進程的program break位置。
program break的起始位置(堆的大小為0)位於未初始化數據段末尾之後。
細節:在分配新的內存後,program break位置升高,程序可以訪問新分配區域內的任何內存地址,而此時物理內存頁尚未分配。內存會在進程首次試圖訪問這些虛擬內存地址時自動分配新的物理內存頁。
malloc函數聲明
#include void *malloc(size_t size);
作用:在堆上分配參數size字節大小的內存。
返回值:成功返回指向新分配內存起始地址的指針,失敗返回NULL
free函數聲明?
#include void free(void *ptr);
?作用:釋放ptr參數所指向的內存塊,該參數應該是之前由malloc或者其他內存分配函數之一所返回的地址。
需要注意的是:一般情況下,free並不降低program break的位置,而是將這塊內存增加到空閒內存列表中,供後續的malloc函數循環使用。因為:
僅當堆頂空閒內存“足夠”大的時候,free函數的glibc實現會調用sbrk()來降低program break的地址,至於“足夠”與否則取決於malloc函數包行為的控制參數(128KB為典型值)。這減少了必須對sbrk()發起的調用次數。
malloc()的實現
free()的實現
首先先了解兩點:malloc返回的內存塊和空閒列表中的內存塊的結構
為了知道每一個內存塊的大小,當malloc分配內存塊時,會額外分配幾個字節來存放記錄這塊內存大小的整數值。該整數位於內存塊的起始處,而實際返回給調用者的內存地址恰好位於這一長度記錄字節之後。如下圖所示:
為了管理空閒內存列表,free()會使用內存塊本身的空間來存放鏈表指針,將自身添加到列表中。如下圖所示:
所以,在頻繁地分配和釋放內存之後,堆中的鏈表可能會變成下圖的樣子,空閒鏈表中的空閒內存會和已分配的在用內存混雜在一起。
?
通過對內存相關知識更多的了解,在平時編程的時候,應更清楚為什麼我們需要遵守下面的規則。
雖然在我們平時的工作當中,可能涉及不到這麼底層的原理,但是通過對這些基本原理的了解,可以讓我們更加清除,我們寫代碼究竟在寫些什麼 :)
?
參考資料:
《Linux/Unix系統編程手冊(上冊)》 第6章,第7章?