本文首先介紹一下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
記憶體空間不用,
一下也沒有搜尋到答案,有待以後研究。
上面已經說過,每個程序都有自己的頁表,記錄著邏輯位址與物理頁面的對應關係,當程序切換時,新程序頁表首位址就被載入到
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...