進程的內(nèi)存布局
內(nèi)存地址由高到低依次是:
- kernel space
- stack:向下增長
- dynamic libraries:共享庫載入的空間
- heap:向上增長
- read/write sections(.data .bss):屬于可執(zhí)行文件映像
- readonly sections(.init .rodata .text):屬于可執(zhí)行文件映像
- reserved:內(nèi)存中受到保護而禁止訪問的內(nèi)存區(qū)域
動態(tài)庫和棧之間扬虚,動態(tài)庫和堆之間仍有可使用的空間肝匆,因此棧和堆才能增長。此外,其他區(qū)域之間也并不是一定是連續(xù)的拉盾。
段錯誤(segment default):非法指針解引用所引起的錯誤鲫凶。例如座哩,指針指向不允許讀寫的地址聂使,或者指針指向的地址并沒有映射到實際的物理內(nèi)存。
多線程下的內(nèi)存布局
上面所說的內(nèi)存布局是《程序員的自我修養(yǎng)》上講的汰规,在《Linux環(huán)境編程》上提到了多線程進程的內(nèi)存布局:
mmap區(qū)域指的是執(zhí)行系統(tǒng)調(diào)用mmap()所分配出來的區(qū)域汤功,這塊區(qū)域又稱為文件映射區(qū)。mmap()的作用是向操作系統(tǒng)申請一塊虛擬地址空間溜哮√辖穑可以指定某個文件來填充這塊空間(文件映射),也可以不指定(分配匿名空間)茂嗓。
mmap()調(diào)用后餐茵,僅僅是消耗了虛擬地址空間,創(chuàng)建/更改了內(nèi)存映射所需的數(shù)據(jù)結(jié)構(gòu)(頁表)述吸,并沒有映射到物理頁忿族。當進程后續(xù)訪問這些虛擬地址的時候,會發(fā)現(xiàn)這些虛擬內(nèi)存頁沒有對應(yīng)的物理頁,然后發(fā)生頁錯誤(Page Default)道批。在處理頁錯誤的過程中才分配物理頁(如果調(diào)用mmap時指定了文件错英,就把磁盤中的文件拷貝到物理頁中),并建立映射屹徘。此時分配的虛擬內(nèi)存才變得真正可用走趋。
頁錯誤也是缺頁中斷衅金。除了第一次訪問mmap分配的頁(因為沒有映射到物理頁)會引發(fā)頁錯誤外噪伊,后續(xù)訪問時,如果物理頁已經(jīng)換出到外存氮唯,也會引發(fā)頁錯誤鉴吹。
glibc中malloc的實現(xiàn)
在標準C庫中,提供了malloc函數(shù)來分配一塊虛擬內(nèi)存空間惩琉。
glibc下malloc獲取的空間的來源有兩個:brk系統(tǒng)調(diào)用豆励,mmap系統(tǒng)調(diào)用。
(有人把malloc管理的空間統(tǒng)稱為堆瞒渠,也有人只把brk系統(tǒng)調(diào)用分配的空間稱之為堆良蒸,下面說的都取前者的含義)
- brk的作用是設(shè)置進程數(shù)據(jù)段的結(jié)束地址。如果把數(shù)據(jù)段的結(jié)束地址向高地址移動伍玖,那么擴大的那部分空間就可以拿來作為堆空間使用嫩痰。
- mmap是在進程的虛擬地址空間中(brk分配的空間和棧之間的一塊內(nèi)存,稱為文件映射區(qū))分配一塊空閑的虛擬內(nèi)存窍箍。
對于小于128KB的請求串纺,malloc會在現(xiàn)有的堆空間里面,按照堆分配算法分配一塊空間并返回椰棘;對于大于128KB的請求纺棺,malloc會使用mmap系統(tǒng)調(diào)用分配一塊匿名空間,然后在這塊匿名空間中為用戶分配空間邪狞。
這兩種方式分配的都是虛擬內(nèi)存祷蝌,沒有分配物理內(nèi)存。在第一次訪問已分配的虛擬地址空間的時候帆卓,發(fā)生缺頁中斷巨朦,操作系統(tǒng)負責分配物理內(nèi)存,然后建立虛擬內(nèi)存和物理內(nèi)存之間的映射關(guān)系鳞疲。