Linux記憶體管理

2021-04-30 01:48:24 字數 4134 閱讀 5043

本文首先介紹一下linux記憶體管理方式,著重說明一下使用者空間的記憶體管理,包括linux虛擬對映以及glibc中malloc的實現;然後簡要介紹單程序多執行緒的記憶體管理方式,主要涉及各執行緒堆疊空間的分配;

linux

採用兩級保護機制,隔離核心空間和使用者程式空間,使使用者程式無法直接訪問核心,而只能通過系統呼叫的方式。 對於

32 位

cpu 來說,

linux

虛擬記憶體空間大小為

4g ,其中核心佔據

3g~4g

空間(依據

cpu 體系的不同而有差別),應用程式佔據

0~3g

空間。其中使用者空間又分為**段、資料段、

bss 、堆及棧空間,各段分布如圖所示:

上圖不連續部分,表示可能存在隔離空間。實際上在我虛擬機器上的

linux

,核心為

2.6.9

128m

空間裡的,可網上眾多資料都說其對映到堆疊之間,有待以後研究。下面就來分別介紹各部分記憶體空間。 對於

x86 體系來說,核心佔據高階

1g 虛擬記憶體,其中又分為兩部分

linux

核心區域及

vmalloc

區域。 1.

核心區域

linux

核心可以認為是實位址模式,實體地址經過簡單的偏移即構成邏輯位址,即核心程式設計時,我們可以認為操作的是物理記憶體,並且這段記憶體是物理連續的。其中用到兩種記憶體分配方式,整頁分配(大記憶體)、

slab

分配(小記憶體)。

linux

核心提供了比較完善的記憶體管理方式,頁分配

_get_free_page

,基於slab

的小記憶體分配

kmalloc

。這兩個介面的實現都首先呼叫

__alloc_pages()

,分配物理頁面,然後

kmalloc

在分配的物理頁面上實現

slab

管理。分配物理頁面基於

buddy

演算法,在使用

buddy

實現的物理記憶體管理中最小分配粒度是以頁為單位的,大小通常為

4k 。

__alloc_pages()

函式中,多次嘗試呼叫

get_page_from_freelist()

函式從zonelist

中取得相關

zone

,並從其中返回乙個可用的物理頁面。

實際上,核心空間的起始位址是物理記憶體的低位址,核心有自己的頁表,經過簡單的偏移轉換成邏輯位址,另外每個程序也都有自己的頁表。 2.

vmalloc

這個區域一般佔據最高位址

128m

空間,也屬於核心區域,不過它對應不連續的物理空間,這裡的情況很類似於使用者空間分配虛擬記憶體,記憶體邏輯上連續,其實對映到並不一定連續的物理記憶體上。

linux

核心借用了這個技術,允許核心程式在核心位址空間中分配虛擬位址,同樣也利用頁表(核心頁表)將虛擬位址對映到分散的記憶體頁上。核心提供

vmalloc

函式分配核心虛擬記憶體,該函式不同於

kmalloc

,它可以分配較

kmalloc

大得多的記憶體空間(可遠大於

128k

,但必須是頁大小的倍數),但相比

kmalloc

來說,vmalloc

需要對核心虛擬位址進行重對映,必須更新核心頁表,因此分配效率上要低一些(用空間換時間)。

當應用程式以

elf 格式被載入時,

linux

載入器首先將其載入到

0x0804 8000

處,對x86 和

ppc 來說,

0x0804 8000~0xc000 0000

這段位址空間才是程式可用的,任何訪問到這個範圍以外的指令,都會引起段錯誤。至於為什麼

linux

放著前128m

記憶體空間不用,

google

一下也沒有搜尋到答案,有待以後研究。

上面已經說過,每個程序都有自己的頁表,記錄著邏輯位址與物理頁面的對應關係,當程序切換時,新程序頁表首位址就被載入到

cr3 暫存器中,記憶體的訪問首先都是從

cr3 開始,找到

tlb (頁表緩衝區),通過邏輯位址找出對應的物理頁面。另外程序

pcb 中還有許多

vm_struct

指標,每個

vm_struct

記錄著一段邏輯位址空間以及對應的操作函式,比如**空間、棧空間、

mmap

對映空間等,不同的空間,其讀寫函式以及許可權也是不一樣的。這些結構體對應的邏輯空間都會有間隔,防止越界訪問。

當發生缺頁異常時,即訪問的邏輯位址對應的物理頁面還未對映,或是已經被交換出去時,異常處理程式首先找到這個邏輯位址對應的

vm_struct

。如果找不到對應的

vm_struct

,說明發生了非法訪問,否則找到相應的

vm_struct

一般未發生缺頁異常或重新對映到物理記憶體後,記憶體訪問經過頁表轉換時,頁表條目中相應的位代表訪問該頁的一系列許可權,這時就會進行許可權檢查,做到記憶體保護。

關於棧空間與

mmap

對映動態鏈結庫的空間,都是由核心管理的,在必要的時候也可以挪動

mmap

對映的位置,為棧的增長提供空間。這裡我們主要討論一下堆空間,即

malloc

記憶體,它是呼叫

brk 實現的。這個呼叫只是擴張堆空間邊界,並不對應物理記憶體,只是在使用時,發生缺頁異常後,才由核心對映物理記憶體。但是,並不是每次

malloc

都會引起

brk 呼叫,

malloc

自身維護乙個空閒鏈,用於收集堆空間上已經釋放的空間,當然這個是邏輯位址空間。每次

malloc

呼叫,首先試圖從該空閒鏈中獲取該空間,當鍊上的元素都不能滿足時,才會呼叫

brk ,擴張堆空間,以獲得足夠大的邏輯空間。每次

free

時,都會將釋放的空間放入空閒鏈中,並檢查該空間相鄰的邏輯空間,如果也空閒,則合併為一塊較大的空間。實際上

malloc

管理操作會比以上所說的更複雜,具體細節只有研究

glibc

的**,網上並沒有很好的資料。

所以,malloc

這種管理方式類似於

vxworks

的 記憶體管理方式,如果頻繁的申請、釋放小記憶體,同樣會在邏輯空間產生記憶體碎片。雖然對於不同的記憶體部分,其邏輯空間並不會連續(像棧空間和堆空間),但是堆 空間內部,通過malloc申請的記憶體仍然有可能發生越界,破壞本程序相鄰的變數記憶體空間,不過這種越界比較容易定位,因為只是限於本程序內部。至於野指 針,其影響也只限於本程序,對系統和其它程序不會構成威脅。

執行緒最主要的目的就是更好地支援

smp 以及減小程序上下文切換開銷,針對這兩大意義,分別開發了核心執行緒和使用者級執行緒。

linux

核心僅支援輕量級程序,所以

linux

下的執行緒庫不可能實現完全意義上的

posix

執行緒機制,不能實現使用者級排程,以減小切換開銷。

linux

執行緒庫棧空間的分配,依據個

cpu 架構的不同而有所區別,這裡只能分析

i386

平台所使用的兩種棧組織方式:

floating_stack

方式和使用者自定義方式。 在

floating_stack

方式下,

linuxthreads

利用mmap()

分配8mb

空間(i386

系統預設的最大棧空間大小,如果有執行限制(

rlimit

),則按照執行限制設定),使用

mprotect()

設定其中第一頁為非訪問區。該

8m 空間的功能分配如下圖:

低位址被保護的頁面用來監測棧溢位。對於使用者指定的棧,在按照指針對界後,設定執行緒棧頂,並計算出棧底,不做保護,正確性由使用者自己保證,我想應該也可以呼叫

mprotect

設定隔離區域,監測堆疊溢位。不論哪種組織方式,執行緒描述結構總是位於棧頂緊鄰堆疊的位置。

至於堆空間,和單執行緒分配的機制是一樣的。

Linux記憶體管理

本文首先介紹一下linux記憶體管理方式,著重說明一下使用者空間的記憶體管理,包括linux虛擬對映以及glibc中malloc的實現 然後簡要介紹單程序多執行緒的記憶體管理方式,主要涉及各執行緒堆疊空間的分配 linux採用兩級保護機制,隔離核心空間和使用者程式空間,使使用者程式無法直接訪問核心,...

Linux記憶體管理

首先我要說,我這是轉貼,轉的cu論壇上 nonameboy 的帖子,你可以連線過去看看。今天因為要解釋系統中可用記憶體的大小,用google看了半天,還有在cu上找了關天,竞然沒有發現有比較好的章,估計很多人都沒有注意到,懂了以後又沒有整理出來。在cu上看了很多文章說什麼memory leak和li...

linux記憶體管理

名詞解釋 2.頁描述符 描述每乙個頁框的狀態資訊,所有的頁描述符都是儲存在mem map陣列中,每個描述符32個位元組。3.節點 系統物理記憶體被劃分為多個節點,每個節點內cpu訪問頁面的時間是相同的,對應的資料結構 節點描述符。4.管理區 每個節點又分為多個管理區,對應的資料結構 管理區描述符 1...