簡述:Unix/Linux內存管理

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 內存不足。

相關文章
相關標籤/搜索