1、底層結構程序員
採用三層結構,實際使用中能夠方便映射到兩層或者三層結構,以適用不一樣的硬件結構。最下層的申請內存函數get_free_page。之上有三種類型的內存分配函數:
函數
1.kmalloc類型。內核進程使用,基於切片(slab)技術,用於管理小於內存頁的內存申請。思想出發點和應用層的內存緩衝池同出一轍。但它針對內核結構,特別處理應用場景固定,不考慮釋放。spa
2.vmalloc類型。內核進程使用。用於申請不連續內存。
code
3.brk/mmap類型。用戶進程使用。malloc/free實現的基礎。
對象
2、內存管理的相關函數圖進程
STL -> 內存自動分配和自動回收(C++)
內存
|
開發
C++ -> new分配內存,delete回收內存
字符串
|
get
C -> malloc分配內存,free回收內存
|
Unix 系統函數 -> sbrk/brk 分配和回收內存
|
Unix底層系統函數 -> mmap/munmap分配回收
(用戶層)
----------------------------------------------------------------------------
(內核層)
Unix內核函數 kmalloc/vmalloc/get_free_page
3、進程與內存
a.全部進程(執行的程序)都必須佔用必定數量的內存
b.對任何一個普通進程來說,它都會涉及到5種不一樣的數據段,其內存空間劃分爲:
1.代碼區 —— 存放代碼/函數,也就是說它是可執行程序的內存中的鏡像。(只讀)
2.全局區 —— 保存全局變量,static局部變量。
3.BSS段 —— 未初始化的全局變量,BSS段在main函數執行以前會自動清零
4.棧區 —— 局部變量,包括函數的形參,棧區內存自動分配和自動回收。
5.堆區 —— 程序員本身管理的區域,malloc/free操做的都是堆區。
6.只讀常量區 —— 存放字符串常量和const修飾的全局變量
注:只讀常量區和代碼區很是接近,有些書把只讀常量區和代碼區合併爲代碼區。
c.進程如何組織這些區域?
從小到大次序:代碼區、只讀常量區、全局區、BSS段、堆區、棧區
堆區在離前面四個區不遠的地址空間開始,從小到大分配,棧區從3G開始,從大到小分配。主要爲了不堆區和棧區重疊。
d.查看內存分配
Linux把一切都看作成文件,內存也能夠在文件中查看。每一個進程都在/proc目錄下有一個對應的子目錄,以進程ID做爲子目錄名。進程ID是系統對進程的標識。能夠用ps-aux命令查看進程。
cat /proc/進程ID/maps 能夠查看當前進程的內存狀況。
4、虛擬內存管理技術
Linux使用了虛擬內存地址。每一個Linux中的進程都有0~4G的虛擬內存地址,就是0~4G的數字。虛擬內存地址在開始時只是一個數字,不對應任何的內存。虛擬內存地址必須先映射一段物理內存或硬盤上的文件才能被使用。所謂的分配內存其實就是讓虛擬內存地址映射一段物理內存。若是使用沒有映射的虛擬內存地址就會引起段錯誤。
程序員所操做的內存地址都是虛擬內存地址,看不到物理內存地址。
0~4G的虛擬內存地址中,0~3G是用戶使用,叫作「用戶空間」,3G~4G是內核使用的,叫作「內核空間」。用戶空間不能直接使用內核空間,但能夠經過內核空間提供的一些函數(系統調用)訪問內核空間。
注:內存管理的基本單位是4096 byte (4K),叫內存頁。內存的映射和回收都是之內存頁做爲基本單位。
5、進程內存管理
進程內存管理的對象是進程線性地址空間上的內存鏡像,這些內存鏡像其實就是進程使用的虛擬內存區域(memory region)。進程虛擬空間是個32或64位的「平坦」(獨立的連續區間)地址空間(空間的具體大小取決於體系結構)。要統一管理這麼大的平坦空間可絕非易事,爲了方便管理,虛擬空間被劃分爲許多大小可變的(但必須是4096byte的整數倍數)內存區域,這些區域在進程線性地址中像停車位同樣有序排列。這些區域的劃分原則是「將訪問屬性一致的地址空間存放在一塊兒」,所謂訪問屬性一致無非是指「可讀、可寫、可執行等」。
6、物理內存管理(頁管理)
Linux內核管理物理內存是經過分頁機制實現的,它將整個內存劃分紅無數4K(在i386體系結構中)大小頁,從而分配和回收內存的基本單位即是內存頁了。利用分頁管利用助於靈活分配內存地址,由於分配時沒必要要求必須有大塊的連續內存,系統能夠東一頁、西一頁的湊出所須要的內存供進程使用。雖然如此,可是實際上系統使用內存仍是傾向於分配連續的內存塊,由於分配連續內存時,頁表不須要修改,所以能下降刷新率(頻繁刷新會很大增長訪問速度)。
7、brk/sbrk的虛擬內存管理
void *sbrk(int size);
size = 0 返回sbrk/brk上次的末尾地址,表明取當前的位置,
size > 0 分配內存空間,返回上次的末尾地址,表明分配size字節的內存,
size < 0 釋放空間,表明回收size字節內存。
int brk(void* ptr);
直接修改訪問的有效範圍的末尾地址,釋放空間造成一個完整的page,則該頁映射被解除
返回:0 分配成功
-1 分配失敗
經驗:sbrk在分配內存上簡單,brk在釋放內存上簡單。所以,開發大多數使用sbrk分配內存,使用brk釋放內存。
8、系統底層的內存映射(mmap/munmap)
#include <sys/mman.h> void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *start, size_t length);
參數公共部分:
start:指向欲映射的內存起始地址,一般設爲 NULL,表明讓系統自動選定地址,映射成功後返回該地址。
length:表明將文件中多大的部分映射到內存。 映射空間大小。建議4k倍數,不是4K倍數,自動對齊。
mmap獨有部分:
prot:映射區域的保護方式。能夠爲如下幾種方式的組合:
1.PROT_EXEC 映射區域可被執行
2.PROT_READ 映射區域可被讀取
3.PROT_WRITE 映射區域可被寫入
4.PROT_NONE 映射區域不能存取
flags:影響映射區域的各類特性。在調用mmap()時必需要指定MAP_SHARED 或MAP_PRIVATE。
1.MAP_FIXED 若是參數start所指的地址沒法成功創建映射時,則放棄映射,不對地址作修正。一般不鼓勵用此標誌。
2.MAP_SHARED對映射區域的寫入數據會複製迴文件內,並且容許其餘映射該文件的進程共享。
3.MAP_PRIVATE 對映射區域的寫入操做會產生一個映射文件的複製,即私人的「寫入時複製」(copy on write)對此區域做的任何修改都不會寫回原來的文件內容。
4.MAP_ANONYMOUS創建匿名映射。此時會忽略參數fd,不涉及文件,並且映射區域沒法和其餘進程共享。
5.MAP_DENYWRITE只容許對映射區域的寫入操做,其餘對文件直接寫入的操做將會被拒絕。
6.MAP_LOCKED 將映射區域鎖定住,這表示該區域不會被置換(swap)。
fd:要映射到內存中的文件描述符。若是使用匿名內存映射時,即flags中設置了MAP_ANONYMOUS,fd設爲-1。有些系統不支持匿名內存映射,則可使用fopen打開/dev/zero文件,而後對該文件進行映射,能夠一樣達到匿名內存映射的效果。
offset:文件映射的偏移量,一般設置爲0,表明從文件最前方開始對應,offset必須是分頁大小的整數倍。
返回值:
若映射成功則返回映射區的內存起始地址,不然返回MAP_FAILED(-1),錯誤緣由存於errno 中。
9、errno錯誤代碼
1.EBADF 參數fd 不是有效的文件描述詞
2.EACCES 存取權限有誤。若是是MAP_PRIVATE 狀況下文件必須可讀,使用MAP_SHARED則要有PROT_WRITE以及該文件要能寫入。
3.EINVAL 參數start、length 或offset有一個不合法。
4.EAGAIN 文件被鎖住,或是有太多內存被鎖住。
5.ENOMEM 內存不足。