Linux記憶體管理

2021-05-04 15:35:11 字數 3058 閱讀 5967

本文首先介紹一下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...