摘要:
本文概述Linux內(nèi)核實(shí)現(xiàn)內(nèi)存管理的基本概念,在了解基本概念后,逐步展開介紹實(shí)現(xiàn)內(nèi)存管理的相關(guān)技術(shù),后面會(huì)分多篇進(jìn)行介紹。
0、引言
Linux內(nèi)核主要由進(jìn)程管理、內(nèi)存管理、文件系統(tǒng)和設(shè)備驅(qū)動(dòng)四個(gè)子系統(tǒng)組成。內(nèi)存管理是內(nèi)核最復(fù)雜同時(shí)也是最重要的一部分。
1、物理地址的分布
ARM體系結(jié)構(gòu)的物理地址映射是預(yù)定義的,并且規(guī)定好了哪個(gè)位置應(yīng)該使用哪條總線。芯片制造商在集成ARM core的時(shí)候,只需按照預(yù)定義的物理地址映射分配內(nèi)存、外設(shè)等即可。
ARM體系結(jié)構(gòu)的物理地址都是從0地址開始的線性組合,下面分別簡介ARM cortex-M3/M4、ARMv7和ARMv8系列芯片的物理地址映射。
1.1 ARM cortex-M3/M4的物理地址分布
ARM cortex-M3/M4的地址分布如下,由于該系列的core沒有MMU,所以其使用內(nèi)存都是一一對(duì)應(yīng)的,雖然不是我們介紹的重點(diǎn),可以了解下。其理論地址空間范圍是0~4GB。
手冊(cè)中詳細(xì)介紹了每memory region的用途。
1.2 Cortex-A(ARMv7)系列芯片的地址映射
ARMv7作為32位的微處理器,下圖展示的是使能MMU后物理地址和虛擬地址之間的概覽,其理論地址空間范圍是0~4GB。
1.3 Cortex-A(ARMv8)系列芯片的地址映射
ARMv8作為64位的微處理器,使能MMU后物理地址和虛擬地址之間的概覽如下,其理論地址空間范圍是0~4*(2^32)GB(0xFFFFFFFF_FFFFFFFF),這個(gè)地址空間范圍異常之大,是用不完的,所以中間有大量的Reserved區(qū)域。
2、Linux 虛擬地址空間分布
Linux內(nèi)核一般將處理的虛擬地址空間劃分成兩個(gè)部分:用戶態(tài)和內(nèi)核態(tài)。
在32位系統(tǒng)上,最大尋址空間是4GB,所以最大虛擬地址空間也是4GB,其中1GB分給內(nèi)核,3GB分為用戶。
在64位系統(tǒng)上,最大尋址空間是0xFFFFFFFFFFFFFFFF,這個(gè)地址非常大,對(duì)于當(dāng)前的技術(shù),用完幾乎是不可能的,針對(duì)不同的芯片架構(gòu)有不同的分法,比如x86 64位系統(tǒng)給內(nèi)核和用戶各分了128TB的地址空間,ARM 64位系統(tǒng)給內(nèi)核和用戶各分了256TB的地址空間,由于我們主要研究ARM體系結(jié)構(gòu),后續(xù)講到內(nèi)存和芯片架構(gòu)相關(guān)的統(tǒng)一默認(rèn)為ARM系統(tǒng)結(jié)構(gòu)。
2.1 內(nèi)核態(tài)地址空間
簡單地從ARM 32位系統(tǒng)分析。
2.2 用戶態(tài)地址空間
每個(gè)用戶進(jìn)程都有自己獨(dú)立的虛擬地址空間,仿佛該進(jìn)程獨(dú)占了整個(gè)內(nèi)存。
Linux C/C++編譯之后的可執(zhí)行文件常使用ELF(全稱:Executableand Linking Format)文件格式,可執(zhí)行文件是按照段(segment)組織的。我們就以進(jìn)程的可執(zhí)行文件為ELF下的內(nèi)存布局。
(2)data段:存放初始化后的全局變量、局部靜態(tài)變量
(3)bss段:存放未初始化的全局變量、局部靜態(tài)變量
(4)堆:存放動(dòng)態(tài)分配的內(nèi)存,比如melloc函數(shù)申請(qǐng)的內(nèi)存
(5)內(nèi)存映射區(qū)(MemoryMapping Segment):存放mmap函數(shù)映射內(nèi)容和動(dòng)態(tài)鏈接庫等內(nèi)容
(6)棧:存放函數(shù)運(yùn)行時(shí)的參數(shù)、局部變量等
(7)內(nèi)核空間:每個(gè)用戶進(jìn)程都共享同一份內(nèi)核空間,用戶進(jìn)程不能直接訪問內(nèi)核空間,必須使用系統(tǒng)調(diào)用來保證安全性。
3、虛擬地址和物理地址轉(zhuǎn)換MMU
在進(jìn)程執(zhí)行中,CPU不能直接操作物理地址,必須要經(jīng)過MMU轉(zhuǎn)換后才能訪問真正的物理地址。
Memory Management Unit(MMU)最主要的功能是管理任務(wù)作為獨(dú)立的程序,每個(gè)程序具有私有的虛擬內(nèi)存空間。虛擬內(nèi)存系統(tǒng)的關(guān)鍵特性是地址重映射和實(shí)現(xiàn)虛擬地址與物理地址之間的轉(zhuǎn)換。
3.1 ARM core、MMU與內(nèi)存之間的關(guān)系
在MMU使能前,地址轉(zhuǎn)換表必須寫入內(nèi)存,將地址轉(zhuǎn)換表指針寫入TTBR寄存器,然后通過ARM 的CP15協(xié)處理器使能打開MMU,具體操作代碼如下:
使能MMU后的操作系統(tǒng),ARM core不能直接訪問內(nèi)存,必須經(jīng)過MMU進(jìn)行地址轉(zhuǎn)換。
上圖可知,MMU主要包含兩個(gè)部分:TLBs和Table Walk Unit。
TLB(Translation Lookaside Buffer)用于緩存最近執(zhí)行的轉(zhuǎn)換頁表。當(dāng)ARM core訪問每一個(gè)內(nèi)存時(shí),MMU首先檢查是否緩存在TLB,如果查詢的到,則地址轉(zhuǎn)換非常迅速。如果沒有在TLB里查詢到,產(chǎn)生了一個(gè)TLB miss,同時(shí)需求Table Walk Unit介入,去系統(tǒng)的Cache或內(nèi)存中取新的頁表,并將取回的頁表緩存進(jìn)TLB待被使用。
3.2 MMU的地址映射
ARM Cortex-A(armV7)支持兩級(jí)映射,分為:
一級(jí)映射:16MB or 1MB sections(分段)
二級(jí)映射:64KB or 4KB page sizes(分頁)
3.3 一級(jí)映射為1MB sections
一級(jí)映射表將4GB的地址空間(32bit-core)分成4096個(gè)相同大小的secions,每一個(gè)section描述1MB的虛擬內(nèi)存空間。
(1)一級(jí)頁表基地址為Translation Table Base Address,寫入TTBR寄存器中。
(2)由于一級(jí)映射為1MB,所以一共有4096個(gè)頁表項(xiàng)。虛擬地址從Translation Table Base Address,找到對(duì)應(yīng)的頁表項(xiàng)。
(3)一級(jí)頁表Section Base Address Descriptor寄存器的[31 : 20]bits + Virtual Address的[19 : 0]bits,便可得到物理地址。
3.4 二級(jí)映射為4KB page size
二級(jí)映射表具有256個(gè)4字節(jié)的entries,需要占用1KB,同時(shí)必須是1KB對(duì)齊。
(1)一級(jí)頁表基地址為Translation Table Base Address,寫入TTBR寄存器中。
(2)由于一級(jí)映射為1MB,所以一共有4096個(gè)頁表項(xiàng)。虛擬地址從Translation Table Base Address,找到對(duì)應(yīng)的頁表項(xiàng)。一級(jí)頁表Section Base Address Descriptor寄存器的[31 : 20]bits 。
(3)一級(jí)頁表Section Base Address Descriptor寄存器的[1 : 0]bits表示是否開啟二級(jí)頁表,此處為01b,表示打開了二級(jí)頁表。
(4)虛擬地址的[19 : 12]bits尋找二級(jí)頁表的頁表項(xiàng)。
(5)最后根據(jù)二級(jí)頁表項(xiàng)的[31 : 12]bits + Virtual Address的[11 : 0]bits,便可得到物理地址。
4、進(jìn)程內(nèi)存空間管理
進(jìn)程該如何管理內(nèi)存呢?我們分別從用戶進(jìn)程和內(nèi)核進(jìn)程兩個(gè)角度去分析。
4.1 用戶空間內(nèi)存管理
用戶空間使用malloc()申請(qǐng)內(nèi)存,使用free()釋放內(nèi)存,malloc()和free()函數(shù)是Glibc庫內(nèi)的內(nèi)存分配器ptmalloc提供的接口函數(shù)。
ptmalloc通過Linux的系統(tǒng)調(diào)用brk或mmap向內(nèi)核申請(qǐng)內(nèi)存,為了保持高效的分配,內(nèi)核中申請(qǐng)內(nèi)存是以頁為單位,而不是以字節(jié)為單位,然后返回給給用戶空間的分配器,分配器通過某種算法管理這塊內(nèi)存,來滿足用戶空間的內(nèi)存分配要求;當(dāng)用戶free()釋放內(nèi)存時(shí),并不是立即返回給操作系統(tǒng),而是分配器管理這塊要被釋放的內(nèi)存。也就是說,Glibc庫內(nèi)的內(nèi)存分配器ptmalloc既要管理分配的內(nèi)存,也要管理釋放的內(nèi)存。
上述ptmalloc是Glibc提供的內(nèi)存分配器,Google也提供了tcmalloc(全稱: Thread-caching malloc)內(nèi)存分配器,F(xiàn)reeBSD提供了jemalloc內(nèi)存分配器。
4.2 內(nèi)核空間內(nèi)存管理
虛擬內(nèi)存管理負(fù)責(zé)從進(jìn)程的虛擬地址空間分配虛擬頁,內(nèi)核函數(shù)sys_brk用來擴(kuò)大或縮小進(jìn)程的堆,sys_mmap用來在內(nèi)存映射區(qū)域分配虛擬頁,sys_munmap用來釋放虛擬頁。
內(nèi)核使用延時(shí)分配內(nèi)存的策略,進(jìn)程第一次訪問不存在的虛擬頁時(shí),會(huì)觸發(fā)頁錯(cuò)誤異常,此時(shí),頁錯(cuò)誤異常處理程序會(huì)從頁分配器中申請(qǐng)物理頁,在進(jìn)程的頁表中把虛擬頁和物理頁進(jìn)行映射。
伙伴系統(tǒng)(Buddy System)是以頁為單位管理和分配內(nèi)存,提供的內(nèi)存的接口是vmalloc()和vfree()。當(dāng)以字節(jié)為單位分配內(nèi)存時(shí),伙伴系統(tǒng)內(nèi)存利用率極低,而且伙伴系統(tǒng)內(nèi)存分配效率低,分配路徑比較長,遇到內(nèi)存不足時(shí),還會(huì)進(jìn)行內(nèi)存壓縮或回收,同時(shí),由于伙伴系統(tǒng)至少分配一個(gè)頁框,訪問內(nèi)存很容易出現(xiàn)cache miss。
所以,Linux提供了分配小內(nèi)存的SLAB分配器可以解決上述問題,提供的內(nèi)存的接口是kmalloc()和kfree()。
另外,還有SLUB分配器和SLOB分配器,SLOB分配器可以簡單理解為SLAB的縮小版,專門用于內(nèi)存較少的嵌入式設(shè)備,SLUB分配器是SLAB的增強(qiáng)版。
評(píng)論
查看更多