一、以程序開發者的角度審視Linux的進程內存管理編程
二、系統物理內存管理和內核內存的使用方法函數
三、內存映射、理解內核內存管理與用戶內存管理之間的關係spa
毫無疑問,全部進程(執行的程序)都必須佔用必定數量的內存,它或是用來存放從磁盤載入的程序代碼,或是存放取自用戶輸入的數據等等。不過進程對這些內存的管理方式因內存用途不一而不盡相同,有些內存是事先靜態分配和統一回收的,而有些倒是按須要動態分配和回收的。操作系統
對任何一個普通進程來說,它都會涉及到5種不一樣的數據段。稍有編程知識的朋友都能想到這幾個數據段中包含有「程序代碼段」、「程序數據段」、「程序堆棧段」等。不錯,這幾種數據段都在其中,但除了以上幾種數據段以外,進程還另外包含兩種數據段。下面咱們來簡單概括一下進程對應的內存空間中所包含的5種不一樣的數據區。.net
代碼段:代碼段是用來存放可執行文件的操做指令,也就是說是它是可執行程序在內存中的鏡像。代碼段須要防止在運行時被非法修改,因此只准許讀取操做,而不容許寫入(修改)操做——它是不可寫的。htm
數據段:數據段用來存放可執行文件中已初始化全局變量,換句話說就是存放程序靜態分配[1]的變量和全局變量。進程
BSS段:BSS段包含了程序中未初始化的全局變量,在內存中 bss段所有置零。內存
堆(heap):堆是用於存放進程運行中被動態分配的內存段,它的大小並不固定,可動態擴張或縮減。當進程調用malloc等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張);當利用free等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)開發
棧:棧是用戶存放程序臨時建立的局部變量,也就是說咱們函數括弧「{}」中定義的變量(但不包括static聲明的變量,static意味着在數據段中存放變量)。除此之外,在函數被調用時,其參數也會被壓入發起調用的進程棧中,而且待到調用結束後,函數的返回值也會被存放回棧中。因爲棧的先進先出特色,因此棧特別方便用來保存/恢復調用現場。從這個意義上講,咱們能夠把堆棧當作一個寄存、交換臨時數據的內存區。get
上述幾種內存區域中數據段、BSS和堆一般是被連續存儲的——內存位置上是連續的,而代碼段和棧每每會被獨立存放。有趣的是,堆和棧兩個區域關係很「曖昧」,他們一個向下「長」(i386體系結構中棧向下、堆向上),一個向上「長」,相對而生。但你沒必要擔憂他們會碰頭,由於他們之間間隔很大(到底大到多少,你能夠從下面的例子程序計算一下),絕少有機會能碰到一塊兒。
進入操做系統內核看看,進程對內存具體是如何進行分配和管理的。
從用戶向內核看,所使用的內存表象形式會依次經歷「邏輯地址」——「線性地址」——「物理地址」幾種形式。邏輯地址經段機制轉化成線性地址;線性地址又通過頁機制轉化爲物理地址。(可是咱們要知道Linux系統雖然保留了段機制,可是將全部程序的段地址都定死爲0-4G,因此雖然邏輯地址和線性地址是兩種不一樣的地址空間,但在Linux中邏輯地址就等於線性地址,它們的值是同樣的)。沿着這條線索,咱們所研究的主要問題也就集中在下面幾個問題。
1. 進程空間地址如何管理?
2. 進程地址如何映射到物理內存?
3. 物理內存如何被管理?
Linux操做系統採用虛擬內存管理技術,使得每一個進程都有各自互不干涉的進程地址空間。該空間是塊大小爲4G的線性虛擬空間,用戶所看到和接觸到的都是該虛擬地址,沒法看到實際的物理內存地址。利用這種虛擬地址不但能起到保護操做系統的效果(用戶不能直接訪問物理內存),並且更重要的是,用戶程序可以使用比實際物理內存更大的地址空間。
在討論進程空間細節前,這裏先要澄清下面幾個問題:
1、4G的進程地址空間被人爲的分爲兩個部分——用戶空間與內核空間。用戶空間從0到3G(0xC0000000),內核空間佔據3G到4G。用戶進程一般狀況下只能訪問用戶空間的虛擬地址,不能訪問內核空間虛擬地址。只有用戶進程進行系統調用(表明用戶進程在內核態執行)等時刻能夠訪問到內核空間。
2、用戶空間對應進程,因此每當進程切換,用戶空間就會跟着變化;而內核空間是由內核負責映射,它並不會跟着進程改變,是固定的。內核空間地址有本身對應的頁表(init_mm.pgd),用戶進程各自有不一樣的頁表。
3、每一個進程的用戶空間都是徹底獨立、互不相干的。不信的話,你能夠把上面的程序同時運行10次(固然爲了同時運行,讓它們在返回前一同睡眠100秒吧),你會看到10個進程佔用的線性地址如出一轍。